Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang][C23] Crash when trying to use a constexpr variable that could not be initialized #109095

Closed
ianichitei opened this issue Sep 18, 2024 · 5 comments · Fixed by #112708
Closed
Assignees
Labels
c23 clang:frontend Language frontend issues, e.g. anything involving "Sema" confirmed Verified by a second party constexpr Anything related to constant evaluation crash Prefer [crash-on-valid] or [crash-on-invalid]

Comments

@ianichitei
Copy link

I don't think I chose the best title, but I don't know how else to describe it, but the code is pretty straight forward: https://godbolt.org/z/1Tj8ba5bq

constexpr char c[] = { 'a' };
constexpr int i = c[1];
static_assert(i == c[0], "whatever");

When compiled with -std=c23 with clang 19.1.0 this happens:

<source>:2:19: error: constexpr variable 'i' must be initialized by a constant expression
    2 | constexpr int i = c[1];
      |                   ^~~~
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:
0.	Program arguments: /opt/compiler-explorer/clang-19.1.0/bin/clang -gdwarf-4 -g -o /app/output.s -mllvm --x86-asm-syntax=intel -fno-verbose-asm -S --gcc-toolchain=/opt/compiler-explorer/gcc-13.2.0 -fcolor-diagnostics -fno-crash-diagnostics -std=c23 <source>
1.	<eof> parser at end of file
 #0 0x00000000036fdb08 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x36fdb08)
 #1 0x00000000036fbc74 llvm::sys::CleanupOnSignal(unsigned long) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x36fbc74)
 #2 0x0000000003647be8 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #3 0x0000795267642520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007952677aedcd (/lib/x86_64-linux-gnu/libc.so.6+0x1aedcd)
 #5 0x0000000007257afd clang::Expr::getIntegerConstantExpr(clang::ASTContext const&, clang::SourceLocation*) const (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x7257afd)
 #6 0x0000000006181049 AnalyzeImplicitConversions(clang::Sema&, clang::Expr*, clang::SourceLocation, bool) (.constprop.0) SemaChecking.cpp:0:0
 #7 0x00000000061833fa clang::Sema::CheckCompletedExpr(clang::Expr*, clang::SourceLocation, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x61833fa)
 #8 0x00000000066204e4 clang::Sema::ActOnFinishFullExpr(clang::Expr*, clang::SourceLocation, bool, bool, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x66204e4)
 #9 0x0000000006385d7d clang::Sema::BuildStaticAssertDeclaration(clang::SourceLocation, clang::Expr*, clang::Expr*, clang::SourceLocation, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x6385d7d)
#10 0x0000000005f81e36 clang::Parser::ParseStaticAssertDeclaration(clang::SourceLocation&) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f81e36)
#11 0x0000000005f71028 clang::Parser::ParseDeclaration(clang::DeclaratorContext, clang::SourceLocation&, clang::ParsedAttributes&, clang::ParsedAttributes&, clang::SourceLocation*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f71028)
#12 0x0000000005f27cfa clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f27cfa)
#13 0x0000000005f29ac8 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f29ac8)
#14 0x0000000005f1877a clang::ParseAST(clang::Sema&, bool, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f1877a)
#15 0x0000000003fce825 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fce825)
#16 0x0000000004274201 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x4274201)
#17 0x00000000041f699b clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x41f699b)
#18 0x000000000435a843 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x435a843)
#19 0x0000000000c874dc cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xc874dc)
#20 0x0000000000c8146d ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#21 0x0000000004017309 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::'lambda'()>(long) Job.cpp:0:0
#22 0x0000000003648013 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3648013)
#23 0x0000000004017529 clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const (.part.0) Job.cpp:0:0
#24 0x0000000003fdd947 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fdd947)
#25 0x0000000003fde2fd clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fde2fd)
#26 0x0000000003fe7d4c clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fe7d4c)
#27 0x0000000000c847f1 clang_main(int, char**, llvm::ToolContext const&) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xc847f1)
#28 0x0000000000b5c2d4 main (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xb5c2d4)
#29 0x0000795267629d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#30 0x0000795267629e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#31 0x0000000000c80f0e _start (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xc80f0e)
clang: error: clang frontend command failed with exit code 139 (use -v to see invocation)
Compiler returned: 139

