diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 22 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 285 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 9 |
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. |