Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions src/interpret.c
Original file line number Diff line number Diff line change
Expand Up @@ -22554,10 +22554,13 @@ if *p_this_arg is not initialized.)
constructor has not completely run yet, or because the object has
been partially destroyed). */
} else {
a_routine_ptr callee = *p_callee;
/* The final overrider is a member of the class of the subobject from which
it is found, and that subobject is the object for the call. */
a_byte *callee_subobj = subobj;
do {
a_base_class_ptr bcp = *(a_base_class_ptr*)subobj;
a_byte_count offset;
a_byte_count offset, retval_offset_step = 0;
a_routine_ptr overrider = NULL;
an_overriding_virtual_function_ptr
ovfp;
if (bcp == NULL) {
Expand All @@ -22568,29 +22571,32 @@ if *p_this_arg is not initialized.)
check_assertion(!bcp->is_pack_expansion);
ovfp = bcp->variant.overriding_virtual_functions;
for (; ovfp != NULL; ovfp = ovfp->next) {
if (ovfp->primary_function == callee) {
if (ovfp->primary_function == *p_callee) {
a_base_class_ptr ret_base = ovfp->return_adjustment_base_class;
a_byte_count retval_offset_step;
if (ret_base != NULL) {
/* A covariant return override. The return value will need
adjustment. */
get_mapped_byte_count(&persistent_map, bcp, retval_offset_step);
*p_retval_offset += retval_offset_step;
adjustment (possibly to an indirect base class). */
retval_offset_step = compute_interpreter_base_offset(
ret_base, (a_base_class_ptr*)NULL);
Comment thread
daveedvdv-nvidia marked this conversation as resolved.
} /* if */
callee = ovfp->overriding_function;
overrider = ovfp->overriding_function;
break;
} /* if */
} /* for */
get_mapped_byte_count(&persistent_map, bcp, offset);
subobj -= offset;
if (overrider != NULL) {
*p_callee = overrider;
*p_retval_offset += retval_offset_step;
callee_subobj = subobj;
} /* if */
if (!subobject_is_initialized(subobj, complete_obj)) {
/* The next-enclosing subobject is not constructed. Do not dispatch
from it. */
break;
} /* if */
} while (subobj != complete_obj);
*p_callee = callee;
this_val->address = subobj;
this_val->address = callee_subobj;
} /* if */
return result;
} /* adjust_virtual_callee */
Expand Down Expand Up @@ -22793,10 +22799,12 @@ was charged up front to the actual cost of the call.
a_byte_count *arg_size = (a_byte_count*)cw->arg_sizes;