Weirdly enough, it looks like adding some other flags avoids the crash, for example with -std=c23 -O3: https://godbolt.org/z/41bGxqdh8 (it still crashes with -O3 -std=c23).

@github-actions github-actions bot added the clang Clang issues not falling into any other category label Sep 18, 2024
@tbaederr tbaederr added c23 constexpr Anything related to constant evaluation labels Sep 18, 2024
@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" crash Prefer [crash-on-valid] or [crash-on-invalid] and removed clang Clang issues not falling into any other category labels Sep 18, 2024
@llvmbot
Copy link

llvmbot commented Sep 18, 2024

@llvm/issue-subscribers-clang-frontend

Author: Anichitei Ionel-Cristinel (ianichitei)

I don't think I chose the best title, but I don't know how else to describe it, but the code is pretty straight forward: https://godbolt.org/z/1Tj8ba5bq
constexpr char c[] = { 'a' };
constexpr int i = c[1];
static_assert(i == c[0], "whatever");

When compiled with -std=c23 with clang 19.1.0 this happens:

&lt;source&gt;:2:19: error: constexpr variable 'i' must be initialized by a constant expression
    2 | constexpr int i = c[1];
      |                   ^~~~
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:
0.	Program arguments: /opt/compiler-explorer/clang-19.1.0/bin/clang -gdwarf-4 -g -o /app/output.s -mllvm --x86-asm-syntax=intel -fno-verbose-asm -S --gcc-toolchain=/opt/compiler-explorer/gcc-13.2.0 -fcolor-diagnostics -fno-crash-diagnostics -std=c23 &lt;source&gt;
1.	&lt;eof&gt; parser at end of file
 #<!-- -->0 0x00000000036fdb08 llvm::sys::PrintStackTrace(llvm::raw_ostream&amp;, int) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x36fdb08)
 #<!-- -->1 0x00000000036fbc74 llvm::sys::CleanupOnSignal(unsigned long) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x36fbc74)
 #<!-- -->2 0x0000000003647be8 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #<!-- -->3 0x0000795267642520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #<!-- -->4 0x00007952677aedcd (/lib/x86_64-linux-gnu/libc.so.6+0x1aedcd)
 #<!-- -->5 0x0000000007257afd clang::Expr::getIntegerConstantExpr(clang::ASTContext const&amp;, clang::SourceLocation*) const (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x7257afd)
 #<!-- -->6 0x0000000006181049 AnalyzeImplicitConversions(clang::Sema&amp;, clang::Expr*, clang::SourceLocation, bool) (.constprop.0) SemaChecking.cpp:0:0
 #<!-- -->7 0x00000000061833fa clang::Sema::CheckCompletedExpr(clang::Expr*, clang::SourceLocation, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x61833fa)
 #<!-- -->8 0x00000000066204e4 clang::Sema::ActOnFinishFullExpr(clang::Expr*, clang::SourceLocation, bool, bool, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x66204e4)
 #<!-- -->9 0x0000000006385d7d clang::Sema::BuildStaticAssertDeclaration(clang::SourceLocation, clang::Expr*, clang::Expr*, clang::SourceLocation, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x6385d7d)
