forked from OSchip/llvm-project
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:
parent
badcd986bb
commit
99005e65cd
|
@ -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 "
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(), "");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue