summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2013-05-07 03:19:20 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2013-05-07 03:19:20 +0000
commita8942d7686dde6d221a176c502ce857bdc409dab (patch)
tree4e9fa5c6b51f4278370535448b2d7bfa57e46504
parent3139d664ceb0a352cd253b52dbf82c602e8de00d (diff)
downloadclang-a8942d7686dde6d221a176c502ce857bdc409dab.tar.gz
clang-a8942d7686dde6d221a176c502ce857bdc409dab.tar.bz2
clang-a8942d7686dde6d221a176c502ce857bdc409dab.tar.xz
C++1y: an assignment operator is implicitly 'constexpr' if it would only call 'constexpr' assignment operators for a literal class type.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181284 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td5
-rw-r--r--lib/AST/ExprConstant.cpp21
-rw-r--r--lib/Sema/SemaDeclCXX.cpp68
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp12
-rw-r--r--test/SemaCXX/constant-expression-cxx1y.cpp43
5 files changed, 125 insertions, 24 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index f5345eb8c3..0c308469fc 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5718,7 +5718,7 @@ def err_defaulted_special_member_return_type : Error<
"return %1">;
def err_defaulted_special_member_quals : Error<
"an explicitly-defaulted %select{copy|move}0 assignment operator may not "
- "have 'const', 'constexpr' or 'volatile' qualifiers">;
+ "have 'const'%select{, 'constexpr'|}1 or 'volatile' qualifiers">;
def err_defaulted_special_member_volatile_param : Error<
"the parameter for an explicitly-defaulted %select{<<ERROR>>|"
"copy constructor|move constructor|copy assignment operator|"
@@ -5740,7 +5740,8 @@ def err_incorrect_defaulted_exception_spec : Error<
"calculated one">;
def err_incorrect_defaulted_constexpr : Error<
"defaulted definition of %select{default constructor|copy constructor|"
- "move constructor}0 is not constexpr">;
+ "move constructor|copy assignment operator|move assignment operator}0 "
+ "is not constexpr">;
def err_out_of_line_default_deletes : Error<
"defaulting this %select{default constructor|copy constructor|move "
"constructor|copy assignment operator|move assignment operator|destructor}0 "
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 8c650290b5..a3315e3b87 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -2774,6 +2774,27 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
return false;
CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data());
+
+ // For a trivial copy or move assignment, perform an APValue copy. This is
+ // essential for unions, where the operations performed by the assignment
+ // operator cannot be represented as statements.
+ const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee);
+ if (MD && MD->isDefaulted() && MD->isTrivial()) {
+ assert(This &&
+ (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
+ LValue RHS;
+ RHS.setFrom(Info.Ctx, ArgValues[0]);
+ APValue RHSValue;
+ if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
+ RHS, RHSValue))
+ return false;
+ if (!handleAssignment(Info, Args[0], *This, MD->getThisType(Info.Ctx),
+ RHSValue))
+ return false;
+ This->moveInto(Result);
+ return true;
+ }
+
EvalStmtResult ESR = EvaluateStmt(Result, Info, Body);
if (ESR == ESR_Succeeded) {
if (Callee->getResultType()->isVoidType())
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index e6a131a076..a4e65ee765 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -4278,6 +4278,7 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
// C++11 [dcl.constexpr]p4:
// In the definition of a constexpr constructor [...]
+ bool Ctor = true;
switch (CSM) {
case Sema::CXXDefaultConstructor:
// Since default constructor lookup is essentially trivial (and cannot
@@ -4295,6 +4296,12 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
case Sema::CXXCopyAssignment:
case Sema::CXXMoveAssignment:
+ if (!S.getLangOpts().CPlusPlus1y)
+ return false;
+ // In C++1y, we need to perform overload resolution.
+ Ctor = false;
+ break;
+
case Sema::CXXDestructor:
case Sema::CXXInvalid:
return false;
@@ -4307,15 +4314,22 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
// If we squint, this is guaranteed, since exactly one non-static data member
// will be initialized (if the constructor isn't deleted), we just don't know
// which one.
- if (ClassDecl->isUnion())
+ if (Ctor && ClassDecl->isUnion())
return true;
// -- the class shall not have any virtual base classes;
- if (ClassDecl->getNumVBases())
+ if (Ctor && ClassDecl->getNumVBases())
+ return false;
+
+ // C++1y [class.copy]p26:
+ // -- [the class] is a literal type, and
+ if (!Ctor && !ClassDecl->isLiteral())
return false;
// -- every constructor involved in initializing [...] base class
// sub-objects shall be a constexpr constructor;
+ // -- the assignment operator selected to copy/move each direct base
+ // class is a constexpr function, and
for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(),
BEnd = ClassDecl->bases_end();
B != BEnd; ++B) {
@@ -4331,6 +4345,9 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
// [...] shall be a constexpr constructor;
// -- every non-static data member and base class sub-object shall be
// initialized
+ // -- for each non-stastic data member of X that is of class type (or array
+ // thereof), the assignment operator selected to copy/move that member is
+ // a constexpr function
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
FEnd = ClassDecl->field_end();
F != FEnd; ++F) {
@@ -4461,7 +4478,7 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
// A defaulted special member cannot have cv-qualifiers.
if (Type->getTypeQuals()) {
Diag(MD->getLocation(), diag::err_defaulted_special_member_quals)
- << (CSM == CXXMoveAssignment);
+ << (CSM == CXXMoveAssignment) << getLangOpts().CPlusPlus1y;
HadError = true;
}
}
@@ -4506,13 +4523,16 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
// would have been implicitly declared as constexpr,
// Do not apply this rule to members of class templates, since core issue 1358
// makes such functions always instantiate to constexpr functions. For
- // non-constructors, this is checked elsewhere.
+ // functions which cannot be constexpr (for non-constructors in C++11 and for
+ // destructors in C++1y), this is checked elsewhere.
bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM,
HasConstParam);
- if (isa<CXXConstructorDecl>(MD) && MD->isConstexpr() && !Constexpr &&
+ if ((getLangOpts().CPlusPlus1y ? !isa<CXXDestructorDecl>(MD)
+ : isa<CXXConstructorDecl>(MD)) &&
+ MD->isConstexpr() && !Constexpr &&
MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
Diag(MD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM;
- // FIXME: Explain why the constructor can't be constexpr.
+ // FIXME: Explain why the special member can't be constexpr.
HadError = true;
}
@@ -8736,21 +8756,24 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
QualType ArgType = Context.getTypeDeclType(ClassDecl);
QualType RetType = Context.getLValueReferenceType(ArgType);
- if (ClassDecl->implicitCopyAssignmentHasConstParam())
+ bool Const = ClassDecl->implicitCopyAssignmentHasConstParam();
+ if (Const)
ArgType = ArgType.withConst();
ArgType = Context.getLValueReferenceType(ArgType);
+ bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
+ CXXCopyAssignment,
+ Const);
+
// An implicitly-declared copy assignment operator is an inline public
// member of its class.
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
SourceLocation ClassLoc = ClassDecl->getLocation();
DeclarationNameInfo NameInfo(Name, ClassLoc);
- CXXMethodDecl *CopyAssignment
- = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
- /*TInfo=*/0,
- /*StorageClass=*/SC_None,
- /*isInline=*/true, /*isConstexpr=*/false,
- SourceLocation());
+ CXXMethodDecl *CopyAssignment =
+ CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
+ /*TInfo=*/ 0, /*StorageClass=*/ SC_None,
+ /*isInline=*/ true, Constexpr, SourceLocation());
CopyAssignment->setAccess(AS_public);
CopyAssignment->setDefaulted();
CopyAssignment->setImplicit();
@@ -8775,7 +8798,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
? SpecialMemberIsTrivial(CopyAssignment, CXXCopyAssignment)
: ClassDecl->hasTrivialCopyAssignment());
- // C++0x [class.copy]p19:
+ // C++11 [class.copy]p19:
// .... If the class definition does not explicitly declare a copy
// assignment operator, there is no user-declared move constructor, and
// there is no user-declared move assignment operator, a copy assignment
@@ -9188,18 +9211,19 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) {
QualType RetType = Context.getLValueReferenceType(ArgType);
ArgType = Context.getRValueReferenceType(ArgType);
+ bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
+ CXXMoveAssignment,
+ false);
+
// An implicitly-declared move assignment operator is an inline public
// member of its class.
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
SourceLocation ClassLoc = ClassDecl->getLocation();
DeclarationNameInfo NameInfo(Name, ClassLoc);
- CXXMethodDecl *MoveAssignment
- = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
- /*TInfo=*/0,
- /*StorageClass=*/SC_None,
- /*isInline=*/true,
- /*isConstexpr=*/false,
- SourceLocation());
+ CXXMethodDecl *MoveAssignment =
+ CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
+ /*TInfo=*/0, /*StorageClass=*/SC_None,
+ /*isInline=*/true, Constexpr, SourceLocation());
MoveAssignment->setAccess(AS_public);
MoveAssignment->setDefaulted();
MoveAssignment->setImplicit();
@@ -9732,7 +9756,7 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
SourceLocation ClassLoc = ClassDecl->getLocation();
DeclarationNameInfo NameInfo(Name, ClassLoc);
- // C++0x [class.copy]p11:
+ // C++11 [class.copy]p11:
// An implicitly-declared copy/move constructor is an inline public
// member of its class.
CXXConstructorDecl *MoveConstructor = CXXConstructorDecl::Create(
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
index 4393727c19..780a420959 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
@@ -63,8 +63,20 @@ struct T : SS, NonLiteral { // expected-note {{base class 'NonLiteral' of non-li
#ifndef CXX1Y
// expected-error@-2 {{an explicitly-defaulted copy assignment operator may not have 'const', 'constexpr' or 'volatile' qualifiers}}
// expected-warning@-3 {{C++1y}}
+#else
+ // expected-error@-5 {{defaulted definition of copy assignment operator is not constexpr}}
#endif
};
+#ifdef CXX1Y
+struct T2 {
+ int n = 0;
+ constexpr T2 &operator=(const T2&) = default; // ok
+};
+struct T3 {
+ constexpr T3 &operator=(const T3&) const = default;
+ // expected-error@-1 {{an explicitly-defaulted copy assignment operator may not have 'const' or 'volatile' qualifiers}}
+};
+#endif
struct U {
constexpr U SelfReturn() const;
constexpr int SelfParam(U) const;
diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp
index 60ec82062d..198b9945da 100644
--- a/test/SemaCXX/constant-expression-cxx1y.cpp
+++ b/test/SemaCXX/constant-expression-cxx1y.cpp
@@ -457,3 +457,46 @@ namespace loops {
}
static_assert(range_for_2() == 10, "");
}
+
+namespace assignment {
+ struct A {
+ constexpr A() : n(5) {}
+ int n;
+ struct B {
+ int k = 1;
+ union U {
+ constexpr U() : y(4) {}
+ int x;
+ int y;
+ } u;
+ } b;
+ };
+ constexpr bool testA() {
+ A a, b;
+ a.n = 7;
+ a.b.u.y = 5;
+ b = a;
+ return b.n == 7 && b.b.u.y == 5 && b.b.k == 1;
+ }
+ static_assert(testA(), "");
+
+ struct B {
+ bool assigned = false;
+ constexpr B &operator=(const B&) {
+ assigned = true;
+ return *this;
+ }
+ };
+ struct C : B {
+ B b;
+ int n = 5;
+ };
+ constexpr bool testC() {
+ C c, d;
+ c.n = 7;
+ d = c;
+ c.n = 3;
+ return d.n == 7 && d.assigned && d.b.assigned;
+ }
+ static_assert(testC(), "");
+}