#<!-- -->10 0x0000000005f81e36 clang::Parser::ParseStaticAssertDeclaration(clang::SourceLocation&amp;) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f81e36)
#<!-- -->11 0x0000000005f71028 clang::Parser::ParseDeclaration(clang::DeclaratorContext, clang::SourceLocation&amp;, clang::ParsedAttributes&amp;, clang::ParsedAttributes&amp;, clang::SourceLocation*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f71028)
#<!-- -->12 0x0000000005f27cfa clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&amp;, clang::ParsedAttributes&amp;, clang::ParsingDeclSpec*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f27cfa)
#<!-- -->13 0x0000000005f29ac8 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr&lt;clang::DeclGroupRef&gt;&amp;, clang::Sema::ModuleImportState&amp;) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f29ac8)
#<!-- -->14 0x0000000005f1877a clang::ParseAST(clang::Sema&amp;, bool, bool) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x5f1877a)
#<!-- -->15 0x0000000003fce825 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fce825)
#<!-- -->16 0x0000000004274201 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x4274201)
#<!-- -->17 0x00000000041f699b clang::CompilerInstance::ExecuteAction(clang::FrontendAction&amp;) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x41f699b)
#<!-- -->18 0x000000000435a843 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x435a843)
#<!-- -->19 0x0000000000c874dc cc1_main(llvm::ArrayRef&lt;char const*&gt;, char const*, void*) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xc874dc)
#<!-- -->20 0x0000000000c8146d ExecuteCC1Tool(llvm::SmallVectorImpl&lt;char const*&gt;&amp;, llvm::ToolContext const&amp;) driver.cpp:0:0
#<!-- -->21 0x0000000004017309 void llvm::function_ref&lt;void ()&gt;::callback_fn&lt;clang::driver::CC1Command::Execute(llvm::ArrayRef&lt;std::optional&lt;llvm::StringRef&gt;&gt;, std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt;&gt;*, bool*) const::'lambda'()&gt;(long) Job.cpp:0:0
#<!-- -->22 0x0000000003648013 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref&lt;void ()&gt;) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3648013)
#<!-- -->23 0x0000000004017529 clang::driver::CC1Command::Execute(llvm::ArrayRef&lt;std::optional&lt;llvm::StringRef&gt;&gt;, std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt;&gt;*, bool*) const (.part.0) Job.cpp:0:0
#<!-- -->24 0x0000000003fdd947 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&amp;, clang::driver::Command const*&amp;, bool) const (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fdd947)
#<!-- -->25 0x0000000003fde2fd clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&amp;, llvm::SmallVectorImpl&lt;std::pair&lt;int, clang::driver::Command const*&gt;&gt;&amp;, bool) const (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fde2fd)
#<!-- -->26 0x0000000003fe7d4c clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&amp;, llvm::SmallVectorImpl&lt;std::pair&lt;int, clang::driver::Command const*&gt;&gt;&amp;) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0x3fe7d4c)
#<!-- -->27 0x0000000000c847f1 clang_main(int, char**, llvm::ToolContext const&amp;) (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xc847f1)
#<!-- -->28 0x0000000000b5c2d4 main (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xb5c2d4)
#<!-- -->29 0x0000795267629d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#<!-- -->30 0x0000795267629e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#<!-- -->31 0x0000000000c80f0e _start (/opt/compiler-explorer/clang-19.1.0/bin/clang+0xc80f0e)
clang: error: clang frontend command failed with exit code 139 (use -v to see invocation)
Compiler returned: 139

Weirdly enough, it looks like adding some other flags avoids the crash, for example with -std=c23 -O3: https://godbolt.org/z/41bGxqdh8 (it still crashes with -O3 -std=c23).

@shafik
Copy link
Collaborator

shafik commented Sep 18, 2024

Confirmed: https://godbolt.org/z/5PGzqeevv

It crashes unconditionally w/ assertion build though: https://godbolt.org/z/7Wb5snbEo

@shafik
Copy link
Collaborator

shafik commented Sep 18, 2024

CC @Fznamznon @AaronBallman

@shafik shafik added the confirmed Verified by a second party label Sep 18, 2024
@Fznamznon Fznamznon self-assigned this Sep 18, 2024
@Fznamznon
Copy link
Contributor

The problem here seems to be that due to variable being constexpr, isIntegerConstantExpr and CheckICE routines return true since for constexpr variables isUsableInConstantExpressions returns now true (which is correct, I think). We then end up in the codepath that assumes only valid integer constant expression here

llvm_unreachable("ICE cannot be evaluated!");

