Skip to content

Commit 5296d01

Browse files
authored
[clang][bytecode] Assert on virtual func call from array elem (#158502)
Fixes #152893. An assert was raised when a constexpr virtual function was called from an constexpr array element with -fexperimental-new-constant-interpreter set.
1 parent fee71a3 commit 5296d01

File tree

3 files changed

+117
-13
lines changed

3 files changed

+117
-13
lines changed

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,6 +1638,36 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
16381638
return true;
16391639
}
16401640

1641+
static bool GetDynamicDecl(InterpState &S, CodePtr OpPC, Pointer TypePtr,
1642+
const CXXRecordDecl *&DynamicDecl) {
1643+
while (TypePtr.isBaseClass())
1644+
TypePtr = TypePtr.getBase();
1645+
1646+
QualType DynamicType = TypePtr.getType();
1647+
if (TypePtr.isStatic() || TypePtr.isConst()) {
1648+
const VarDecl *VD = TypePtr.getDeclDesc()->asVarDecl();
1649+
if (!VD->isConstexpr()) {
1650+
const Expr *E = S.Current->getExpr(OpPC);
1651+
APValue V = TypePtr.toAPValue(S.getASTContext());
1652+
QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
1653+
S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
1654+
<< AccessKinds::AK_MemberCall << V.getAsString(S.getASTContext(), TT);
1655+
return false;
1656+
}
1657+
}
1658+
1659+
if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
1660+
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
1661+
} else if (DynamicType->isArrayType()) {
1662+
const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
1663+
assert(ElemType);
1664+
DynamicDecl = ElemType->getAsCXXRecordDecl();
1665+
} else {
1666+
DynamicDecl = DynamicType->getAsCXXRecordDecl();
1667+
}
1668+
return true;
1669+
}
1670+
16411671
bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
16421672
uint32_t VarArgSize) {
16431673
assert(Func->hasThisPointer());
@@ -1662,17 +1692,8 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
16621692
}
16631693

16641694
const CXXRecordDecl *DynamicDecl = nullptr;
1665-
{
1666-
Pointer TypePtr = ThisPtr;
1667-
while (TypePtr.isBaseClass())
1668-
TypePtr = TypePtr.getBase();
1669-
1670-
QualType DynamicType = TypePtr.getType();
1671-
if (DynamicType->isPointerType() || DynamicType->isReferenceType())
1672-
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
1673-
else
1674-
DynamicDecl = DynamicType->getAsCXXRecordDecl();
1675-
}
1695+
if (!GetDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
1696+
return false;
16761697
assert(DynamicDecl);
16771698

16781699
const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());

clang/test/AST/ByteCode/cxx20.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1070,9 +1070,30 @@ namespace Virtual {
10701070
public:
10711071
int a = f();
10721072

1073-
virtual constexpr int f() { return 10; }
1073+
virtual constexpr int f() const { return 10; }
10741074
};
10751075

1076+
K k;
1077+
static_assert(k.f() == 10); // both-error {{not an integral constant expression}} \
1078+
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
1079+
1080+
void f() {
1081+
constexpr K k;
1082+
static_assert(k.f() == 10);
1083+
}
1084+
1085+
void f2() {
1086+
K k;
1087+
static_assert(k.f() == 10); // both-error {{not an integral constant expression}} \
1088+
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
1089+
}
1090+
1091+
static_assert(K().f() == 10);
1092+
1093+
void f3() {
1094+
static_assert(K().f() == 10);
1095+
}
1096+
10761097
class L : public K {
10771098
public:
10781099
int b = f();
@@ -1083,6 +1104,42 @@ namespace Virtual {
10831104
static_assert(l.a == 10);
10841105
static_assert(l.b == 10);
10851106
static_assert(l.c == 10);
1107+
static_assert(l.f() == 10);
1108+
1109+
struct M {
1110+
K& mk = k;
1111+
};
1112+
static_assert(M{}.mk.f() == 10); // both-error {{not an integral constant expression}} \
1113+
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
1114+
1115+
struct N {
1116+
K* mk = &k;
1117+
};
1118+
static_assert(N{}.mk->f() == 10); // both-error {{not an integral constant expression}} \
1119+
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
1120+
1121+
extern K o;
1122+
static_assert(o.f() == 10); // both-error {{not an integral constant expression}} \
1123+
// both-note {{virtual function called on object 'o' whose dynamic type is not constant}}
1124+
static K p;
1125+
static_assert(p.f() == 10); // both-error {{not an integral constant expression}} \
1126+
// both-note {{virtual function called on object 'p' whose dynamic type is not constant}}
1127+
1128+
void f4() {
1129+
static K p;
1130+
static_assert(p.f() == 10); // both-error {{not an integral constant expression}} \
1131+
// both-note {{virtual function called on object 'p' whose dynamic type is not constant}}
1132+
}
1133+
1134+
const K q;
1135+
static_assert(q.f() == 10); // both-error {{not an integral constant expression}} \
1136+
// both-note {{virtual function called on object 'q' whose dynamic type is not constant}}
1137+
1138+
void f5() {
1139+
const K q;
1140+
static_assert(q.f() == 10); // both-error {{not an integral constant expression}} \
1141+
// both-note {{virtual function called on object 'q' whose dynamic type is not constant}}
1142+
}
10861143
}
10871144

10881145
namespace DiscardedTrivialCXXConstructExpr {
@@ -1100,3 +1157,29 @@ namespace DiscardedTrivialCXXConstructExpr {
11001157
constexpr int y = foo(12); // both-error {{must be initialized by a constant expression}} \
11011158
// both-note {{in call to}}
11021159
}
1160+
1161+
namespace VirtualFunctionCallThroughArrayElem {
1162+
struct X {
1163+
constexpr virtual int foo() const {
1164+
return 3;
1165+
}
1166+
};
1167+
constexpr X xs[5];
1168+
static_assert(xs[3].foo() == 3);
1169+
1170+
constexpr X xs2[1][2];
1171+
static_assert(xs2[0].foo() == 3); // both-error {{is not a structure or union}}
1172+
static_assert(xs2[0][0].foo() == 3);
1173+
1174+
struct Y: public X {
1175+
constexpr int foo() const override {
1176+
return 1;
1177+
}
1178+
};
1179+
constexpr Y ys[20];
1180+
static_assert(ys[12].foo() == static_cast<const X&>(ys[12]).foo());
1181+
1182+
X a[3][4];
1183+
static_assert(a[2][3].foo()); // both-error {{not an integral constant expression}} \
1184+
// both-note {{virtual function called on object 'a[2][3]' whose dynamic type is not constant}}
1185+
}

clang/test/SemaCXX/constant-expression-p2280r4.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void splash(Swim& swam) { // nointerpreter-note {{declared here}
4444
static_assert(how_many(swam) == 28); // ok
4545
static_assert(Swim().lochte() == 12); // ok
4646
static_assert(swam.lochte() == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
47-
// nointerpreter-note {{virtual function called on object 'swam' whose dynamic type is not constant}}
47+
// expected-note {{virtual function called on object 'swam' whose dynamic type is not constant}}
4848
static_assert(swam.coughlin == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
4949
// nointerpreter-note {{read of variable 'swam' whose value is not known}}
5050
}

0 commit comments

Comments
 (0)