diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -174,6 +174,9 @@ - Class member variables are now in scope when parsing a ``requires`` clause. Fixes `GH55216 `_. +- Correctly set expression evaluation context as 'immediate function context' in + consteval functions. + This fixes `GH51182 https://github.com/llvm/llvm-project/issues/51182` C++2b Feature Support diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1351,6 +1351,15 @@ bool isImmediateFunctionContext() const { return Context == ExpressionEvaluationContext::ImmediateFunctionContext || (Context == ExpressionEvaluationContext::DiscardedStatement && + InImmediateFunctionContext) || + // C++2b [expr.const]p14: + // An expression or conversion is in an immediate function + // context if it is potentially evaluated and either: + // * its innermost enclosing non-block scope is a function + // parameter scope of an immediate function, or + // * its enclosing statement is enclosed by the compound- + // statement of a consteval if statement. + (Context == ExpressionEvaluationContext::PotentiallyEvaluated && InImmediateFunctionContext); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14800,8 +14800,12 @@ // Do not push if it is a lambda because one is already pushed when building // the lambda in ActOnStartOfLambdaDefinition(). if (!isLambdaCallOperator(FD)) + // [expr.const]/p14.1 + // An expression or conversion is in an immediate function context if it is + // potentially evaluated and either: its innermost enclosing non-block scope + // is a function parameter scope of an immediate function. PushExpressionEvaluationContext( - FD->isConsteval() ? ExpressionEvaluationContext::ConstantEvaluated + FD->isConsteval() ? ExpressionEvaluationContext::ImmediateFunctionContext : ExprEvalContexts.back().Context); // Check for defining attributes before the check for redefinition. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19675,8 +19675,8 @@ if (auto *FD = dyn_cast(E->getDecl())) if (!isUnevaluatedContext() && !isConstantEvaluated() && - FD->isConsteval() && !RebuildingImmediateInvocation && - !FD->isDependentContext()) + !isImmediateFunctionContext() && FD->isConsteval() && + !RebuildingImmediateInvocation && !FD->isDependentContext()) ExprEvalContexts.back().ReferenceToConsteval.insert(E); MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse, RefsMinusAssignments); diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -908,3 +908,34 @@ return testDefaultArgForParam() + testDefaultArgForParam((E)1); } } + +namespace GH51182 { +// Nested consteval function. +consteval int f(int v) { + return v; +} + +template +consteval int g(T a) { + // An immediate function context. + int n = f(a); + return n; +} +static_assert(g(100) == 100); +// -------------------------------------- +template +consteval T max(const T& a, const T& b) { + return (a > b) ? a : b; +} +template +consteval T mid(const T& a, const T& b, const T& c) { + T m = max(max(a, b), c); + if (m == a) + return max(b, c); + if (m == b) + return max(a, c); + return max(a, b); +} +static_assert(max(1,2)==2); +static_assert(mid(1,2,3)==2); +} // namespace GH51182