It seems logical to not return true from isIntegerConstantExpr for invalid constexpr variables whose initializer is not a constant expression and avoid failing code path, like this:

@@ -2504,7 +2504,7 @@ bool VarDecl::isUsableInConstantExpressions(const ASTContext &Context) const {
   if (!DefVD->mightBeUsableInConstantExpressions(Context))
     return false;
   //   ... and its initializer is a constant initializer.
-  if (Context.getLangOpts().CPlusPlus && !DefVD->hasConstantInitialization())
+  if ((Context.getLangOpts().CPlusPlus || Context.getLangOpts().C23) && !DefVD->hasConstantInitialization())
     return false;
   // C++98 [expr.const]p1:
   //   An integral constant-expression can involve only [...] const variables

However hasConstantInitialization routine is problematic, since for C it without checking assumes that all globals have constant initialization (which is not the case for the variable in the example). However for constexpr variables we have the info in place, just like for C++ variables, so adding somewhat like

 bool VarDecl::hasConstantInitialization() const {
   // In C, all globals (and only globals) have constant initialization.
-  if (hasGlobalStorage() && !getASTContext().getLangOpts().CPlusPlus)
+  if (hasGlobalStorage() && !getASTContext().getLangOpts().CPlusPlus && !isConstexpr())
     return true;

In addition to the previous change in isUsableInConstantExpressions actually fixes the problem and passes testing.
But now it IMO seems like avoiding some global problem. In C++11 each constexpr evaluation is guarded with functions like EvaluateCPlusPlus11IntegralConstantExpr, isCXX11ConstantExpr etc. I don't really like this distinction, especially since we have C23 constexpr now. So, I wonder if it makes sense to follow the same codepath like EvaluateCPlusPlus11IntegralConstantExpr maybe with generalization of it for C++ and C23?
Or the easier way to fix the problem seems enough?

cc @AaronBallman @cor3ntin

@AaronBallman
Copy link
Collaborator

I don't really like this distinction, especially since we have C23 constexpr now. So, I wonder if it makes sense to follow the same codepath like EvaluateCPlusPlus11IntegralConstantExpr maybe with generalization of it for C++ and C23?

I think the larger refactoring makes more sense; ideally, the "high level" interfaces in the AST should be language agnostic because constexpr in C is intended to be compatible with the feature in C++, and we should make distinctions where necessary in the implementation-detail parts of the implementation (not to say we can't have helper functions that are C- or C++-specific where useful). However, if that looks to be a significant undertaking, the simpler approach is reasonable as a stop-gap. My fear with going with the easy approach is that each time we do that, the larger refactoring becomes a bit harder to implement.

Fznamznon added a commit to Fznamznon/llvm-project that referenced this issue Oct 17, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes llvm#109095
Fixes llvm#112516
Fznamznon added a commit that referenced this issue Oct 18, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes #109095
Fixes #112516
Fznamznon added a commit to Fznamznon/llvm-project that referenced this issue Oct 18, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes llvm#109095
Fixes llvm#112516
Fznamznon added a commit to Fznamznon/llvm-project that referenced this issue Oct 18, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes llvm#109095
Fixes llvm#112516
bricknerb pushed a commit to bricknerb/llvm-project that referenced this issue Oct 21, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes llvm#109095
Fixes llvm#112516
EricWF pushed a commit to efcs/llvm-project that referenced this issue Oct 22, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes llvm#109095
Fixes llvm#112516
tru pushed a commit to Fznamznon/llvm-project that referenced this issue Nov 12, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes llvm#109095
Fixes llvm#112516
tru pushed a commit to Fznamznon/llvm-project that referenced this issue Nov 15, 2024
Before using a constexpr variable that is not properly initialized check
that it is valid.

Fixes llvm#109095
Fixes llvm#112516
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c23 clang:frontend Language frontend issues, e.g. anything involving "Sema" confirmed Verified by a second party constexpr Anything related to constant evaluation crash Prefer [crash-on-valid] or [crash-on-invalid]
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants