llvm / llvm/llvm-project

[clang][bytecode] Missing -Winvalid-constexpr diagnostic for always-ill-formed constexpr function under bytecode interpreter

Open
#181,857 0 comments 0 reactions 0 assignees View on GitHub
clang:bytecode
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

### Bug Description And Reproducer

When compiling a simple constexpr function with **"clang++ -std=C++20 rep.cpp -o rep.exe"**

```cpp
constexpr void f(bool b){
return b ? throw 0 : throw 1;
}

int main() {
}
```
the following error is emitted:

```
fact.cpp:12:16: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
12 | constexpr void f(bool b){
| ^
fact.cpp:13:11: note: both arms of conditional operator are unable to produce a constant expression
13 | return b ? throw 0 : throw 1;
| ^~~~~~~~~~~~~~~~~~~~~
1 error generated.
```

but compiling the same code with the new bytecode interpreter (**"clang++ -std=C++20 -fexperimental-new-constant-interpreter rep.cpp -o rep.exe"**) no error or any other diagnostic gets emitted and code compiles fine.

### Environment
Clang: 21.1.8 (standalone binaries not Visual Studio bundled Clang)
Target: x86_64-pc-windows-msvc

### Findings
Before starting, here is the bytecode
```asm
0 InitScope 0
16 GetParamBool 0
32 Jf 24 --+
48 Invalid |
56 Jmp 8 | --+
72 Invalid <-+ |
80 Destroy 0 <-+
96 RetVoid
104 RetVoid
```
Apparently the bytecode interpreter and when verifying if program is well formed per [[dcl.constexpr] p6](https://timsong-cpp.github.io/cppwp/n4868/dcl.constexpr#6) the interpreter bailout early when executing any param load instruction like 'GetParamBool'

```cpp
template ::T>
bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
if (S.checkingPotentialConstantExpression()) {
return false;
}
S.Stk.push(S.Current->getParam(I));
return true;
}
```
since it's part of checking if function can yield constant expression
```cpp
bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
assert(Stk.empty());

// Get a function handle.
const Function *Func = getOrCreateFunction(FD);
if (!Func)
return false;

// Compile the function.
Compiler(*this, *P).compileFunc(
FD, const_cast(Func));

if (!Func->isValid())
return false;

++EvalID;
// And run it.
return Run(Parent, Func);
}
```
which originates from checking if function body is constexpr
```cpp
static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
Stmt *Body,
Sema::CheckConstexprKind Kind) {

.
.
.
.
bool SkipCheck =
!SemaRef.getLangOpts().CheckConstexprFunctionBodies ||
SemaRef.getSourceManager().isInSystemHeader(Dcl->getLocation()) ||
SemaRef.getDiagnostics().isIgnored(
diag::ext_constexpr_function_never_constant_expr, Dcl->getLocation());
SmallVector Diags;
if (Kind == Sema::CheckConstexprKind::Diagnose && !SkipCheck &&
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
SemaRef.Diag(Dcl->getLocation(),
diag::ext_constexpr_function_never_constant_expr)
<< isa(Dcl) << Dcl->isConsteval()
<< Dcl->getNameInfo().getSourceRange();
for (const auto &Diag : Diags)
SemaRef.Diag(Diag.first, Diag.second);
// Don't return false here: we allow this for compatibility in
// system headers.
}

return true;
}
```
The tree evaluator check conditional expression arms when doing it's own version of `isPotentialConstantExpr` and emit the error as expected.

I hope this was helpful.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the difference with the two clang++ commands in the issue, then read Context::isPotentialConstantExpr, GetParam, and CheckConstexprFunctionBody. Compare the bytecode interpreter's handling of the conditional expression with the tree evaluator. Done means the bytecode interpreter emits the -Winvalid-constexpr diagnostic and explanatory note for the provided constexpr function.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.