summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r--clang/lib/Sema/SemaDecl.cpp22
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp285
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp9
3 files changed, 247 insertions, 69 deletions
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