cor3ntin updated this revision to Diff 541983.
cor3ntin added a comment.
Address Mariya & Aaron's feedback
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D155175/new/
https://reviews.llvm.org/D155175
Files:
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/CodeGen/CGExpr.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp
clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
Index: clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
===================================================================
--- clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
+++ clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
@@ -34,7 +34,7 @@
template <typename T>
constexpr T h(T t = id(x)) { // expected-note {{read of non-const variable 'x' is not allowed in a constant expression}} \
- // expected-note 2{{'hh<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
+ // expected-note {{'hh<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
return t;
}
@@ -164,3 +164,169 @@
int i = g(x); // expected-error {{call to immediate function 'ConstevalConstructor::g<int>' is not a constant expression}} \
// expected-note {{read of non-const variable 'x' is not allowed in a constant expression}}
}
+
+
+
+namespace Aggregate {
+consteval int f(int); // expected-note {{declared here}}
+struct S {
+ int x = f(42); // expected-note {{undefined function 'f' cannot be used in a constant expression}} \
+ // expected-note {{'immediate<int>' is an immediate function because its body contains a call to a consteval function 'f' and that call is not a constant expression}}
+};
+
+constexpr S immediate(auto) {
+ return S{};
+}
+
+void test_runtime() {
+ (void)immediate(0); // expected-error {{call to immediate function 'Aggregate::immediate<int>' is not a constant expression}} \
+ // expected-note {{in call to 'immediate(0)'}}
+}
+consteval int f(int i) {
+ return i;
+}
+consteval void test() {
+ constexpr S s = immediate(0);
+ static_assert(s.x == 42);
+}
+}
+
+
+
+namespace GH63742 {
+void side_effect(); // expected-note {{declared here}}
+consteval int f(int x) {
+ if (!x) side_effect(); // expected-note {{non-constexpr function 'side_effect' cannot be used in a constant expression}}
+ return x;
+}
+struct SS {
+ int y = f(1); // Ok
+ int x = f(0); // expected-error {{call to consteval function 'GH63742::f' is not a constant expression}} \
+ // expected-note {{declared here}} \
+ // expected-note {{in call to 'f(0)'}}
+ SS();
+};
+SS::SS(){} // expected-note {{in the default initializer of 'x'}}
+
+consteval int f2(int x) {
+ if (!__builtin_is_constant_evaluated()) side_effect();
+ return x;
+}
+struct S2 {
+ int x = f2(0);
+ constexpr S2();
+};
+
+constexpr S2::S2(){}
+S2 s = {};
+constinit S2 s2 = {};
+
+struct S3 {
+ int x = f2(0);
+ S3();
+};
+S3::S3(){}
+
+}
+
+namespace Defaulted {
+consteval int f(int x);
+struct SS {
+ int x = f(0);
+ SS() = default;
+};
+}
+
+namespace DefaultedUse{
+consteval int f(int x); // expected-note {{declared here}}
+struct SS {
+ int a = sizeof(f(0)); // Ok
+ int x = f(0); // expected-note {{undefined function 'f' cannot be used in a constant expression}} \
+ // expected-note {{'SS' is an immediate constructor because the default initializer of 'x' contains a call to a consteval function 'f' and that call is not a constant expression}}
+ SS() = default;
+};
+
+void test() {
+ [[maybe_unused]] SS s; // expected-error {{call to immediate function 'DefaultedUse::SS::SS' is not a constant expression}} \
+ // expected-note {{in call to 'SS()'}}
+}
+}
+
+namespace UserDefinedConstructors {
+consteval int f(int x) {
+ return x;
+}
+extern int NonConst; // expected-note 2{{declared here}}
+
+struct ConstevalCtr {
+ int y;
+ int x = f(y);
+ consteval ConstevalCtr(int yy)
+ : y(f(yy)) {}
+};
+
+ConstevalCtr c1(1);
+ConstevalCtr c2(NonConst);
+// expected-error@-1 {{call to consteval function 'UserDefinedConstructors::ConstevalCtr::ConstevalCtr' is not a constant expression}} \
+// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}}
+
+struct ImmediateEscalating {
+ int y;
+ int x = f(y);
+ template<typename T>
+ constexpr ImmediateEscalating(T yy)
+ : y(f(yy)) {} // expected-note {{ImmediateEscalating<int>' is an immediate constructor because the initializer of 'y' contains a call to a consteval function 'f' and that call is not a constant expression}}
+};
+
+ImmediateEscalating c3(1);
+ImmediateEscalating c4(NonConst);
+// expected-error@-1 {{call to immediate function 'UserDefinedConstructors::ImmediateEscalating::ImmediateEscalating<int>' is not a constant expression}} \
+// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}}
+
+
+struct NonEscalating {
+ int y;
+ int x = f(this->y); // expected-error {{call to consteval function 'UserDefinedConstructors::f' is not a constant expression}} \
+ // expected-note {{declared here}} \
+ // expected-note {{use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}}
+ constexpr NonEscalating(int yy) : y(yy) {} // expected-note {{in the default initializer of 'x'}}
+};
+NonEscalating s = {1};
+
+}
+
+namespace AggregateInit {
+
+consteval int f(int x) {
+ return x;
+}
+
+struct S {
+ int i;
+ int j = f(i);
+};
+
+constexpr S test(auto) {
+ return {};
+}
+
+S s = test(0);
+
+}
+
+namespace GlobalAggregateInit {
+
+consteval int f(int x) {
+ return x;
+}
+
+struct S {
+ int i;
+ int j = f(i); // expected-error {{call to consteval function 'GlobalAggregateInit::f' is not a constant expression}} \
+ // expected-note {{implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}} \
+ // expected-note {{declared here}}
+};
+
+S s(0); // expected-note {{in the default initializer of 'j'}}
+
+}
Index: clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
+++ clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
@@ -41,19 +41,22 @@
}();
};
-consteval int ub(int n) { // expected-note {{declared here}}
+consteval int ub(int n) {
return 0/n;
}
struct InitWithLambda {
- int b = [](int error = undefined()) { // expected-error {{cannot take address of consteval function 'undefined' outside of an immediate invocation}}
+ int b = [](int error = undefined()) { // expected-note {{undefined function 'undefined' cannot be used in a constant expression}} \
+ // expected-note {{'InitWithLambda' is an immediate constructor because the default initializer of 'b' contains a call to a consteval function 'undefined' and that call is not a constant expression}}
return error;
}();
- int c = [](int error = sizeof(undefined()) + ub(0)) { // expected-error {{cannot take address of consteval function 'ub' outside of an immediate invocation}}
+ int c = [](int error = sizeof(undefined()) + ub(0)) {
return error;
}();
} i;
+// expected-error@-1 {{call to immediate function 'InitWithLambda::InitWithLambda' is not a constant expression}} \
+ expected-note@-1 {{in call to 'InitWithLambda()'}}
namespace ShouldNotCrash {
template<typename T>
Index: clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -emit-llvm %s -std=c++20 -triple x86_64-unknown-linux-gnu -o - | FileCheck %s
+
+namespace GH63742 {
+
+void side_effect();
+consteval int f(int x) {
+ if (!__builtin_is_constant_evaluated()) side_effect();
+ return x;
+}
+struct SS {
+ int x = f(42);
+ SS();
+};
+SS::SS(){}
+
+}
+
+// CHECK-LABEL: @_ZN7GH637422SSC2Ev
+// CHECK-NOT: call
+// CHECK: store i32 42, ptr {{.*}}
+// CHECK: ret void
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -6084,17 +6084,11 @@
ImmediateCallVisitor(const ASTContext &Ctx) : Context(Ctx) {}
bool HasImmediateCalls = false;
- bool IsImmediateInvocation = false;
-
bool shouldVisitImplicitCode() const { return true; }
bool VisitCallExpr(CallExpr *E) {
- if (const FunctionDecl *FD = E->getDirectCallee()) {
+ if (const FunctionDecl *FD = E->getDirectCallee())
HasImmediateCalls |= FD->isImmediateFunction();
- if (FD->isConsteval() && !E->isCXX11ConstantExpr(Context))
- IsImmediateInvocation = true;
- }
-
return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
}
@@ -6224,6 +6218,8 @@
if (Field->isInvalidDecl())
return ExprError();
+ CXXThisScopeRAII This(*this, Field->getParent(), Qualifiers());
+
auto *ParentRD = cast<CXXRecordDecl>(Field->getParent());
std::optional<ExpressionEvaluationContextRecord::InitializationContext>
@@ -6271,13 +6267,6 @@
if (!NestedDefaultChecking)
V.TraverseDecl(Field);
if (V.HasImmediateCalls) {
- // C++23 [expr.const]/p15
- // An aggregate initialization is an immediate invocation
- // if it evaluates a default member initializer that has a subexpression
- // that is an immediate-escalating expression.
- ExprEvalContexts.back().InImmediateFunctionContext |=
- V.IsImmediateInvocation;
-
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
CurContext};
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
@@ -18439,6 +18428,11 @@
if (!CE.getInt())
EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE);
for (auto *DR : Rec.ReferenceToConsteval) {
+ // If the expression is immediate escalating, it is not an error;
+ // The outer context itself becomes immediate and further errors,
+ // if any, will be handled by DiagnoseImmediateEscalatingReason.
+ if (DR->isImmediateEscalating())
+ continue;
const auto *FD = cast<FunctionDecl>(DR->getDecl());
const NamedDecl *ND = FD;
if (const auto *MD = dyn_cast<CXXMethodDecl>(ND);
@@ -18464,8 +18458,15 @@
SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address)
<< ND << isa<CXXRecordDecl>(ND) << FD->isConsteval();
SemaRef.Diag(ND->getLocation(), diag::note_declared_at);
+ if (auto Context =
+ SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) {
+ SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer)
+ << Context->Decl;
+ SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at);
+ }
if (FD->isImmediateEscalating() && !FD->isConsteval())
SemaRef.DiagnoseImmediateEscalatingReason(FD);
+
} else {
SemaRef.MarkExpressionAsImmediateEscalating(DR);
}
@@ -18914,6 +18915,12 @@
// or of another default member initializer (ie a PotentiallyEvaluatedIfUsed
// context), its initializers may not be referenced yet.
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Func)) {
+ EnterExpressionEvaluationContext EvalContext(
+ *this,
+ Constructor->isImmediateFunction()
+ ? ExpressionEvaluationContext::ImmediateFunctionContext
+ : ExpressionEvaluationContext::PotentiallyEvaluated,
+ Constructor);
for (CXXCtorInitializer *Init : Constructor->inits()) {
if (Init->isInClassMemberInitializer())
runWithSufficientStackSpace(Init->getSourceLocation(), [&]() {
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -46,6 +46,7 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/SaveAndRestore.h"
#include <map>
#include <optional>
#include <set>
@@ -2438,13 +2439,12 @@
}
bool Sema::CheckImmediateEscalatingFunctionDefinition(
- FunctionDecl *FD, bool HasImmediateEscalatingExpression) {
- if (!FD->hasBody() || !getLangOpts().CPlusPlus20 ||
- !FD->isImmediateEscalating())
+ FunctionDecl *FD, const sema::FunctionScopeInfo *FSI) {
+ if (!getLangOpts().CPlusPlus20 || !FD->isImmediateEscalating())
return true;
FD->setBodyContainsImmediateEscalatingExpressions(
- HasImmediateEscalatingExpression);
- if (HasImmediateEscalatingExpression) {
+ FSI->FoundImmediateEscalatingExpression);
+ if (FSI->FoundImmediateEscalatingExpression) {
auto it = UndefinedButUsed.find(FD->getCanonicalDecl());
if (it != UndefinedButUsed.end()) {
Diag(it->second, diag::err_immediate_function_used_before_definition)
@@ -2464,59 +2464,73 @@
assert(FD->hasBody() && "expected the function to have a body");
struct ImmediateEscalatingExpressionsVisitor
: public RecursiveASTVisitor<ImmediateEscalatingExpressionsVisitor> {
+
using Base = RecursiveASTVisitor<ImmediateEscalatingExpressionsVisitor>;
Sema &SemaRef;
+
const FunctionDecl *FD;
+ bool IsConstructor;
+ CXXCtorInitializer *CurrentInit = nullptr;
+
ImmediateEscalatingExpressionsVisitor(Sema &SemaRef, const FunctionDecl *FD)
- : SemaRef(SemaRef), FD(FD) {}
+ : SemaRef(SemaRef), FD(FD), IsConstructor(isa<CXXConstructorDecl>(FD)) {
+ }
bool shouldVisitImplicitCode() const { return true; }
bool shouldVisitLambdaBody() const { return false; }
+ void Diag(const Expr *E, const FunctionDecl *Fn, bool IsCall) {
+ SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason)
+ << FD << Fn << Fn->isConsteval() << IsCall
+ << isa<CXXConstructorDecl>(Fn) << IsConstructor
+ << (CurrentInit != nullptr)
+ << (CurrentInit != nullptr && !CurrentInit->isWritten())
+ << (CurrentInit != nullptr ? CurrentInit->getAnyMember() : nullptr)
+ << E->getSourceRange();
+ }
bool TraverseCallExpr(CallExpr *E) {
if (const auto *DR =
dyn_cast<DeclRefExpr>(E->getCallee()->IgnoreImplicit());
DR && DR->isImmediateEscalating()) {
- SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason)
- << FD << E->getDirectCallee() << E->getDirectCallee()->isConsteval()
- << /*Call*/ 1 << /*Function*/ 0 << E->getSourceRange();
- }
- for (auto A : E->arguments()) {
- getDerived().TraverseStmt(A);
+ Diag(E, E->getDirectCallee(), /*IsCall=*/true);
+ return false;
}
+
+ for (auto A : E->arguments())
+ if (!getDerived().TraverseStmt(A))
+ return false;
+
return true;
}
+
bool VisitDeclRefExpr(DeclRefExpr *E) {
if (const auto *ReferencedFn = dyn_cast<FunctionDecl>(E->getDecl());
ReferencedFn && E->isImmediateEscalating()) {
- SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason)
- << FD << ReferencedFn << ReferencedFn->isConsteval()
- << /*Address*/ 0 << /*Function*/ 0 << E->getSourceRange();
+ Diag(E, ReferencedFn, /*IsCall=*/false);
+ return false;
}
+
return true;
}
+
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
CXXConstructorDecl *D = E->getConstructor();
if (E->isImmediateEscalating()) {
- SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason)
- << FD << D << D->isConsteval() << /*Call*/ 1 << /*Constructor*/ 1
- << E->getSourceRange();
+ Diag(E, D, /*IsCall=*/true);
+ return false;
}
return true;
}
- // These nodes can never contain an immediate escalating expression,
- // we can skip them to avoid unecessary work.
- bool TraverseDecl(Decl *D) {
- if (isa<FunctionDecl, RecordDecl>(D))
- return true;
- return Base::TraverseDecl(D);
+ bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
+ llvm::SaveAndRestore RAII(CurrentInit, Init);
+ return Base::TraverseConstructorInitializer(Init);
}
bool TraverseType(QualType T) { return true; }
bool VisitBlockExpr(BlockExpr *T) { return true; }
} Visitor(*this, FD);
- Visitor.TraverseStmt(FD->getBody());
+ Visitor.TraverseDecl(const_cast<FunctionDecl *>(FD));
}
/// Get the class that is directly named by the current context. This is the
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -15575,8 +15575,7 @@
if (FD) {
FD->setBody(Body);
FD->setWillHaveBody(false);
- CheckImmediateEscalatingFunctionDefinition(
- FD, FSI->FoundImmediateEscalatingExpression);
+ CheckImmediateEscalatingFunctionDefinition(FD, FSI);
if (getLangOpts().CPlusPlus14) {
if (!FD->isInvalidDecl() && Body && !FD->isDependentContext() &&
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -5319,6 +5319,10 @@
const Decl *TargetDecl =
OrigCallee.getAbstractInfo().getCalleeDecl().getDecl();
+ assert((!isa_and_present<FunctionDecl>(TargetDecl) ||
+ !cast<FunctionDecl>(TargetDecl)->isImmediateFunction()) &&
+ "trying to emit a call to an immediate function");
+
CalleeType = getContext().getCanonicalType(CalleeType);
auto PointeeType = cast<PointerType>(CalleeType)->getPointeeType();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1078,10 +1078,13 @@
public:
SynthesizedFunctionScope(Sema &S, DeclContext *DC)
: S(S), SavedContext(S, DC) {
+ auto *FD = dyn_cast<FunctionDecl>(DC);
S.PushFunctionScope();
S.PushExpressionEvaluationContext(
- Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
- if (auto *FD = dyn_cast<FunctionDecl>(DC)) {
+ (FD && FD->isConsteval())
+ ? ExpressionEvaluationContext::ImmediateFunctionContext
+ : ExpressionEvaluationContext::PotentiallyEvaluated);
+ if (FD) {
FD->setWillHaveBody(true);
S.ExprEvalContexts.back().InImmediateFunctionContext =
FD->isImmediateFunction();
@@ -1106,8 +1109,10 @@
~SynthesizedFunctionScope() {
if (PushedCodeSynthesisContext)
S.popCodeSynthesisContext();
- if (auto *FD = dyn_cast<FunctionDecl>(S.CurContext))
+ if (auto *FD = dyn_cast<FunctionDecl>(S.CurContext)) {
FD->setWillHaveBody(false);
+ S.CheckImmediateEscalatingFunctionDefinition(FD, S.getCurFunction());
+ }
S.PopExpressionEvaluationContext();
S.PopFunctionScopeInfo();
}
@@ -6569,7 +6574,7 @@
ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl);
bool CheckImmediateEscalatingFunctionDefinition(
- FunctionDecl *FD, bool HasImmediateEscalatingExpression);
+ FunctionDecl *FD, const sema::FunctionScopeInfo *FSI);
void MarkExpressionAsImmediateEscalating(Expr *E);
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2667,7 +2667,8 @@
"immediate function %0 used before it is defined">;
def note_immediate_function_reason : Note<
- "%0 is an immediate function because its body "
+ "%0 is an immediate %select{function|constructor}5 because "
+ "%select{its body|the%select{| default}7 initializer of %8}6 "
"%select{evaluates the address of %select{an immediate|a consteval}2 "
"function %1|contains a call to %select{an immediate|a consteval}2 "
"%select{function|constructor}4 %1 and that call is not a constant "
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2727,7 +2727,11 @@
TRY_TO(TraverseStmt(S->getExpr()));
})
-DEF_TRAVERSE_STMT(CXXDefaultInitExpr, {})
+DEF_TRAVERSE_STMT(CXXDefaultInitExpr, {
+ if (getDerived().shouldVisitImplicitCode())
+ TRY_TO(TraverseStmt(S->getExpr()));
+})
+
DEF_TRAVERSE_STMT(CXXDeleteExpr, {})
DEF_TRAVERSE_STMT(ExprWithCleanups, {})
DEF_TRAVERSE_STMT(CXXInheritedCtorInitExpr, {})
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits