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,13 @@ bool isImmediateFunctionContext() const { return Context == ExpressionEvaluationContext::ImmediateFunctionContext || (Context == ExpressionEvaluationContext::DiscardedStatement && + InImmediateFunctionContext) || + // [expr.const#def:immediate_function_context] + // 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. + (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 @@ -14771,8 +14771,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#def:immediate_function_context] + // 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 @@ -19676,8 +19676,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 \ No newline at end of file