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

Reply via email to