| Issue |
181857
|
| Summary |
[clang][bytecode] Missing -Winvalid-constexpr diagnostic for always-ill-formed constexpr function under bytecode interpreter
|
| Labels |
clang
|
| Assignees |
|
| Reporter |
yazandaba
|
### 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 <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
if (S.checkingPotentialConstantExpression()) {
return false;
}
S.Stk.push<T>(S.Current->getParam<T>(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<ByteCodeEmitter>(*this, *P).compileFunc(
FD, const_cast<Function *>(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<PartialDiagnosticAt, 8> Diags;
if (Kind == Sema::CheckConstexprKind::Diagnose && !SkipCheck &&
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
SemaRef.Diag(Dcl->getLocation(),
diag::ext_constexpr_function_never_constant_expr)
<< isa<CXXConstructorDecl>(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.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs