[clang][bytecode] Don't call checkLiteralType() in visitInitializer() (#109530)

We were calling checkLiteralType() too many time and rejecting some
things we shouldn't. Add The calls manually when handling
MaterializeTemporaryExprs. Maybe we should call it in other places as
well, but adding more calls is easier than removing them from a generic
code path.
This commit is contained in:
Timm Baeder 2024-09-21 20:01:21 +02:00 committed by GitHub
parent 39e30508a7
commit f86050de73
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 36 deletions

View File

@ -2529,6 +2529,8 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
return this->emitGetPtrGlobal(*GlobalIndex, E);
}
if (!this->checkLiteralType(SubExpr))
return false;
// Non-primitive values.
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
return false;
@ -2549,6 +2551,10 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
return false;
return this->emitGetPtrLocal(LocalIndex, E);
} else {
if (!this->checkLiteralType(SubExpr))
return false;
const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();
if (std::optional<unsigned> LocalIndex =
allocateLocal(Inner, E->getExtendingDecl())) {
@ -3570,9 +3576,6 @@ template <class Emitter>
bool Compiler<Emitter>::visitInitializer(const Expr *E) {
assert(!classify(E->getType()));
if (!this->checkLiteralType(E))
return false;
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
/*NewInitializing=*/true);
return this->Visit(E);

View File

@ -1012,6 +1012,37 @@ void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
}
}
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T) {
assert(T);
assert(!S.getLangOpts().CPlusPlus23);
// C++1y: A constant initializer for an object o [...] may also invoke
// constexpr constructors for o and its subobjects even if those objects
// are of non-literal class types.
//
// C++11 missed this detail for aggregates, so classes like this:
// struct foo_t { union { int i; volatile int j; } u; };
// are not (obviously) initializable like so:
// __attribute__((__require_constant_initialization__))
// static const foo_t x = {{0}};
// because "i" is a subobject with non-literal initialization (due to the
// volatile member of the union). See:
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1677
// Therefore, we use the C++1y behavior.
if (S.Current->getFunction() && S.Current->getFunction()->isConstructor() &&
S.Current->getThis().getDeclDesc()->asDecl() == S.EvaluatingDecl) {
return true;
}
const Expr *E = S.Current->getExpr(OpPC);
if (S.getLangOpts().CPlusPlus11)
S.FFDiag(E, diag::note_constexpr_nonliteral) << E->getType();
else
S.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
return false;
}
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
if (Func->hasThisPointer()) {

View File

@ -158,6 +158,7 @@ bool CallBI(InterpState &S, CodePtr OpPC, const Function *Func,
const CallExpr *CE, uint32_t BuiltinID);
bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
const CallExpr *CE);
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
enum class ShiftDir { Left, Right };
@ -2946,39 +2947,6 @@ static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {
return true;
}
inline bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T) {
assert(T);
assert(!S.getLangOpts().CPlusPlus23);
// C++1y: A constant initializer for an object o [...] may also invoke
// constexpr constructors for o and its subobjects even if those objects
// are of non-literal class types.
//
// C++11 missed this detail for aggregates, so classes like this:
// struct foo_t { union { int i; volatile int j; } u; };
// are not (obviously) initializable like so:
// __attribute__((__require_constant_initialization__))
// static const foo_t x = {{0}};
// because "i" is a subobject with non-literal initialization (due to the
// volatile member of the union). See:
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1677
// Therefore, we use the C++1y behavior.
if (S.EvaluatingDecl)
return true;
if (S.Current->getFunction() && S.Current->getFunction()->isConstructor() &&
S.Current->getThis().getDeclDesc()->asDecl() == S.EvaluatingDecl)
return true;
const Expr *E = S.Current->getExpr(OpPC);
if (S.getLangOpts().CPlusPlus11)
S.FFDiag(E, diag::note_constexpr_nonliteral) << E->getType();
else
S.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
return false;
}
//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//

View File

@ -81,6 +81,18 @@ constexpr int b() {
}
static_assert(b() == 11);
namespace cwg1872 {
template<typename T> struct A : T {
constexpr int f() const { return 0; }
};
struct X {};
struct Y { virtual int f() const; };
struct Z : virtual X {};
constexpr int z = A<Z>().f(); // both-error {{must be initialized by a constant expression}} \
// both-note {{non-literal type 'A<Z>' cannot be used in a constant expression}}
}
/// The diagnostics between the two interpreters used to be different here.
struct S { int a; };
constexpr S getS() { // both-error {{constexpr function never produces a constant expression}}