diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2466,7 +2466,7 @@ "cannot take address of consteval function %0 outside" " of an immediate invocation">; def err_invalid_consteval_call : Error< - "call to consteval function '%q0' is not a constant expression">; + "call to consteval function %q0 is not a constant expression">; def err_invalid_consteval_decl_kind : Error< "%0 cannot be declared consteval">; def err_invalid_constexpr : Error< diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -14762,12 +14762,14 @@ if (getLangOpts().CUDA && !CheckCUDACall(ConstructLoc, Constructor)) return ExprError(); - return CXXConstructExpr::Create( - Context, DeclInitType, ConstructLoc, Constructor, Elidable, - ExprArgs, HadMultipleCandidates, IsListInitialization, - IsStdInitListInitialization, RequiresZeroInit, - static_cast(ConstructKind), - ParenRange); + return CheckForImmediateInvocation( + CXXConstructExpr::Create( + Context, DeclInitType, ConstructLoc, Constructor, Elidable, ExprArgs, + HadMultipleCandidates, IsListInitialization, + IsStdInitListInitialization, RequiresZeroInit, + static_cast(ConstructKind), + ParenRange), + Constructor); } ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { 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 @@ -15403,6 +15403,8 @@ SemaRef.getASTContext(), true); if (!Result || !Notes.empty()) { Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); + if (auto *FunctionalCast = dyn_cast(InnerExpr)) + InnerExpr = FunctionalCast->getSubExpr(); FunctionDecl *FD = nullptr; if (auto *Call = dyn_cast(InnerExpr)) FD = cast(Call->getCalleeDecl()); @@ -15473,8 +15475,25 @@ } bool AlwaysRebuild() { return false; } bool ReplacingOriginal() { return true; } + bool AllowSkippingCXXConstructExpr() { + bool Res = AllowSkippingFirstCXXConstructExpr; + AllowSkippingFirstCXXConstructExpr = true; + return Res; + } + bool AllowSkippingFirstCXXConstructExpr = true; } Transformer(SemaRef, Rec.ReferenceToConsteval, Rec.ImmediateInvocationCandidates, It); + + /// CXXConstructExpr with a single argument are getting skipped by + /// TreeTransform in some situtation because they could be implicit. This + /// can only occur for the top-level CXXConstructExpr because it is used + /// nowhere in the expression being transformed therefore will not be rebuilt. + /// Setting AllowSkippingFirstCXXConstructExpr to false will prevent from + /// skipping the first CXXConstructExpr. + if (auto *OldExpr = + dyn_cast(It->getPointer()->IgnoreImplicit())) + Transformer.AllowSkippingFirstCXXConstructExpr = false; + ExprResult Res = Transformer.TransformExpr(It->getPointer()->getSubExpr()); assert(Res.isUsable()); Res = SemaRef.MaybeCreateExprWithCleanups(Res); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6434,12 +6434,14 @@ } S.MarkFunctionReferenced(Loc, Constructor); - CurInit = CXXTemporaryObjectExpr::Create( - S.Context, Constructor, - Entity.getType().getNonLValueExprType(S.Context), TSInfo, - ConstructorArgs, ParenOrBraceRange, HadMultipleCandidates, - IsListInitialization, IsStdInitListInitialization, - ConstructorInitRequiresZeroInit); + CurInit = S.CheckForImmediateInvocation( + CXXTemporaryObjectExpr::Create( + S.Context, Constructor, + Entity.getType().getNonLValueExprType(S.Context), TSInfo, + ConstructorArgs, ParenOrBraceRange, HadMultipleCandidates, + IsListInitialization, IsStdInitListInitialization, + ConstructorInitRequiresZeroInit), + Constructor); } else { CXXConstructExpr::ConstructionKind ConstructKind = CXXConstructExpr::CK_Complete; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -157,6 +157,13 @@ /// existing lambdas. bool ReplacingOriginal() { return false; } + /// Wether CXXConstructExpr can be skipped when they are implicit. + /// They will be reconstructed when used if needed. + /// This is usefull when the user that cause rebuilding of the + /// CXXConstructExpr is outside of the expression at which the TreeTransform + /// started. + bool AllowSkippingCXXConstructExpr() { return true; } + /// Returns the location of the entity being transformed, if that /// information was not available elsewhere in the AST. /// @@ -11658,10 +11665,11 @@ // CXXConstructExprs other than for list-initialization and // CXXTemporaryObjectExpr are always implicit, so when we have // a 1-argument construction we just transform that argument. - if ((E->getNumArgs() == 1 || - (E->getNumArgs() > 1 && getDerived().DropCallArgument(E->getArg(1)))) && - (!getDerived().DropCallArgument(E->getArg(0))) && - !E->isListInitialization()) + if (getDerived().AllowSkippingCXXConstructExpr() && + ((E->getNumArgs() == 1 || + (E->getNumArgs() > 1 && getDerived().DropCallArgument(E->getArg(1)))) && + (!getDerived().DropCallArgument(E->getArg(0))) && + !E->isListInitialization())) return getDerived().TransformExpr(E->getArg(0)); TemporaryBase Rebase(*this, /*FIXME*/ E->getBeginLoc(), DeclarationName()); 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 @@ -260,6 +260,19 @@ } +namespace std { + +template struct remove_reference { using type = T; }; +template struct remove_reference { using type = T; }; +template struct remove_reference { using type = T; }; + +template +constexpr typename std::remove_reference::type&& move(T &&t) noexcept { + return static_cast::type &&>(t); +} + +} + namespace temporaries { struct A { @@ -295,12 +308,12 @@ { int k = const_a_ref(A()); } { int k = const_a_ref(a); } { int k = rvalue_ref(A()); } - { int k = rvalue_ref(static_cast(a)); } + { int k = rvalue_ref(std::move(a)); } { int k = const_a_ref(A().ret_a()); } { int k = const_a_ref(to_lvalue_ref(A().ret_a())); } - { int k = const_a_ref(to_lvalue_ref(static_cast(a))); } + { int k = const_a_ref(to_lvalue_ref(std::move(a))); } { int k = by_value_a(A().ret_a()); } - { int k = by_value_a(to_lvalue_ref(static_cast(a))); } + { int k = by_value_a(to_lvalue_ref(std::move(a))); } { int k = (A().ret_a(), A().ret_i()); } { int k = (const_a_ref(A().ret_a()), A().ret_i()); }// } @@ -353,10 +366,10 @@ { int k = const_a_ref(A()); } { int k = const_a_ref(a); } { int k = rvalue_ref(A()); } - { int k = rvalue_ref(static_cast(a)); } + { int k = rvalue_ref(std::move(a)); } { int k = const_a_ref(A().ret_a()); } { int k = const_a_ref(to_lvalue_ref(A().ret_a())); } - { int k = const_a_ref(to_lvalue_ref(static_cast(a))); } + { int k = const_a_ref(to_lvalue_ref(std::move(a))); } { int k = by_value_a(A().ret_a()); } { int k = by_value_a(to_lvalue_ref(static_cast(a))); } { int k = (A().ret_a(), A().ret_i()); }// expected-error {{is not a constant expression}} @@ -388,6 +401,27 @@ // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} } +struct S1 { + S1* ptr = nullptr; + consteval S1(int i) : ptr(this) { + if (this == ptr && i) + ptr = nullptr; + } + constexpr ~S1() {} +}; + +void test1() { + S1 s(1); + s = S1(1); + s = S1(0); // expected-error {{is not a constant expression}} + // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} +} + +} +namespace ctor { + +consteval int f_eval() { // expected-note+ {{declared here}} + return 0; } namespace std { @@ -441,3 +475,103 @@ }; } } + +struct A { + int(*ptr)(); + consteval A(int(*p)() = nullptr) : ptr(p) {} +}; + +struct B { + int(*ptr)(); + B() : ptr(nullptr) {} + consteval B(int(*p)(), int) : ptr(p) {} +}; + +void test() { + { A a; } + { A a(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B b(nullptr, 0); } + { B b(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { A a{}; } + { A a{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B b{nullptr, 0}; } + { B b{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { A a = A(); } + { A a = A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B b = B(nullptr, 0); } + { B b = B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { A a = A{}; } + { A a = A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B b = B{nullptr, 0}; } + { B b = B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { A a; a = A(); } + { A a; a = A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B b; b = B(nullptr, 0); } + { B b; b = B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { A a; a = A{}; } + { A a; a = A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B b; b = B{nullptr, 0}; } + { B b; b = B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { A* a; a = new A(); } + { A* a; a = new A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B* b; b = new B(nullptr, 0); } + { B* b; b = new B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { A* a; a = new A{}; } + { A* a; a = new A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { B* b; b = new B{nullptr, 0}; } + { B* b; b = new B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}} +} + +} + +namespace copy_ctor { + +consteval int f_eval() { // expected-note+ {{declared here}} + return 0; +} + +struct Copy { + int(*ptr)(); + constexpr Copy(int(*p)() = nullptr) : ptr(p) {} + consteval Copy(const Copy&) = default; +}; + +constexpr const Copy &to_lvalue_ref(const Copy &&a) { + return a; +} + +void test() { + constexpr const Copy C; + // there is no the copy constructor call when its argument is a prvalue because of garanteed copy elision. + // so we need to test with both prvalue and xvalues. + { Copy c(C); } + { Copy c((Copy(&f_eval))); }// expected-error {{cannot take address of consteval}} + { Copy c(std::move(C)); } + { Copy c(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c(to_lvalue_ref((Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c(to_lvalue_ref(std::move(C))); } + { Copy c(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c = Copy(C); } + { Copy c = Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}} + { Copy c = Copy(std::move(C)); } + { Copy c = Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c = Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c = Copy(to_lvalue_ref(std::move(C))); } + { Copy c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c; c = Copy(C); } + { Copy c; c = Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}} + { Copy c; c = Copy(std::move(C)); } + { Copy c; c = Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c; c = Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy c; c = Copy(to_lvalue_ref(std::move(C))); } + { Copy c; c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy* c; c = new Copy(C); } + { Copy* c; c = new Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}} + { Copy* c; c = new Copy(std::move(C)); } + { Copy* c; c = new Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy* c; c = new Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} + { Copy* c; c = new Copy(to_lvalue_ref(std::move(C))); } + { Copy* c; c = new Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}} +} + +} // namespace special_ctor