if (result) {
if (cw->retval_offset != 0) {
if (cw->retval_offset != 0 &&
((a_constexpr_address*)item->result_storage)->address != NULL) {
/* A virtual call dispatching to an overriding function with a covariant
return type. The return value is an address that must be updated to
match the static type of the expression. */
return type. The return value is an address (other than a null
pointer) that must be updated to match the static type of the
expression. */
((a_constexpr_address*)item->result_storage)->address +=
cw->retval_offset;
} /* if */
Expand Down
21 changes: 21 additions & 0 deletions src/pending_changes/gh-397-virtual-call-overrider-subobject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[GH #397]
Constant evaluation of virtual calls to an overrider in a base class

During constant evaluation, a virtual function call whose final overrider is
a member of an intermediate base class (rather than of the most derived
class) was evaluated with the complete object as the object expression,
instead of the base class subobject of which the overrider is a member. For
example, with --c++20:

struct A { constexpr virtual int f() const { return 1; } int a = 1; };
struct B : A { constexpr int f() const override { return a + 2; } };
struct E { int e = 0; };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not required for the example, I'd remove E as it doesn't seem relevant to the bug being described (my reading is the issue is A -> B -> F traverses to F and tries to use F to resolve the virtual function overrider instead of B resulting in A being what's found since F has no overrider).

struct F : E, B {};
constexpr int g() { F f; A& r = f; return r.f(); }
static_assert(g() == 3); // Previously failed.

This is now fixed. In addition, the adjustment of the result of a call to
an overrider with a covariant return type (e.g., from a pointer to a derived
class to a pointer to its base class) was computed incorrectly, and was
applied even to a null pointer result (which could cause the front end to
crash); this is also fixed.
1 change: 0 additions & 1 deletion tests/expectations/edg_x86_64/imported.log
Original file line number Diff line number Diff line change
Expand Up @@ -7070,7 +7070,6 @@ gnu/cpp/cpp2a/consteval-memfn2.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval-prop17.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval-prop3.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval-prop6.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval-virtual5.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval12.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval13.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval25.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
Expand Down
13 changes: 13 additions & 0 deletions tests/tests/changes/.gh-397-null.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
fe_only -DTEST_NUMBER=1 --g++ --c++20 Test_name.c
"Test_name.c", line 21: error: expression must have a constant value
constexpr int v = g(); // Error: null pointer argument (previously crashed).
^
"Test_name.c", line 19: note: attempt to dereference a null pointer
return __builtin_memcmp(rp.p(), (const X*)&s.z, sizeof(X));
^

"Test_name.c", line 21: warning: variable "v" was declared but never referenced
constexpr int v = g(); // Error: null pointer argument (previously crashed).
^

1 error detected in the compilation of "Test_name.c".
1 change: 1 addition & 0 deletions tests/tests/changes/.gh-397.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
21 changes: 21 additions & 0 deletions tests/tests/changes/gh-397-null.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//type:fn
//options:--g++ --c++20
//remark:[GH #397] Null pointer result of a covariant virtual call

struct E { int e = 0; };
struct X { int x = 7; };
struct Y { int y = 0; };
struct Z : Y, X {};
struct P { constexpr virtual const X* p() const { return nullptr; } };
struct S : E, P {
Z z;
constexpr const Z* p() const override { return nullptr; }
};
constexpr bool f() { S s; const P& rp = s; return rp.p() == nullptr; }
static_assert(f());
constexpr int g() {
S s;
const P& rp = s;
return __builtin_memcmp(rp.p(), (const X*)&s.z, sizeof(X));
}
constexpr int v = g(); // Error: null pointer argument (previously crashed).
68 changes: 68 additions & 0 deletions tests/tests/changes/gh-397.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//type:fp
//options:--c++20
//remark:[GH #397] Constant evaluation of a virtual call whose final overrider
//remark:is a member of an intermediate base class

struct A { constexpr virtual int f() const { return 1; } int a = 1; };
struct B : A { constexpr int f() const override { return a + 2; } };
struct E { int e = 0; };
struct F : E, B {};
constexpr int g() { F f; A& r = f; return r.f(); }
static_assert(g() == 3); // Previously failed.

struct G : B {};
constexpr int h() { G g; A& r = g; return r.f(); }
static_assert(h() == 3); // Previously failed.

struct H : E, G { int x = 5; };
constexpr int i() { H h; A& ra = h; B& rb = h; return ra.f() * 10 + rb.f(); }
static_assert(i() == 33); // Previously failed.

struct C : A {};
constexpr int j() { C c; A& r = c; return r.f(); }
static_assert(j() == 1);

struct X { int x = 7; };
struct Y { int y = 0; };
struct Z : Y, X {};
struct P { constexpr virtual const X* p() const { return nullptr; } };
struct Q : P { Z z; constexpr const Z* p() const override { return &z; } };
struct R : E, Q {};
constexpr int k() { R r; const P& rp = r; return rp.p()->x; }
static_assert(k() == 7);
struct S : E, P {
Z z;
bool null;
constexpr S(bool b) : null(b) {}
constexpr const Z* p() const override { return null ? nullptr : &z; }
};
constexpr bool l(bool b) {
S s(b);
const P& rp = s;
return b ? rp.p() == nullptr : rp.p() == (const X*)&s.z;
}
static_assert(l(false)); // Previously failed.
static_assert(l(true));
struct W : Y, Z {};
struct T : Q { W w; constexpr const W* p() const override { return &w; } };
struct U : E, T {};
constexpr bool m() {
U u;
const P& rp = u;
const Q& rq = u;
return rp.p() == (const X*)&u.w && rq.p() == (const Z*)&u.w &&
rp.p()->x == 7;
}
static_assert(m()); // Previously failed.

// A covariant return type whose class is an indirect base class.
struct V : Y, Z {};
struct VV : E, V {};
struct I : P { VV vv; constexpr const VV* p() const override { return &vv; } };
struct J : E, I {};
constexpr bool n() {
J j;
const P& rp = j;
return rp.p() == (const X*)&j.vv && rp.p()->x == 7;
}
static_assert(n());
Original file line number Diff line number Diff line change
Expand Up @@ -41,94 +41,20 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
^
detected during instantiation of class "Virtual::Covariant3<T> [with T=Virtual::C]" at line 203

"SemaCXX/constant-expression-cxx2a.cpp", line 207: warning: integer conversion resulted in a change of sign
static_assert(cb1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 207: error: static assertion failed
static_assert(cb1->f()->a == 'A');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 207: note: the final comparison was 0 == 65
static_assert(cb1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 208: error: static assertion failed
static_assert(cb1->f() == (B*)&cb.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 209: error: static assertion failed
static_assert(cb1->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 209: note: the final comparison was 65 == 68
static_assert(cb1->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 210: warning: integer conversion resulted in a change of sign
static_assert(cb2->f()->b == 'B');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 210: error: static assertion failed
static_assert(cb2->f()->b == 'B');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 210: note: the final comparison was VALUE == 66
static_assert(cb2->f()->b == 'B');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 211: error: static assertion failed
static_assert(cb2->f() == &cb.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 212: error: static assertion failed
static_assert(cb2->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 212: note: the final comparison was 66 == 68
static_assert(cb2->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 216: warning: integer conversion resulted in a change of sign
static_assert(cc1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 216: error: static assertion failed
static_assert(cc1->f()->a == 'A');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 216: note: the final comparison was 0 == 65
static_assert(cc1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 217: error: static assertion failed
static_assert(cc1->f() == (C*)&cc.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 218: error: static assertion failed
static_assert(cc1->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 218: note: the final comparison was 65 == 68
static_assert(cc1->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 219: warning: integer conversion resulted in a change of sign
static_assert(cc2->f()->c == 'C');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 219: error: static assertion failed
static_assert(cc2->f()->c == 'C');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 219: note: the final comparison was 2131689472 == 67
static_assert(cc2->f()->c == 'C');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 220: error: static assertion failed
static_assert(cc2->f() == &cc.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 221: error: static assertion failed
static_assert(cc2->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 221: note: the final comparison was 67 == 68
static_assert(cc2->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 232: error: initialization is not constant
constexpr PureVirtualCall pure_virtual_call;
^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,94 +41,20 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
^
detected during instantiation of class "Virtual::Covariant3<T> [with T=Virtual::C]" at line 203

"SemaCXX/constant-expression-cxx2a.cpp", line 207: warning: integer conversion resulted in a change of sign
static_assert(cb1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 207: error: static assertion failed
static_assert(cb1->f()->a == 'A');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 207: note: the final comparison was 0 == 65
static_assert(cb1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 208: error: static assertion failed
static_assert(cb1->f() == (B*)&cb.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 209: error: static assertion failed
static_assert(cb1->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 209: note: the final comparison was 65 == 68
static_assert(cb1->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 210: warning: integer conversion resulted in a change of sign
static_assert(cb2->f()->b == 'B');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 210: error: static assertion failed
static_assert(cb2->f()->b == 'B');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 210: note: the final comparison was VALUE == 66
static_assert(cb2->f()->b == 'B');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 211: error: static assertion failed
static_assert(cb2->f() == &cb.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 212: error: static assertion failed
static_assert(cb2->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 212: note: the final comparison was 66 == 68
static_assert(cb2->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 216: warning: integer conversion resulted in a change of sign
static_assert(cc1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 216: error: static assertion failed
static_assert(cc1->f()->a == 'A');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 216: note: the final comparison was 0 == 65
static_assert(cc1->f()->a == 'A');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 217: error: static assertion failed
static_assert(cc1->f() == (C*)&cc.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 218: error: static assertion failed
static_assert(cc1->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 218: note: the final comparison was 65 == 68
static_assert(cc1->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 219: warning: integer conversion resulted in a change of sign
static_assert(cc2->f()->c == 'C');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 219: error: static assertion failed
static_assert(cc2->f()->c == 'C');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 219: note: the final comparison was 0 == 67
static_assert(cc2->f()->c == 'C');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 220: error: static assertion failed
static_assert(cc2->f() == &cc.d);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 221: error: static assertion failed
static_assert(cc2->f()->f() == 'D');
^
"SemaCXX/constant-expression-cxx2a.cpp", line 221: note: the final comparison was 67 == 68
static_assert(cc2->f()->f() == 'D');
^

"SemaCXX/constant-expression-cxx2a.cpp", line 232: error: initialization is not constant
constexpr PureVirtualCall pure_virtual_call;
^
Expand Down
Loading
Loading