Skip to content

Commit f86050d

Browse files
authored
[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.
1 parent 39e3050 commit f86050d

File tree

4 files changed

+50
-36
lines changed

4 files changed

+50
-36
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,6 +2529,8 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
25292529
return this->emitGetPtrGlobal(*GlobalIndex, E);
25302530
}
25312531

2532+
if (!this->checkLiteralType(SubExpr))
2533+
return false;
25322534
// Non-primitive values.
25332535
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
25342536
return false;
@@ -2549,6 +2551,10 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
25492551
return false;
25502552
return this->emitGetPtrLocal(LocalIndex, E);
25512553
} else {
2554+
2555+
if (!this->checkLiteralType(SubExpr))
2556+
return false;
2557+
25522558
const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();
25532559
if (std::optional<unsigned> LocalIndex =
25542560
allocateLocal(Inner, E->getExtendingDecl())) {
@@ -3570,9 +3576,6 @@ template <class Emitter>
35703576
bool Compiler<Emitter>::visitInitializer(const Expr *E) {
35713577
assert(!classify(E->getType()));
35723578

3573-
if (!this->checkLiteralType(E))
3574-
return false;
3575-
35763579
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
35773580
/*NewInitializing=*/true);
35783581
return this->Visit(E);

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,37 @@ void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
10121012
}
10131013
}
10141014

1015+
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T) {
1016+
assert(T);
1017+
assert(!S.getLangOpts().CPlusPlus23);
1018+
1019+
// C++1y: A constant initializer for an object o [...] may also invoke
1020+
// constexpr constructors for o and its subobjects even if those objects
1021+
// are of non-literal class types.
1022+
//
1023+
// C++11 missed this detail for aggregates, so classes like this:
1024+
// struct foo_t { union { int i; volatile int j; } u; };
1025+
// are not (obviously) initializable like so:
1026+
// __attribute__((__require_constant_initialization__))
1027+
// static const foo_t x = {{0}};
1028+
// because "i" is a subobject with non-literal initialization (due to the
1029+
// volatile member of the union). See:
1030+
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1677
1031+
// Therefore, we use the C++1y behavior.
1032+
1033+
if (S.Current->getFunction() && S.Current->getFunction()->isConstructor() &&
1034+
S.Current->getThis().getDeclDesc()->asDecl() == S.EvaluatingDecl) {
1035+
return true;
1036+
}
1037+
1038+
const Expr *E = S.Current->getExpr(OpPC);
1039+
if (S.getLangOpts().CPlusPlus11)
1040+
S.FFDiag(E, diag::note_constexpr_nonliteral) << E->getType();
1041+
else
1042+
S.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
1043+
return false;
1044+
}
1045+
10151046
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
10161047
uint32_t VarArgSize) {
10171048
if (Func->hasThisPointer()) {

clang/lib/AST/ByteCode/Interp.h

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ bool CallBI(InterpState &S, CodePtr OpPC, const Function *Func,
158158
const CallExpr *CE, uint32_t BuiltinID);
159159
bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
160160
const CallExpr *CE);
161+
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
161162

162163
enum class ShiftDir { Left, Right };
163164

@@ -2946,39 +2947,6 @@ static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {
29462947
return true;
29472948
}
29482949

2949-
inline bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T) {
2950-
assert(T);
2951-
assert(!S.getLangOpts().CPlusPlus23);
2952-
2953-
// C++1y: A constant initializer for an object o [...] may also invoke
2954-
// constexpr constructors for o and its subobjects even if those objects
2955-
// are of non-literal class types.
2956-
//
2957-
// C++11 missed this detail for aggregates, so classes like this:
2958-
// struct foo_t { union { int i; volatile int j; } u; };
2959-
// are not (obviously) initializable like so:
2960-
// __attribute__((__require_constant_initialization__))
2961-
// static const foo_t x = {{0}};
2962-
// because "i" is a subobject with non-literal initialization (due to the
2963-
// volatile member of the union). See:
2964-
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1677
2965-
// Therefore, we use the C++1y behavior.
2966-
2967-
if (S.EvaluatingDecl)
2968-
return true;
2969-
2970-
if (S.Current->getFunction() && S.Current->getFunction()->isConstructor() &&
2971-
S.Current->getThis().getDeclDesc()->asDecl() == S.EvaluatingDecl)
2972-
return true;
2973-
2974-
const Expr *E = S.Current->getExpr(OpPC);
2975-
if (S.getLangOpts().CPlusPlus11)
2976-
S.FFDiag(E, diag::note_constexpr_nonliteral) << E->getType();
2977-
else
2978-
S.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
2979-
return false;
2980-
}
2981-
29822950
//===----------------------------------------------------------------------===//
29832951
// Read opcode arguments
29842952
//===----------------------------------------------------------------------===//

clang/test/AST/ByteCode/cxx17.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ constexpr int b() {
8181
}
8282
static_assert(b() == 11);
8383

84+
namespace cwg1872 {
85+
template<typename T> struct A : T {
86+
constexpr int f() const { return 0; }
87+
};
88+
struct X {};
89+
struct Y { virtual int f() const; };
90+
struct Z : virtual X {};
91+
92+
constexpr int z = A<Z>().f(); // both-error {{must be initialized by a constant expression}} \
93+
// both-note {{non-literal type 'A<Z>' cannot be used in a constant expression}}
94+
}
95+
8496
/// The diagnostics between the two interpreters used to be different here.
8597
struct S { int a; };
8698
constexpr S getS() { // both-error {{constexpr function never produces a constant expression}}

0 commit comments

Comments
 (0)