C++1y: an assignment operator is implicitly 'constexpr' if it would only call 'constexpr' assignment operators for a literal class type.

llvm-svn: 181284
This commit is contained in:
Richard Smith 2013-05-07 03:19:20 +00:00
parent badcd986bb
commit 99005e65cd
5 changed files with 125 additions and 24 deletions

View File

@ -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 "

View File

@ -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())

View File

@ -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(

View File

@ -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;

View File

@ -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(), "");
}