diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 39bacbebb5ba7..f07e430e279d2 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -2244,7 +2244,7 @@ template bool ByteCodeExprGen::discard(const Expr *E) { template bool ByteCodeExprGen::delegate(const Expr *E) { if (E->containsErrors()) - return false; + return this->emitError(E); // We're basically doing: // OptionScope Scope(this, DicardResult, Initializing); @@ -2254,7 +2254,7 @@ bool ByteCodeExprGen::delegate(const Expr *E) { template bool ByteCodeExprGen::visit(const Expr *E) { if (E->containsErrors()) - return false; + return this->emitError(E); if (E->getType()->isVoidType()) return this->discard(E); @@ -2283,7 +2283,7 @@ bool ByteCodeExprGen::visitInitializer(const Expr *E) { assert(!classify(E->getType())); if (E->containsErrors()) - return false; + return this->emitError(E); OptionScope Scope(this, /*NewDiscardResult=*/false, /*NewInitializing=*/true); diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 08e272cbc005b..405993eb82703 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -2227,6 +2227,9 @@ inline bool Invalid(InterpState &S, CodePtr OpPC) { return false; } +/// Do nothing and just abort execution. +inline bool Error(InterpState &S, CodePtr OpPC) { return false; } + /// Same here, but only for casts. inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind) { const SourceLocation &Loc = S.Current->getLocation(OpPC); diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 0ed214af3548a..cc1310f4c0d52 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -706,6 +706,7 @@ def Dup : Opcode { // [] -> [] def Invalid : Opcode {} +def Error : Opcode {} def InvalidCast : Opcode { let Args = [ArgCastKind]; } diff --git a/clang/test/AST/Interp/if.cpp b/clang/test/AST/Interp/if.cpp index 86ae8de6f73eb..37289d69d3255 100644 --- a/clang/test/AST/Interp/if.cpp +++ b/clang/test/AST/Interp/if.cpp @@ -1,8 +1,5 @@ -// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fexperimental-new-constant-interpreter %s -verify -// RUN: %clang_cc1 -std=c++23 -fsyntax-only %s -verify=ref - -// expected-no-diagnostics -// ref-no-diagnostics +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fexperimental-new-constant-interpreter %s -verify=expected,both +// RUN: %clang_cc1 -std=c++23 -fsyntax-only %s -verify=ref,both namespace ConstEval { constexpr int f() { @@ -51,3 +48,13 @@ namespace InitDecl { } static_assert(attrs() == 1, ""); }; + +/// The faulty if statement creates a RecoveryExpr with contains-errors, +/// but the execution will never reach that. +constexpr char g(char const (&x)[2]) { + return 'x'; + if (auto [a, b] = x) // both-error {{an array type is not allowed here}} \ + // both-warning {{ISO C++17 does not permit structured binding declaration in a condition}} + ; +} +static_assert(g("x") == 'x');