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
43 changes: 32 additions & 11 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,36 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
return true;
}

static bool GetDynamicDecl(InterpState &S, CodePtr OpPC, Pointer TypePtr,
const CXXRecordDecl *&DynamicDecl) {
while (TypePtr.isBaseClass())
TypePtr = TypePtr.getBase();

QualType DynamicType = TypePtr.getType();
if (TypePtr.isStatic() || TypePtr.isConst()) {
const VarDecl *VD = TypePtr.getDeclDesc()->asVarDecl();
if (!VD->isConstexpr()) {
const Expr *E = S.Current->getExpr(OpPC);
APValue V = TypePtr.toAPValue(S.getASTContext());
QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
<< AccessKinds::AK_MemberCall << V.getAsString(S.getASTContext(), TT);
return false;
}
}

if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
} else if (DynamicType->isArrayType()) {
const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
assert(ElemType);
DynamicDecl = ElemType->getAsCXXRecordDecl();
} else {
DynamicDecl = DynamicType->getAsCXXRecordDecl();
}
return true;
}

bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
assert(Func->hasThisPointer());
Expand All @@ -1660,17 +1690,8 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
}

const CXXRecordDecl *DynamicDecl = nullptr;
{
Pointer TypePtr = ThisPtr;
while (TypePtr.isBaseClass())
TypePtr = TypePtr.getBase();

QualType DynamicType = TypePtr.getType();
if (DynamicType->isPointerType() || DynamicType->isReferenceType())
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
else
DynamicDecl = DynamicType->getAsCXXRecordDecl();
}
if (!GetDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
return false;
assert(DynamicDecl);

const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());
Expand Down
85 changes: 84 additions & 1 deletion clang/test/AST/ByteCode/cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1070,9 +1070,30 @@ namespace Virtual {
public:
int a = f();

virtual constexpr int f() { return 10; }
virtual constexpr int f() const { return 10; }
};

K k;
static_assert(k.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}

void f() {
constexpr K k;
static_assert(k.f() == 10);
}

void f2() {
K k;
static_assert(k.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
}

static_assert(K().f() == 10);

void f3() {
static_assert(K().f() == 10);
}

class L : public K {
public:
int b = f();
Expand All @@ -1083,6 +1104,42 @@ namespace Virtual {
static_assert(l.a == 10);
static_assert(l.b == 10);
static_assert(l.c == 10);
static_assert(l.f() == 10);

struct M {
K& mk = k;
};
static_assert(M{}.mk.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}

struct N {
K* mk = &k;
};
static_assert(N{}.mk->f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'k' whose dynamic type is not constant}}

extern K o;
static_assert(o.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'o' whose dynamic type is not constant}}
static K p;
static_assert(p.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'p' whose dynamic type is not constant}}

void f4() {
static K p;
static_assert(p.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'p' whose dynamic type is not constant}}
}

const K q;
static_assert(q.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'q' whose dynamic type is not constant}}

void f5() {
const K q;
static_assert(q.f() == 10); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'q' whose dynamic type is not constant}}
}
}

namespace DiscardedTrivialCXXConstructExpr {
Expand All @@ -1100,3 +1157,29 @@ namespace DiscardedTrivialCXXConstructExpr {
constexpr int y = foo(12); // both-error {{must be initialized by a constant expression}} \
// both-note {{in call to}}
}

namespace VirtualFunctionCallThroughArrayElem {
struct X {
constexpr virtual int foo() const {
return 3;
}
};
constexpr X xs[5];
static_assert(xs[3].foo() == 3);

constexpr X xs2[1][2];
static_assert(xs2[0].foo() == 3); // both-error {{is not a structure or union}}
static_assert(xs2[0][0].foo() == 3);

struct Y: public X {
constexpr int foo() const override {
return 1;
}
};
constexpr Y ys[20];
Copy link
Contributor

Choose a reason for hiding this comment

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

can you include the original reproducer from #152893 as well? This patch fixes the assertion failure but the diagnostic output is still different between the two interpreters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added that test case and the diagnostic. For normal globals the interpreters also disagreed in their diagnostics outputs (https://godbolt.org/z/84zeeYxd3), now they raise the same messages.

static_assert(ys[12].foo() == static_cast<const X&>(ys[12]).foo());

X a[3][4];
static_assert(a[2][3].foo()); // both-error {{not an integral constant expression}} \
// both-note {{virtual function called on object 'a[2][3]' whose dynamic type is not constant}}
}
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/constant-expression-p2280r4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void splash(Swim& swam) { // nointerpreter-note {{declared here}
static_assert(how_many(swam) == 28); // ok
static_assert(Swim().lochte() == 12); // ok
static_assert(swam.lochte() == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
// nointerpreter-note {{virtual function called on object 'swam' whose dynamic type is not constant}}
// expected-note {{virtual function called on object 'swam' whose dynamic type is not constant}}
static_assert(swam.coughlin == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
// nointerpreter-note {{read of variable 'swam' whose value is not known}}
}
Expand Down