summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
authorRichard Smith <richard@metafoo.co.uk>2019-10-22 17:44:08 -0700
committerRichard Smith <richard@metafoo.co.uk>2019-10-22 18:16:17 -0700
commitd052a578de58cbbb638cbe2dba05242d1ff443b9 (patch)
tree2f3ed903007c6a0459234ce4d05c40e8a33889a6 /clang/lib
parent437e0e5191ca255db27e86d232020844c1fd08c8 (diff)
downloadbcm5719-llvm-d052a578de58cbbb638cbe2dba05242d1ff443b9.tar.gz
bcm5719-llvm-d052a578de58cbbb638cbe2dba05242d1ff443b9.zip
[c++2a] Allow comparison functions to be explicitly defaulted.
This adds some initial syntactic checking that only the appropriate function signatures can be defaulted. No implicit definitions are generated yet.
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/Decl.cpp23
-rw-r--r--clang/lib/Parse/ParseDecl.cpp3
-rw-r--r--clang/lib/Parse/ParseDeclCXX.cpp3
-rw-r--r--clang/lib/Sema/SemaDecl.cpp22
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp285
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp9
6 files changed, 260 insertions, 85 deletions
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 80235d8496d..dae4af8bb24 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3322,12 +3322,14 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
return FoundBody;
}
-SourceRange FunctionDecl::getReturnTypeSourceRange() const {
+FunctionTypeLoc FunctionDecl::getFunctionTypeLoc() const {
const TypeSourceInfo *TSI = getTypeSourceInfo();
- if (!TSI)
- return SourceRange();
- FunctionTypeLoc FTL =
- TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
+ return TSI ? TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>()
+ : FunctionTypeLoc();
+}
+
+SourceRange FunctionDecl::getReturnTypeSourceRange() const {
+ FunctionTypeLoc FTL = getFunctionTypeLoc();
if (!FTL)
return SourceRange();
@@ -3343,15 +3345,8 @@ SourceRange FunctionDecl::getReturnTypeSourceRange() const {
}
SourceRange FunctionDecl::getExceptionSpecSourceRange() const {
- const TypeSourceInfo *TSI = getTypeSourceInfo();
- if (!TSI)
- return SourceRange();
- FunctionTypeLoc FTL =
- TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
- if (!FTL)
- return SourceRange();
-
- return FTL.getExceptionSpecRange();
+ FunctionTypeLoc FTL = getFunctionTypeLoc();
+ return FTL ? FTL.getExceptionSpecRange() : SourceRange();
}
/// For an inline function definition in C, or for a gnu_inline function
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index b248d7582d8..c41eb74a9cf 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2349,7 +2349,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
<< 0 /* default */;
else
- Diag(ConsumeToken(), diag::err_default_special_members);
+ Diag(ConsumeToken(), diag::err_default_special_members)
+ << getLangOpts().CPlusPlus2a;
} else {
InitializerScopeRAII InitScope(*this, D, ThisDecl);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index b98ce3e6629..6d4a1a4a4e8 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2978,7 +2978,8 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
Diag(Tok, diag::err_default_delete_in_multiple_declaration)
<< 0 /* default */;
else
- Diag(ConsumeToken(), diag::err_default_special_members);
+ Diag(ConsumeToken(), diag::err_default_special_members)
+ << getLangOpts().CPlusPlus2a;
return ExprError();
}
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 62ec83967bf..6202391ee0b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2993,28 +2993,6 @@ struct GNUCompatibleParamWarning {
} // end anonymous namespace
-/// getSpecialMember - get the special member enum for a method.
-Sema::CXXSpecialMember Sema::getSpecialMember(const CXXMethodDecl *MD) {
- if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(MD)) {
- if (Ctor->isDefaultConstructor())
- return Sema::CXXDefaultConstructor;
-
- if (Ctor->isCopyConstructor())
- return Sema::CXXCopyConstructor;
-
- if (Ctor->isMoveConstructor())
- return Sema::CXXMoveConstructor;
- } else if (isa<CXXDestructorDecl>(MD)) {
- return Sema::CXXDestructor;
- } else if (MD->isCopyAssignmentOperator()) {
- return Sema::CXXCopyAssignment;
- } else if (MD->isMoveAssignmentOperator()) {
- return Sema::CXXMoveAssignment;
- }
-
- return Sema::CXXInvalid;
-}
-
// Determine whether the previous declaration was a definition, implicit
// declaration, or a declaration.
template <typename T>
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ff90b9548e2..0201d014e6f 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6084,6 +6084,67 @@ void Sema::propagateDLLAttrToBaseClassTemplate(
}
}
+/// Determine the kind of defaulting that would be done for a given function.
+///
+/// If the function is both a default constructor and a copy / move constructor
+/// (due to having a default argument for the first parameter), this picks
+/// CXXDefaultConstructor.
+///
+/// FIXME: Check that case is properly handled by all callers.
+Sema::DefaultedFunctionKind
+Sema::getDefaultedFunctionKind(const FunctionDecl *FD) {
+ if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(FD)) {
+ if (Ctor->isDefaultConstructor())
+ return Sema::CXXDefaultConstructor;
+
+ if (Ctor->isCopyConstructor())
+ return Sema::CXXCopyConstructor;
+
+ if (Ctor->isMoveConstructor())
+ return Sema::CXXMoveConstructor;
+ }
+
+ if (MD->isCopyAssignmentOperator())
+ return Sema::CXXCopyAssignment;
+
+ if (MD->isMoveAssignmentOperator())
+ return Sema::CXXMoveAssignment;
+
+ if (isa<CXXDestructorDecl>(FD))
+ return Sema::CXXDestructor;
+ }
+
+ switch (FD->getDeclName().getCXXOverloadedOperator()) {
+ case OO_EqualEqual:
+ return DefaultedComparisonKind::Equal;
+
+ case OO_ExclaimEqual:
+ return DefaultedComparisonKind::NotEqual;
+
+ case OO_Spaceship:
+ // No point allowing this if <=> doesn't exist in the current language mode.
+ if (!getLangOpts().CPlusPlus2a)
+ break;
+ return DefaultedComparisonKind::ThreeWay;
+
+ case OO_Less:
+ case OO_LessEqual:
+ case OO_Greater:
+ case OO_GreaterEqual:
+ // No point allowing this if <=> doesn't exist in the current language mode.
+ if (!getLangOpts().CPlusPlus2a)
+ break;
+ return DefaultedComparisonKind::Relational;
+
+ default:
+ break;
+ }
+
+ // Not defaultable.
+ return DefaultedFunctionKind();
+}
+
static void DefineImplicitSpecialMember(Sema &S, CXXMethodDecl *MD,
SourceLocation DefaultLoc) {
switch (S.getSpecialMember(MD)) {
@@ -6331,9 +6392,9 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
Record->setHasTrivialSpecialMemberForCall();
auto CompleteMemberFunction = [&](CXXMethodDecl *M) {
- // Check whether the explicitly-defaulted special members are valid.
+ // Check whether the explicitly-defaulted members are valid.
if (!M->isInvalidDecl() && M->isExplicitlyDefaulted())
- CheckExplicitlyDefaultedSpecialMember(M);
+ CheckExplicitlyDefaultedFunction(M);
// For an explicitly defaulted or deleted special member, we defer
// determining triviality until the class is complete. That time is now!
@@ -6413,6 +6474,15 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
DiagnoseAbsenceOfOverrideControl(M);
}
+ // Process any defaulted friends in the member-specification.
+ if (!Record->isDependentType()) {
+ for (FriendDecl *D : Record->friends()) {
+ auto *FD = dyn_cast_or_null<FunctionDecl>(D->getFriendDecl());
+ if (FD && !FD->isInvalidDecl() && FD->isExplicitlyDefaulted())
+ CheckExplicitlyDefaultedFunction(FD);
+ }
+ }
+
// ms_struct is a request to use the same ABI rules as MSVC. Check
// whether this class uses any C++ features that are implemented
// completely differently in MSVC, and if so, emit a diagnostic.
@@ -6766,9 +6836,22 @@ void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD)
UpdateExceptionSpec(MD->getCanonicalDecl(), ESI);
}
-void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
+void Sema::CheckExplicitlyDefaultedFunction(FunctionDecl *FD) {
+ assert(FD->isExplicitlyDefaulted() && "not explicitly-defaulted");
+
+ DefaultedFunctionKind DefKind = getDefaultedFunctionKind(FD);
+ assert(DefKind && "not a defaultable function");
+
+ if (DefKind.isSpecialMember()
+ ? CheckExplicitlyDefaultedSpecialMember(cast<CXXMethodDecl>(FD),
+ DefKind.asSpecialMember())
+ : CheckExplicitlyDefaultedComparison(FD, DefKind.asComparison()))
+ FD->setInvalidDecl();
+}
+
+bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
+ CXXSpecialMember CSM) {
CXXRecordDecl *RD = MD->getParent();
- CXXSpecialMember CSM = getSpecialMember(MD);
assert(MD->isExplicitlyDefaulted() && CSM != CXXInvalid &&
"not an explicitly-defaulted special member");
@@ -6781,7 +6864,7 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
// C++11 [dcl.fct.def.default]p1:
// A function that is explicitly defaulted shall
- // -- be a special member function (checked elsewhere),
+ // -- be a special member function [...] (checked elsewhere),
// -- have the same type (except for ref-qualifiers, and except that a
// copy operation can take a non-const reference) as an implicit
// declaration, and
@@ -6960,8 +7043,87 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
}
}
- if (HadError)
- MD->setInvalidDecl();
+ return HadError;
+}
+
+bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
+ DefaultedComparisonKind DCK) {
+ assert(DCK != DefaultedComparisonKind::None && "not a defaulted comparison");
+
+ // C++2a [class.compare.default]p1:
+ // A defaulted comparison operator function for some class C shall be a
+ // non-template function declared in the member-specification of C that is
+ // -- a non-static const member of C having one parameter of type
+ // const C&, or
+ // -- a friend of C having two parameters of type const C&.
+ CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(FD->getLexicalDeclContext());
+ assert(RD && "defaulted comparison is not defaulted in a class");
+
+ QualType ExpectedParmType =
+ Context.getLValueReferenceType(Context.getRecordType(RD).withConst());
+ for (const ParmVarDecl *Param : FD->parameters()) {
+ if (!Context.hasSameType(Param->getType(), ExpectedParmType)) {
+ Diag(FD->getLocation(), diag::err_defaulted_comparison_param)
+ << (int)DCK << Param->getType() << ExpectedParmType
+ << Param->getSourceRange();
+ return true;
+ }
+ }
+
+ // ... non-static const member ...
+ if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ assert(!MD->isStatic() && "comparison function cannot be a static member");
+ if (!MD->isConst()) {
+ SourceLocation InsertLoc;
+ if (FunctionTypeLoc Loc = MD->getFunctionTypeLoc())
+ InsertLoc = getLocForEndOfToken(Loc.getRParenLoc());
+ Diag(MD->getLocation(), diag::err_defaulted_comparison_non_const)
+ << (int)DCK << FixItHint::CreateInsertion(InsertLoc, " const");
+
+ // Add the 'const' to the type to recover.
+ const auto *FPT = MD->getType()->castAs<FunctionProtoType>();
+ FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
+ EPI.TypeQuals.addConst();
+ MD->setType(Context.getFunctionType(FPT->getReturnType(),
+ FPT->getParamTypes(), EPI));
+ }
+ } else {
+ // A non-member function declared in a class must be a friend.
+ assert(FD->getFriendObjectKind() && "expected a friend declaration");
+ }
+
+ // C++2a [class.compare.default]p2:
+ // A defaulted comparison operator function for class C is defined as
+ // deleted if any non-static data member of C is of reference type or C is
+ // a union-like class.
+ // FIXME: Applying this to cases other than == and <=> is unreasonable.
+ // FIXME: Implement.
+
+ // C++2a [class.eq]p1, [class.rel]p1:
+ // A [defaulted comparison other than <=>] shall have a declared return
+ // type bool.
+ if (DCK != DefaultedComparisonKind::ThreeWay &&
+ !Context.hasSameType(FD->getDeclaredReturnType(), Context.BoolTy)) {
+ Diag(FD->getLocation(), diag::err_defaulted_comparison_return_type_not_bool)
+ << (int)DCK << FD->getDeclaredReturnType() << Context.BoolTy
+ << FD->getReturnTypeSourceRange();
+ return true;
+ }
+
+ // FIXME: Determine whether the function should be defined as deleted.
+
+ // C++2a [dcl.fct.def.default]p3:
+ // An explicitly-defaulted function [..] may be declared constexpr or
+ // consteval only if it would have been implicitly declared constexpr.
+ // FIXME: There are no rules governing when these should be constexpr,
+ // except for the special case of the injected operator==, for which
+ // C++2a [class.compare.default]p3 says:
+ // The operator is a constexpr function if its definition would satisfy
+ // the requirements for a constexpr function.
+ // FIXME: Apply this rule to all defaulted comparisons. The only way this
+ // can fail is if the return type of a defaulted operator<=> is not a literal
+ // type.
+ return false;
}
void Sema::CheckDelayedMemberExceptionSpecs() {
@@ -15006,51 +15168,88 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) {
}
void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
- CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Dcl);
+ if (!Dcl || Dcl->isInvalidDecl())
+ return;
- if (MD) {
- if (MD->getParent()->isDependentType()) {
- MD->setDefaulted();
- MD->setExplicitlyDefaulted();
- return;
+ auto *FD = dyn_cast<FunctionDecl>(Dcl);
+ if (!FD) {
+ if (auto *FTD = dyn_cast<FunctionTemplateDecl>(Dcl)) {
+ if (getDefaultedFunctionKind(FTD->getTemplatedDecl()).isComparison()) {
+ Diag(DefaultLoc, diag::err_defaulted_comparison_template);
+ return;
+ }
}
- CXXSpecialMember Member = getSpecialMember(MD);
- if (Member == CXXInvalid) {
- if (!MD->isInvalidDecl())
- Diag(DefaultLoc, diag::err_default_special_members);
- return;
- }
+ Diag(DefaultLoc, diag::err_default_special_members)
+ << getLangOpts().CPlusPlus2a;
+ return;
+ }
- MD->setDefaulted();
- MD->setExplicitlyDefaulted();
+ // Reject if this can't possibly be a defaultable function.
+ DefaultedFunctionKind DefKind = getDefaultedFunctionKind(FD);
+ if (!DefKind &&
+ // A dependent function that doesn't locally look defaultable can
+ // still instantiate to a defaultable function if it's a constructor
+ // or assignment operator.
+ (!FD->isDependentContext() ||
+ (!isa<CXXConstructorDecl>(FD) &&
+ FD->getDeclName().getCXXOverloadedOperator() != OO_Equal))) {
+ Diag(DefaultLoc, diag::err_default_special_members)
+ << getLangOpts().CPlusPlus2a;
+ return;
+ }
- // Unset that we will have a body for this function. We might not,
- // if it turns out to be trivial, and we don't need this marking now
- // that we've marked it as defaulted.
- MD->setWillHaveBody(false);
+ if (DefKind.isComparison() &&
+ !isa<CXXRecordDecl>(FD->getLexicalDeclContext())) {
+ Diag(FD->getLocation(), diag::err_defaulted_comparison_out_of_class)
+ << (int)DefKind.asComparison();
+ return;
+ }
- // If this definition appears within the record, do the checking when
- // the record is complete.
- const FunctionDecl *Primary = MD;
- if (const FunctionDecl *Pattern = MD->getTemplateInstantiationPattern())
- // Ask the template instantiation pattern that actually had the
- // '= default' on it.
- Primary = Pattern;
+ // Issue compatibility warning. We already warned if the operator is
+ // 'operator<=>' when parsing the '<=>' token.
+ if (DefKind.isComparison() &&
+ DefKind.asComparison() != DefaultedComparisonKind::ThreeWay) {
+ Diag(DefaultLoc, getLangOpts().CPlusPlus2a
+ ? diag::warn_cxx17_compat_defaulted_comparison
+ : diag::ext_defaulted_comparison);
+ }
- // If the method was defaulted on its first declaration, we will have
- // already performed the checking in CheckCompletedCXXClass. Such a
- // declaration doesn't trigger an implicit definition.
- if (Primary->getCanonicalDecl()->isDefaulted())
- return;
+ FD->setDefaulted();
+ FD->setExplicitlyDefaulted();
- CheckExplicitlyDefaultedSpecialMember(MD);
+ // Defer checking functions that are defaulted in a dependent context.
+ if (FD->isDependentContext())
+ return;
- if (!MD->isInvalidDecl())
- DefineImplicitSpecialMember(*this, MD, DefaultLoc);
- } else {
- Diag(DefaultLoc, diag::err_default_special_members);
- }
+ // Unset that we will have a body for this function. We might not,
+ // if it turns out to be trivial, and we don't need this marking now
+ // that we've marked it as defaulted.
+ FD->setWillHaveBody(false);
+
+ // If this definition appears within the record, do the checking when
+ // the record is complete. This is always the case for a defaulted
+ // comparison.
+ if (DefKind.isComparison())
+ return;
+ auto *MD = cast<CXXMethodDecl>(FD);
+
+ const FunctionDecl *Primary = FD;
+ if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern())
+ // Ask the template instantiation pattern that actually had the
+ // '= default' on it.
+ Primary = Pattern;
+
+ // If the method was defaulted on its first declaration, we will have
+ // already performed the checking in CheckCompletedCXXClass. Such a
+ // declaration doesn't trigger an implicit definition.
+ if (Primary->getCanonicalDecl()->isDefaulted())
+ return;
+
+ if (CheckExplicitlyDefaultedSpecialMember(MD, DefKind.asSpecialMember()))
+ MD->setInvalidDecl();
+ else
+ DefineImplicitSpecialMember(*this, MD, DefaultLoc);
}
static void SearchForReturnInStmt(Sema &Self, Stmt *S) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index d1ad304e62e..31a4302ba82 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2049,6 +2049,11 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
}
}
+ if (D->isExplicitlyDefaulted())
+ SemaRef.SetDeclDefaulted(Function, D->getLocation());
+ if (D->isDeleted())
+ SemaRef.SetDeclDeleted(Function, D->getLocation());
+
if (Function->isLocalExternDecl() && !Function->getPreviousDecl())
DC->makeDeclVisibleInContext(PrincipalDecl);
@@ -2056,7 +2061,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
PrincipalDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
PrincipalDecl->setNonMemberOperator();
- assert(!D->isDefaulted() && "only methods should be defaulted");
return Function;
}
@@ -4016,9 +4020,6 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
bool
TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
FunctionDecl *Tmpl) {
- if (Tmpl->isDeleted())
- New->setDeletedAsWritten();
-
New->setImplicit(Tmpl->isImplicit());
// Forward the mangling number from the template to the instantiated decl.
OpenPOWER on IntegriCloud