Author: Tyker Date: 2020-03-20T11:33:54+01:00 New Revision: 180581cfcf5115388caa3b8ee20eac1fd35f2d11
URL: https://github.com/llvm/llvm-project/commit/180581cfcf5115388caa3b8ee20eac1fd35f2d11 DIFF: https://github.com/llvm/llvm-project/commit/180581cfcf5115388caa3b8ee20eac1fd35f2d11.diff LOG: [clang] Add support for consteval constructors Summary: Changes: - handle immediate invocations for constructors. - add tests after this patch i believe the implementation of consteval is nearly standard compliant, but IR-gen still needs to be taught not to emit consteval declarations. Reviewers: rsmith Reviewed By: rsmith Subscribers: wchilders Differential Revision: https://reviews.llvm.org/D74007 Added: Modified: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaInit.cpp clang/lib/Sema/TreeTransform.h clang/test/SemaCXX/cxx2a-consteval.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f777e0ae4c81..92af99fe5719 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2466,7 +2466,7 @@ def err_invalid_consteval_take_address : Error< "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 index 2cc770e5f3d1..37a5be48b97b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -14762,12 +14762,14 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, if (getLangOpts().CUDA && !CheckCUDACall(ConstructLoc, Constructor)) return ExprError(); - return CXXConstructExpr::Create( - Context, DeclInitType, ConstructLoc, Constructor, Elidable, - ExprArgs, HadMultipleCandidates, IsListInitialization, - IsStdInitListInitialization, RequiresZeroInit, - static_cast<CXXConstructExpr::ConstructionKind>(ConstructKind), - ParenRange); + return CheckForImmediateInvocation( + CXXConstructExpr::Create( + Context, DeclInitType, ConstructLoc, Constructor, Elidable, ExprArgs, + HadMultipleCandidates, IsListInitialization, + IsStdInitListInitialization, RequiresZeroInit, + static_cast<CXXConstructExpr::ConstructionKind>(ConstructKind), + ParenRange), + Constructor); } ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index c9de06ce76cb..eaded8e92d7c 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15403,6 +15403,8 @@ static void EvaluateAndDiagnoseImmediateInvocation( SemaRef.getASTContext(), true); if (!Result || !Notes.empty()) { Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); + if (auto *FunctionalCast = dyn_cast<CXXFunctionalCastExpr>(InnerExpr)) + InnerExpr = FunctionalCast->getSubExpr(); FunctionDecl *FD = nullptr; if (auto *Call = dyn_cast<CallExpr>(InnerExpr)) FD = cast<FunctionDecl>(Call->getCalleeDecl()); @@ -15473,8 +15475,25 @@ static void RemoveNestedImmediateInvocation( } 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<CXXConstructExpr>(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 index 0e7b839f69dd..cefedc63c341 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6434,12 +6434,14 @@ PerformConstructorInitialization(Sema &S, } 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 index e803300bbf16..9186ef0deb2a 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -157,6 +157,13 @@ class TreeTransform { /// 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 @@ TreeTransform<Derived>::TransformCXXConstructExpr(CXXConstructExpr *E) { // 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 index 954f424f7d9a..a1716b4fa8c3 100644 --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -260,6 +260,19 @@ auto l1 = [](int i) constexpr { } +namespace std { + +template <typename T> struct remove_reference { using type = T; }; +template <typename T> struct remove_reference<T &> { using type = T; }; +template <typename T> struct remove_reference<T &&> { using type = T; }; + +template <typename T> +constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept { + return static_cast<typename std::remove_reference<T>::type &&>(t); +} + +} + namespace temporaries { struct A { @@ -295,12 +308,12 @@ void test() { { int k = const_a_ref(A()); } { int k = const_a_ref(a); } { int k = rvalue_ref(A()); } - { int k = rvalue_ref(static_cast<const A&&>(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<const A&&>(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<const A&&>(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 @@ void test() { { int k = const_a_ref(A()); } { int k = const_a_ref(a); } { int k = rvalue_ref(A()); } - { int k = rvalue_ref(static_cast<const A&&>(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<const A&&>(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<const A&&>(a))); } { int k = (A().ret_a(), A().ret_i()); }// expected-error {{is not a constant expression}} @@ -388,6 +401,27 @@ void test() { // 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 @@ namespace override { }; } } + +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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits