Skip to content

Commit 5984bca

Browse files
Constant evaluation: find overriders below non-overriding classes [GH #421]
1 parent 424eae8 commit 5984bca

10 files changed

Lines changed: 174 additions & 158 deletions

File tree

‎src/interpret.c‎

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22832,49 +22832,79 @@ if *p_this_arg is not initialized.)
2283222832
constructor has not completely run yet, or because the object has
2283322833
been partially destroyed). */
2283422834
} else {
22835-
/* The final overrider is a member of the class of the subobject from which
22836-
it is found, and that subobject is the object for the call. */
22837-
a_byte *callee_subobj = subobj;
22835+
/* Find the outermost enclosing subobject from which the call is
22836+
dispatched (outer_subobj, of class outer_class). */
22837+
a_byte *outer_subobj = subobj;
22838+
a_type_ptr outer_class = NULL;
22839+
a_type_ptr callee_class = skip_typerefs(parent_class_of(*p_callee));
22840+
a_base_class_ptr bcp;
2283822841
do {
22839-
a_base_class_ptr bcp = *(a_base_class_ptr*)subobj;
22840-
a_byte_count offset, retval_offset_step = 0;
22841-
a_routine_ptr overrider = NULL;
22842-
an_overriding_virtual_function_ptr
22843-
ovfp;
22842+
a_byte_count offset;
22843+
bcp = *(a_base_class_ptr*)outer_subobj;
2284422844
if (bcp == NULL) {
2284522845
/* A most derived subobject (i.e., a field or array element). We are
2284622846
done. */
2284722847
break;
2284822848
} /* if */
22849-
check_assertion(!bcp->is_pack_expansion);
22850-
ovfp = bcp->variant.overriding_virtual_functions;
22851-
for (; ovfp != NULL; ovfp = ovfp->next) {
22852-
if (ovfp->primary_function == *p_callee) {
22849+
get_mapped_byte_count(&persistent_map, bcp, offset);
22850+
outer_subobj -= offset;
22851+
outer_class = bcp->derived_class;
22852+
if (!subobject_is_initialized(outer_subobj, complete_obj)) {
22853+
/* The next-enclosing subobject is not constructed. Do not dispatch
22854+
from anything enclosing it. */
22855+
break;
22856+
} /* if */
22857+
} while (outer_subobj != complete_obj);
22858+
if (outer_class != NULL) {
22859+
/* The override information for the subobject of the callee's class
22860+
in outer_class gives the final overrider in outer_class (which may
22861+
be a member of an intermediate class). The base class entry for
22862+
that subobject is identified by its offset, since the callee's
22863+
class can be a base class of outer_class more than once. */
22864+
an_overriding_virtual_function_ptr
22865+
ovfp;
22866+
for (bcp = base_classes_of(outer_class); bcp != NULL; bcp = bcp->next) {
22867+
if (!bcp->is_pack_expansion &&
22868+
same_entities(skip_typerefs(bcp->type), callee_class) &&
22869+
outer_subobj + compute_interpreter_base_offset(
22870+
bcp, (a_base_class_ptr*)NULL) ==
22871+
subobj) {
22872+
break;
22873+
} /* if */
22874+
} /* for */
22875+
check_assertion(bcp != NULL);
22876+
do {
22877+
/* If the overrider found is a member of an intermediate class, it
22878+
is normally the final overrider, but a function that overrides
22879+
it without being recorded as overriding the original function
22880+
(e.g., because of an invalid covariant return type) is found
22881+
through the base class entry for that intermediate class. */
22882+
ovfp = bcp->variant.overriding_virtual_functions;
22883+
while (ovfp != NULL && ovfp->primary_function != *p_callee) {
22884+
ovfp = ovfp->next;
22885+
} /* while */
22886+
if (ovfp != NULL) {
2285322887
a_base_class_ptr ret_base = ovfp->return_adjustment_base_class;
22888+
*p_callee = ovfp->overriding_function;
22889+
bcp = ovfp->base_class;
22890+
if (bcp == NULL) {
22891+
/* The overrider is a member of outer_class. */
22892+
this_val->address = outer_subobj;
22893+
} else {
22894+
this_val->address =
22895+
outer_subobj +
22896+
compute_interpreter_base_offset(bcp,
22897+
(a_base_class_ptr*)NULL);
22898+
} /* if */
2285422899
if (ret_base != NULL) {
2285522900
/* A covariant return override. The return value will need
2285622901
adjustment (possibly to an indirect base class). */
22857-
retval_offset_step = compute_interpreter_base_offset(
22902+
*p_retval_offset += compute_interpreter_base_offset(
2285822903
ret_base, (a_base_class_ptr*)NULL);
2285922904
} /* if */
22860-
overrider = ovfp->overriding_function;
22861-
break;
2286222905
} /* if */
22863-
} /* for */
22864-
get_mapped_byte_count(&persistent_map, bcp, offset);
22865-
subobj -= offset;
22866-
if (overrider != NULL) {
22867-
*p_callee = overrider;
22868-
*p_retval_offset += retval_offset_step;
22869-
callee_subobj = subobj;
22870-
} /* if */
22871-
if (!subobject_is_initialized(subobj, complete_obj)) {
22872-
/* The next-enclosing subobject is not constructed. Do not dispatch
22873-
from it. */
22874-
break;
22875-
} /* if */
22876-
} while (subobj != complete_obj);
22877-
this_val->address = callee_subobj;
22906+
} while (ovfp != NULL && bcp != NULL);
22907+
} /* if */
2287822908
} /* if */
2287922909
return result;
2288022910
} /* adjust_virtual_callee */
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[GH #421]
2+
Constant evaluation of virtual calls to an overrider in a derived class
3+
4+
During constant evaluation, a virtual function call did not find the final
5+
overrider if the class of which it is a member is derived from the class of
6+
the called function through an intermediate class that does not itself
7+
override the function: The called function was not replaced. For example,
8+
with --c++20:
9+
10+
struct A { constexpr virtual int f() const { return 1; } };
11+
struct NA : A {};
12+
struct B : NA { constexpr int f() const override { return 2; } };
13+
constexpr int g() { B b; const A* p = &b; return p->f(); }
14+
static_assert(g() == 2); // Previously failed.
15+
16+
This is now fixed.

‎tests/expectations/edg_x86_64/imported.log‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7140,7 +7140,6 @@ gnu/cpp/cpp2a/paren-init30.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL:
71407140
gnu/cpp/cpp2a/paren-init33.sft.cpp:rp:-DTEST_NUMBER=1 --c++20:FAIL(COMPILE):
71417141
gnu/cpp/cpp2a/paren-init35.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
71427142
gnu/cpp/cpp2a/paren-init4.sft.cpp:rp:-DTEST_NUMBER=1 --c++20:FAIL(COMPILE):
7143-
gnu/cpp/cpp2a/pr117317-1.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
71447143
gnu/cpp/cpp2a/pr99833.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:ABORT:
71457144
gnu/cpp/cpp2a/spaceship-constexpr2/test.mft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
71467145
gnu/cpp/cpp2a/spaceship-narrowing1.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c

‎tests/tests/changes/gh-421.sft.cpp‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//type:fp
2+
//options:--c++20
3+
//remark:Constant evaluation: overrider below a class that does not override
4+
5+
struct A {
6+
constexpr virtual int f() const { return 1; }
7+
int a = 0;
8+
};
9+
struct NA : A {};
10+
struct B : NA {
11+
constexpr int f() const override { return 2; }
12+
};
13+
constexpr int g1() { B b; const A* p = &b; return p->f(); }
14+
static_assert(g1() == 2); // Previously failed.
15+
constexpr int g1n() { B b; const NA* p = &b; return p->f(); }
16+
static_assert(g1n() == 2); // Previously failed.
17+
18+
// Two non-overriding levels.
19+
struct NNA : NA {};
20+
struct C : NNA { constexpr int f() const override { return 3; } };
21+
constexpr int g2() { C c; const A* p = &c; return p->f(); }
22+
static_assert(g2() == 3); // Previously failed.
23+
24+
// The overrider is in a middle class, above a non-overriding one.
25+
struct M : NA { constexpr int f() const override { return 4 + a; } };
26+
struct NM : M {};
27+
struct E { int e = 7; };
28+
struct T : E, NM {};
29+
constexpr int g3() { T t; t.a = 10; const A* p = &t; return p->f(); }
30+
static_assert(g3() == 14); // Previously failed.
31+
32+
// Repeated base class: only one path overrides.
33+
struct L : A { constexpr int f() const override { return 5 + a; } };
34+
struct R : A {};
35+
struct D : L, R {};
36+
constexpr int g4() {
37+
D d;
38+
static_cast<L&>(d).a = 1;
39+
static_cast<R&>(d).a = 2;
40+
const A* pl = static_cast<const L*>(&d);
41+
const A* pr = static_cast<const R*>(&d);
42+
return pl->f() * 10 + pr->f();
43+
}
44+
static_assert(g4() == 61);
45+
struct DD : D {
46+
constexpr int f() const override { return 9; }
47+
};
48+
constexpr int g5() {
49+
DD d;
50+
const A* pl = static_cast<const L*>(&d);
51+
const A* pr = static_cast<const R*>(&d);
52+
return pl->f() * 10 + pr->f();
53+
}
54+
static_assert(g5() == 99); // Previously failed.
55+
56+
// Covariant return through a non-overriding intermediate class.
57+
struct X { int x = 'X'; };
58+
struct Y : X { int y = 'Y'; };
59+
struct Z0 { int z = 0; };
60+
struct Q : Z0, Y { int q = 'Q'; };
61+
struct CA { constexpr virtual const X* h() const { return &xx; } X xx; };
62+
struct CN : CA {};
63+
struct CB : CN {
64+
constexpr const Q* h() const override { return &qq; }
65+
Q qq;
66+
};
67+
constexpr bool g6() {
68+
CB b;
69+
const CA* p = &b;
70+
return p->h() == static_cast<const X*>(&b.qq) && p->h()->x == 'X';
71+
}
72+
static_assert(g6()); // Previously failed.
73+
74+
// Virtual call during construction of an intermediate class.
75+
struct K : NA {
76+
int r;
77+
constexpr K() : r(static_cast<const A*>(this)->f()) {}
78+
constexpr int f() const override { return 6; }
79+
};
80+
struct NK : K {};
81+
struct KK : NK { constexpr int f() const override { return 8; } };
82+
constexpr int g7() { KK k; const A* p = &k; return k.r * 10 + p->f(); }
83+
static_assert(g7() == 68); // Previously failed.
84+
struct W : NA {
85+
int r;
86+
constexpr W() : r(static_cast<const A*>(this)->f()) {}
87+
};
88+
struct WW : W { constexpr int f() const override { return 8; } };
89+
constexpr int g8() { WW w; const A* p = &w; return w.r * 10 + p->f(); }
90+
static_assert(g8() == 18); // Previously failed.
91+
92+
constexpr B gb;
93+
static_assert(static_cast<const A&>(gb).f() == 2);

‎tests/tests/imported/clang/cpp/SemaCXX/.constant-expression-cxx2a.rto/default.1.1.txt‎

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,10 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
1313
"SemaCXX/constant-expression-cxx2a.cpp", line 176: error: static assertion failed
1414
static_assert(d.ba == 'B');
1515
^
16-
"SemaCXX/constant-expression-cxx2a.cpp", line 176: note: the final comparison was 65 == 66
16+
"SemaCXX/constant-expression-cxx2a.cpp", line 176: note: the final comparison was 68 == 66
1717
static_assert(d.ba == 'B');
1818
^
1919

20-
"SemaCXX/constant-expression-cxx2a.cpp", line 181: error: static assertion failed
21-
static_assert(a.f() == 'D');
22-
^
23-
"SemaCXX/constant-expression-cxx2a.cpp", line 181: note: the final comparison was 65 == 68
24-
static_assert(a.f() == 'D');
25-
^
26-
27-
"SemaCXX/constant-expression-cxx2a.cpp", line 182: error: static assertion failed
28-
static_assert(b.f() == 'D');
29-
^
30-
"SemaCXX/constant-expression-cxx2a.cpp", line 182: note: the final comparison was 66 == 68
31-
static_assert(b.f() == 'D');
32-
^
33-
3420
"SemaCXX/constant-expression-cxx2a.cpp", line 199: error: return type is not identical to nor covariant with return type "const Virtual::A *" of overridden virtual function "Virtual::Covariant1::f"
3521
constexpr virtual const D *f() const { return &this->d; }
3622
^
@@ -41,20 +27,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
4127
^
4228
detected during instantiation of class "Virtual::Covariant3<T> [with T=Virtual::C]" at line 203
4329

44-
"SemaCXX/constant-expression-cxx2a.cpp", line 209: error: static assertion failed
45-
static_assert(cb1->f()->f() == 'D');
46-
^
47-
"SemaCXX/constant-expression-cxx2a.cpp", line 209: note: the final comparison was 65 == 68
48-
static_assert(cb1->f()->f() == 'D');
49-
^
50-
51-
"SemaCXX/constant-expression-cxx2a.cpp", line 212: error: static assertion failed
52-
static_assert(cb2->f()->f() == 'D');
53-
^
54-
"SemaCXX/constant-expression-cxx2a.cpp", line 212: note: the final comparison was 66 == 68
55-
static_assert(cb2->f()->f() == 'D');
56-
^
57-
5830
"SemaCXX/constant-expression-cxx2a.cpp", line 232: error: initialization is not constant
5931
constexpr PureVirtualCall pure_virtual_call;
6032
^

‎tests/tests/imported/clang/cpp/SemaCXX/.constant-expression-cxx2a.rto/edg_x86_32.1.1.txt‎

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,10 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
1313
"SemaCXX/constant-expression-cxx2a.cpp", line 176: error: static assertion failed
1414
static_assert(d.ba == 'B');
1515
^
16-
"SemaCXX/constant-expression-cxx2a.cpp", line 176: note: the final comparison was 65 == 66
16+
"SemaCXX/constant-expression-cxx2a.cpp", line 176: note: the final comparison was 68 == 66
1717
static_assert(d.ba == 'B');
1818
^
1919

20-
"SemaCXX/constant-expression-cxx2a.cpp", line 181: error: static assertion failed
21-
static_assert(a.f() == 'D');
22-
^
23-
"SemaCXX/constant-expression-cxx2a.cpp", line 181: note: the final comparison was 65 == 68
24-
static_assert(a.f() == 'D');
25-
^
26-
27-
"SemaCXX/constant-expression-cxx2a.cpp", line 182: error: static assertion failed
28-
static_assert(b.f() == 'D');
29-
^
30-
"SemaCXX/constant-expression-cxx2a.cpp", line 182: note: the final comparison was 66 == 68
31-
static_assert(b.f() == 'D');
32-
^
33-
3420
"SemaCXX/constant-expression-cxx2a.cpp", line 199: error: return type is not identical to nor covariant with return type "const Virtual::A *" of overridden virtual function "Virtual::Covariant1::f"
3521
constexpr virtual const D *f() const { return &this->d; }
3622
^
@@ -41,20 +27,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
4127
^
4228
detected during instantiation of class "Virtual::Covariant3<T> [with T=Virtual::C]" at line 203
4329

44-
"SemaCXX/constant-expression-cxx2a.cpp", line 209: error: static assertion failed
45-
static_assert(cb1->f()->f() == 'D');
46-
^
47-
"SemaCXX/constant-expression-cxx2a.cpp", line 209: note: the final comparison was 65 == 68
48-
static_assert(cb1->f()->f() == 'D');
49-
^
50-
51-
"SemaCXX/constant-expression-cxx2a.cpp", line 212: error: static assertion failed
52-
static_assert(cb2->f()->f() == 'D');
53-
^
54-
"SemaCXX/constant-expression-cxx2a.cpp", line 212: note: the final comparison was 66 == 68
55-
static_assert(cb2->f()->f() == 'D');
56-
^
57-
5830
"SemaCXX/constant-expression-cxx2a.cpp", line 232: error: initialization is not constant
5931
constexpr PureVirtualCall pure_virtual_call;
6032
^

‎tests/tests/imported/clang/cpp/SemaCXX/.constant-expression-cxx2a.rto/edg_x86_32_cp.1.1.txt‎

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,10 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
1313
"SemaCXX/constant-expression-cxx2a.cpp", line 176: error: static assertion failed
1414
static_assert(d.ba == 'B');
1515
^
16-
"SemaCXX/constant-expression-cxx2a.cpp", line 176: note: the final comparison was 65 == 66
16+
"SemaCXX/constant-expression-cxx2a.cpp", line 176: note: the final comparison was 68 == 66
1717
static_assert(d.ba == 'B');
1818
^
1919

20-
"SemaCXX/constant-expression-cxx2a.cpp", line 181: error: static assertion failed
21-
static_assert(a.f() == 'D');
22-
^
23-
"SemaCXX/constant-expression-cxx2a.cpp", line 181: note: the final comparison was 65 == 68
24-
static_assert(a.f() == 'D');
25-
^
26-
27-
"SemaCXX/constant-expression-cxx2a.cpp", line 182: error: static assertion failed
28-
static_assert(b.f() == 'D');
29-
^
30-
"SemaCXX/constant-expression-cxx2a.cpp", line 182: note: the final comparison was 66 == 68
31-
static_assert(b.f() == 'D');
32-
^
33-
3420
"SemaCXX/constant-expression-cxx2a.cpp", line 186: error: expression must have a constant value
3521
static_assert(d_not_constexpr.f() == 'D');
3622
^
@@ -48,20 +34,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
4834
^
4935
detected during instantiation of class "Virtual::Covariant3<T> [with T=Virtual::C]" at line 203
5036

51-
"SemaCXX/constant-expression-cxx2a.cpp", line 209: error: static assertion failed
52-
static_assert(cb1->f()->f() == 'D');
53-
^
54-
"SemaCXX/constant-expression-cxx2a.cpp", line 209: note: the final comparison was 65 == 68
55-
static_assert(cb1->f()->f() == 'D');
56-
^
57-
58-
"SemaCXX/constant-expression-cxx2a.cpp", line 212: error: static assertion failed
59-
static_assert(cb2->f()->f() == 'D');
60-
^
61-
"SemaCXX/constant-expression-cxx2a.cpp", line 212: note: the final comparison was 66 == 68
62-
static_assert(cb2->f()->f() == 'D');
63-
^
64-
6537
"SemaCXX/constant-expression-cxx2a.cpp", line 232: error: initialization is not constant
6638
constexpr PureVirtualCall pure_virtual_call;
6739
^

0 commit comments

Comments
 (0)