-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Description
I've identified a few loops over InitListExprs for records that don't account for the changes in C++17 which allowed base initializers to appear in an aggregate initialization expression, namely here and here:
llvm-project/clang/lib/AST/Expr.cpp
Line 3345 in 7a94acb
for (const auto *Field : RD->fields()) { |
https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/CGExprConstant.cpp#L696
The first case represents an actual bug, since it means we don't check the remaining initializer list expressions, as in this example:
struct Empty {};
struct Foo : Empty {
int x;
int y;
};
int f();
Foo o = (Foo){{}, 1, f()};
Clang accepts this, but if you remove the Empty base and its initializer, it rejects it:
struct Foo {
int x;
int y;
};
int f();
Foo o = (Foo){1, f()};
-->
t.cpp:9:19: error: initializer element is not a compile-time constant
9 | Foo o = (Foo){1, f()};
| ^~~
1 error generated.
Something is not right, and the dumped AST doesn't look right, it classifies f()
as a ConstantExpr
:
`-VarDecl 0x55c70d2d2190 <line:7:1, col:25> col:5 o 'Foo' cinit
`-CompoundLiteralExpr 0x55c70d2d25c8 <col:9, col:25> 'Foo'
`-InitListExpr 0x55c70d2d2390 <col:14, col:25> 'Foo'
|-ConstantExpr 0x55c70d2d2580 <col:15, col:16> 'Empty'
| `-InitListExpr 0x55c70d2d23e8 <col:15, col:16> 'Empty'
|-ConstantExpr 0x55c70d2d2598 <col:19> 'int'
| `-IntegerLiteral 0x55c70d2d2248 <col:19> 'int' 1
`-ConstantExpr 0x55c70d2d25b0 <col:22, col:24> 'int'
`-CallExpr 0x55c70d2d2318 <col:22, col:24> 'int'
`-ImplicitCastExpr 0x55c70d2d2300 <col:22> 'int (*)()' <FunctionToPointerDecay>
`-DeclRefExpr 0x55c70d2d22b0 <col:22> 'int ()' lvalue Function 0x55c70d2d2058 'f' 'int ()'
In this larger test case, this misclassification of this initializer as a constant ultimately results in a crash during codegen:
struct Empty {};
struct Foo : Empty {
int x;
int y;
};
int getint();
struct Span {
Span(const Foo (&p)[1]);
};
Span defs = (Foo[1]){{.x = 0, getint()}};
-->
clang: ../clang/lib/CodeGen/CGExprConstant.cpp:932: ConstantAddress (anonymous namespace)::tryEmitGlobalCompoundLiteral(ConstantEmitter &, const CompoundLiteralExpr *): Assertion `!E->isFileScope() && "f
ile-scope compound literal did not have constant initializer!"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
...
#9 0x0000555717168e8e /b/f/w/set_by_reclient/a/../clang/lib/CodeGen/CGExprConstant.cpp:931:5
#10 0x0000555717168a75 clang::CodeGen::CodeGenModule::GetAddrOfConstantCompoundLiteral(clang::CompoundLiteralExpr const*) /b/f/w/set_by_reclient/a/../clang/lib/CodeGen/CGExprConstant.cpp:2243:1
#11 0x0000555717127b31 clang::CodeGen::CodeGenFunction::EmitCompoundLiteralLValue(clang::CompoundLiteralExpr const*) /b/f/w/set_by_reclient/a/../clang/lib/CodeGen/CGExpr.cpp:4945:27
#12 0x0000555717122c19 clang::CodeGen::CodeGenFunction::EmitLValueHelper(clang::Expr const*, clang::CodeGen::KnownNonNull_t) /b/f/w/set_by_reclient/a/../clang/lib/CodeGen/CGExpr.cpp:0:12
...
I believe the solution is to update isConstantInitializer
to iterate over bases and not just fields, but that may uncover more issues.