[clang] 180581c - [clang] Add support for consteval constructors
via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 20 03:34:18 PDT 2020
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 at -1 {{is not a constant expression}} expected-note at -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 at -1 {{is not a constant expression}} expected-note at -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
More information about the cfe-commits
mailing list