llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.81k stars 11.45k forks source link

error: initializer element is not a compile-time constant for valid code #48089

Open llvmbot opened 3 years ago

llvmbot commented 3 years ago
Bugzilla Link 48745
Version trunk
OS All
Reporter LLVM Bugzilla Contributor
CC @dwblaikie,@zygoloid

Extended Description

For the following code: struct wss { unsigned int count; unsigned char _have_ct : 1 ; }; struct capab { struct wss _wss; unsigned char _have_type; }; struct node { struct capab _capab; }; struct node nd = { ._capab = { ._have_type = 1, }, ._capab._wss.count = 1, // ERROR! };

Clang gives error: clang -emit-llvm test.c -c error: initializer element is not a compile-time constant 1 error generated.

Code appears to be valid according to struct initialization rules, and gcc gives no error: gcc -c test.c

Also, error message doesn't show error location in the code. Versions: clang --version clang version 12.0.0 (https://github.com/llvm/llvm-project f4f158b2f89e16ee7068d6292d2d46457d6932bb) Target: x86_64-unknown-linux-gnu Thread model: posix gcc --version gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4)
ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 3 years ago

Hmm, this works in C++ but not in C. In C++ we build this structured initializer:

-VarDecl 0x11503010 <line:12:1, line:17:1> line:12:13 nd 'struct node':'node' cinit -InitListExpr 0x11503340 <col:18, line:17:1> 'struct node':'node' -InitListExpr 0x115033e8 <line:13:15, line:15:5> 'struct capab':'capab' |-InitListExpr 0x115035c8 <col:5> 'struct wss':'wss' | |-ImplicitCastExpr 0x11532a60 <line:16:26> 'unsigned int' <IntegralCast> | |-IntegerLiteral 0x115031a8 'int' 1 | -ImplicitValueInitExpr 0x11503628 <<invalid sloc>> 'unsigned char' -ImplicitCastExpr 0x11503438 'unsigned char' `-IntegerLiteral 0x115030c0 'int' 1

But in C we end up with a DesignatedInitUpdateExpr, which CheckForConstantInitializer rejects (even though CodeGen can typically emit IR for it these days):

-VarDecl 0x108e1470 <line:12:1, line:17:1> line:12:13 nd 'struct node':'struct node' cinit -InitListExpr 0x108e17a0 <col:18, line:17:1> 'struct node':'struct node' -InitListExpr 0x108e1848 <line:13:15, line:15:5> 'struct capab':'struct capab' |-DesignatedInitUpdateExpr 0x108e18c0 <<invalid sloc>> 'struct wss':'struct wss' | |-ImplicitValueInitExpr 0x108e18b0 <<invalid sloc>> 'struct wss':'struct wss' |-InitListExpr 0x108e18e0 <line:16:17, col:26> 'struct wss':'struct wss' | |-ImplicitCastExpr 0x108e1928 'unsigned int' | | -IntegerLiteral 0x108e1608 <col:26> 'int' 1 |-NoInitExpr 0x108e1950 <> 'unsigned char' -ImplicitCastExpr 0x108e1898 <line:14:21> 'unsigned char' <IntegralCast> -IntegerLiteral 0x108e1520 'int' 1

Presumably we should convert the ImplicitValueInitExpr for 'struct wss' into an InitListExpr when attempting a designated update, instead of creating a DesignatedInitUpdateExpr. (In modern C++, uninitialized elements are list-initialized instead of being value-initialized, which is why this works there.)

We might also want to consider allowing DesignatedInitUpdateExprs through CheckForConstantInitializer, given that we can now (in reasonable cases) generate IR constants for them.