diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 140 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 7 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 87 |
4 files changed, 199 insertions, 39 deletions
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7bbd0114b1e..72ac81776aa 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6481,40 +6481,38 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { bool HasMethodWithOverrideControl = false, HasOverridingMethodWithoutOverrideControl = false; - for (auto *M : Record->methods()) { - // FIXME: We could do this check for dependent types with non-dependent - // bases. - if (!Record->isDependentType()) { - // See if a method overloads virtual methods in a base - // class without overriding any. - if (!M->isStatic()) - DiagnoseHiddenVirtualMethods(M); - if (M->hasAttr<OverrideAttr>()) - HasMethodWithOverrideControl = true; - else if (M->size_overridden_methods() > 0) - HasOverridingMethodWithoutOverrideControl = true; - } + for (auto *D : Record->decls()) { + if (auto *M = dyn_cast<CXXMethodDecl>(D)) { + // FIXME: We could do this check for dependent types with non-dependent + // bases. + if (!Record->isDependentType()) { + // See if a method overloads virtual methods in a base + // class without overriding any. + if (!M->isStatic()) + DiagnoseHiddenVirtualMethods(M); + if (M->hasAttr<OverrideAttr>()) + HasMethodWithOverrideControl = true; + else if (M->size_overridden_methods() > 0) + HasOverridingMethodWithoutOverrideControl = true; + } - if (!isa<CXXDestructorDecl>(M)) - CompleteMemberFunction(M); + if (!isa<CXXDestructorDecl>(M)) + CompleteMemberFunction(M); + } else if (auto *F = dyn_cast<FriendDecl>(D)) { + CheckForDefaultedFunction( + dyn_cast_or_null<FunctionDecl>(F->getFriendDecl())); + } } if (HasMethodWithOverrideControl && HasOverridingMethodWithoutOverrideControl) { // At least one method has the 'override' control declared. - // Diagnose all other overridden methods which do not have 'override' specified on them. + // Diagnose all other overridden methods which do not have 'override' + // specified on them. for (auto *M : Record->methods()) DiagnoseAbsenceOfOverrideControl(M); } - // Process any defaulted friends in the member-specification. - if (!Record->isDependentType()) { - for (FriendDecl *D : Record->friends()) { - CheckForDefaultedFunction( - dyn_cast_or_null<FunctionDecl>(D->getFriendDecl())); - } - } - // Check the defaulted secondary comparisons after any other member functions. for (FunctionDecl *FD : DefaultedSecondaryComparisons) CheckExplicitlyDefaultedFunction(S, FD); @@ -7868,7 +7866,9 @@ static void lookupOperatorsForDefaultedComparison(Sema &Self, Scope *S, Lookup(ExtraOp); // For 'operator<=>', we also form a 'cmp != 0' expression, and might - // synthesize a three-way comparison from '<' and '=='. + // synthesize a three-way comparison from '<' and '=='. In a dependent + // context, we also need to look up '==' in case we implicitly declare a + // defaulted 'operator=='. if (Op == OO_Spaceship) { Lookup(OO_ExclaimEqual); Lookup(OO_Less); @@ -7904,9 +7904,13 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, for (const ParmVarDecl *Param : FD->parameters()) { if (!Param->getType()->isDependentType() && !Context.hasSameType(Param->getType(), ExpectedParmType)) { - Diag(FD->getLocation(), diag::err_defaulted_comparison_param) - << (int)DCK << Param->getType() << ExpectedParmType - << Param->getSourceRange(); + // Don't diagnose an implicit 'operator=='; we will have diagnosed the + // corresponding defaulted 'operator<=>' already. + if (!FD->isImplicit()) { + Diag(FD->getLocation(), diag::err_defaulted_comparison_param) + << (int)DCK << Param->getType() << ExpectedParmType + << Param->getSourceRange(); + } return true; } } @@ -7918,8 +7922,12 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, 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"); + // Don't diagnose an implicit 'operator=='; we will have diagnosed the + // corresponding defaulted 'operator<=>' already. + if (!MD->isImplicit()) { + 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>(); @@ -7980,7 +7988,7 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, // This is really just a consequence of the general rule that you can // only delete a function on its first declaration. Diag(FD->getLocation(), diag::err_non_first_default_compare_deletes) - << (int)DCK; + << FD->isImplicit() << (int)DCK; DefaultedComparisonAnalyzer(*this, RD, FD, DCK, DefaultedComparisonAnalyzer::ExplainDeleted) .visit(); @@ -7988,7 +7996,7 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, } SetDeclDeleted(FD, FD->getLocation()); - if (!inTemplateInstantiation()) { + if (!inTemplateInstantiation() && !FD->isImplicit()) { Diag(FD->getLocation(), diag::warn_defaulted_comparison_deleted) << (int)DCK; DefaultedComparisonAnalyzer(*this, RD, FD, DCK, @@ -8028,7 +8036,7 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, !Info.Constexpr) { Diag(FD->getBeginLoc(), diag::err_incorrect_defaulted_comparison_constexpr) - << (int)DCK << FD->isConsteval(); + << FD->isImplicit() << (int)DCK << FD->isConsteval(); DefaultedComparisonAnalyzer(*this, RD, FD, DCK, DefaultedComparisonAnalyzer::ExplainConstexpr) .visit(); @@ -8051,6 +8059,20 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, return false; } +void Sema::DeclareImplicitEqualityComparison(CXXRecordDecl *RD, + FunctionDecl *Spaceship) { + Sema::CodeSynthesisContext Ctx; + Ctx.Kind = Sema::CodeSynthesisContext::DeclaringImplicitEqualityComparison; + Ctx.PointOfInstantiation = Spaceship->getEndLoc(); + Ctx.Entity = Spaceship; + pushCodeSynthesisContext(Ctx); + + if (FunctionDecl *EqualEqual = SubstSpaceshipAsEqualEqual(RD, Spaceship)) + EqualEqual->setImplicit(); + + popCodeSynthesisContext(); +} + void Sema::DefineDefaultedComparison(SourceLocation UseLoc, FunctionDecl *FD, DefaultedComparisonKind DCK) { assert(FD->isDefaulted() && !FD->isDeleted() && @@ -9330,6 +9352,44 @@ void Sema::ActOnFinishCXXMemberSpecification( CheckCompletedCXXClass(S, cast<CXXRecordDecl>(TagDecl)); } +/// Find the equality comparison functions that should be implicitly declared +/// in a given class definition, per C++2a [class.compare.default]p3. +static void findImplicitlyDeclaredEqualityComparisons( + ASTContext &Ctx, CXXRecordDecl *RD, + llvm::SmallVectorImpl<FunctionDecl *> &Spaceships) { + DeclarationName EqEq = Ctx.DeclarationNames.getCXXOperatorName(OO_EqualEqual); + if (!RD->lookup(EqEq).empty()) + // Member operator== explicitly declared: no implicit operator==s. + return; + + // Traverse friends looking for an '==' or a '<=>'. + for (FriendDecl *Friend : RD->friends()) { + FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Friend->getFriendDecl()); + if (!FD) continue; + + if (FD->getOverloadedOperator() == OO_EqualEqual) { + // Friend operator== explicitly declared: no implicit operator==s. + Spaceships.clear(); + return; + } + + if (FD->getOverloadedOperator() == OO_Spaceship && + FD->isExplicitlyDefaulted()) + Spaceships.push_back(FD); + } + + // Look for members named 'operator<=>'. + DeclarationName Cmp = Ctx.DeclarationNames.getCXXOperatorName(OO_Spaceship); + for (NamedDecl *ND : RD->lookup(Cmp)) { + // Note that we could find a non-function here (either a function template + // or a using-declaration). Neither case results in an implicit + // 'operator=='. + if (auto *FD = dyn_cast<FunctionDecl>(ND)) + if (FD->isExplicitlyDefaulted()) + Spaceships.push_back(FD); + } +} + /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared /// special functions, such as the default constructor, copy /// constructor, or destructor, to the given C++ class (C++ @@ -9407,6 +9467,20 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { ClassDecl->needsOverloadResolutionForDestructor()) DeclareImplicitDestructor(ClassDecl); } + + // C++2a [class.compare.default]p3: + // If the member-specification does not explicitly declare any member or + // friend named operator==, an == operator function is declared implicitly + // for each defaulted three-way comparison operator function defined in the + // member-specification + // FIXME: Consider doing this lazily. + if (getLangOpts().CPlusPlus2a) { + llvm::SmallVector<FunctionDecl*, 4> DefaultedSpaceships; + findImplicitlyDeclaredEqualityComparisons(Context, ClassDecl, + DefaultedSpaceships); + for (auto *FD : DefaultedSpaceships) + DeclareImplicitEqualityComparison(ClassDecl, FD); + } } unsigned Sema::ActOnReenterTemplateScope(Scope *S, Decl *D) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 344e54b7f3f..5b1394d7b34 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9723,6 +9723,7 @@ enum OverloadCandidateKind { oc_implicit_move_constructor, oc_implicit_copy_assignment, oc_implicit_move_assignment, + oc_implicit_equality_comparison, oc_inherited_constructor }; @@ -9751,6 +9752,9 @@ ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn, }(); OverloadCandidateKind Kind = [&]() { + if (Fn->isImplicit() && Fn->getOverloadedOperator() == OO_EqualEqual) + return oc_implicit_equality_comparison; + if (CRK & CRK_Reversed) return oc_reversed_binary_operator; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 1451fe4bb4d..6db8eb3e5ed 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -203,6 +203,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case DefaultTemplateArgumentChecking: case DeclaringSpecialMember: + case DeclaringImplicitEqualityComparison: case DefiningSynthesizedFunction: case ExceptionSpecEvaluation: case ConstraintSubstitution: @@ -671,6 +672,11 @@ void Sema::PrintInstantiationStack() { << cast<CXXRecordDecl>(Active->Entity) << Active->SpecialMember; break; + case CodeSynthesisContext::DeclaringImplicitEqualityComparison: + Diags.Report(Active->Entity->getLocation(), + diag::note_in_declaration_of_implicit_equality_comparison); + break; + case CodeSynthesisContext::DefiningSynthesizedFunction: { // FIXME: For synthesized functions that are not defaulted, // produce a note. @@ -772,6 +778,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { return Active->DeductionInfo; case CodeSynthesisContext::DeclaringSpecialMember: + case CodeSynthesisContext::DeclaringImplicitEqualityComparison: case CodeSynthesisContext::DefiningSynthesizedFunction: case CodeSynthesisContext::RewritingOperatorAsSpaceship: // This happens in a context unrelated to template instantiation, so diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 71399ff3590..0bff0747df3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1794,8 +1794,9 @@ static QualType adjustFunctionTypeForInstantiation(ASTContext &Context, /// 1) instantiating function templates /// 2) substituting friend declarations /// 3) substituting deduction guide declarations for nested class templates -Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, - TemplateParameterList *TemplateParams) { +Decl *TemplateDeclInstantiator::VisitFunctionDecl( + FunctionDecl *D, TemplateParameterList *TemplateParams, + RewriteKind FunctionRewriteKind) { // Check whether there is already a function template specialization for // this declaration. FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate(); @@ -1865,6 +1866,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, DeclarationNameInfo NameInfo = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); + if (FunctionRewriteKind != RewriteKind::None) + adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo); + FunctionDecl *Function; if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D)) { Function = CXXDeductionGuideDecl::Create( @@ -2101,8 +2105,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( CXXMethodDecl *D, TemplateParameterList *TemplateParams, - Optional<const ASTTemplateArgumentListInfo *> - ClassScopeSpecializationArgs) { + Optional<const ASTTemplateArgumentListInfo *> ClassScopeSpecializationArgs, + RewriteKind FunctionRewriteKind) { FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate(); if (FunctionTemplate && !TemplateParams) { // We are creating a function template specialization from a function @@ -2181,13 +2185,17 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( if (!DC) return nullptr; } + DeclarationNameInfo NameInfo + = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); + + if (FunctionRewriteKind != RewriteKind::None) + adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo); + // Build the instantiated method declaration. CXXRecordDecl *Record = cast<CXXRecordDecl>(DC); CXXMethodDecl *Method = nullptr; SourceLocation StartLoc = D->getInnerLocStart(); - DeclarationNameInfo NameInfo - = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) { Method = CXXConstructorDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, @@ -3523,6 +3531,73 @@ Decl *Sema::SubstDecl(Decl *D, DeclContext *Owner, return SubstD; } +void TemplateDeclInstantiator::adjustForRewrite(RewriteKind RK, + FunctionDecl *Orig, QualType &T, + TypeSourceInfo *&TInfo, + DeclarationNameInfo &NameInfo) { + assert(RK == RewriteKind::RewriteSpaceshipAsEqualEqual); + + // C++2a [class.compare.default]p3: + // the return type is replaced with bool + auto *FPT = T->castAs<FunctionProtoType>(); + T = SemaRef.Context.getFunctionType( + SemaRef.Context.BoolTy, FPT->getParamTypes(), FPT->getExtProtoInfo()); + + // Update the return type in the source info too. The most straightforward + // way is to create new TypeSourceInfo for the new type. Use the location of + // the '= default' as the location of the new type. + // + // FIXME: Set the correct return type when we initially transform the type, + // rather than delaying it to now. + TypeSourceInfo *NewTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(T, Orig->getEndLoc()); + auto OldLoc = TInfo->getTypeLoc().getAsAdjusted<FunctionProtoTypeLoc>(); + assert(OldLoc && "type of function is not a function type?"); + auto NewLoc = NewTInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>(); + for (unsigned I = 0, N = OldLoc.getNumParams(); I != N; ++I) + NewLoc.setParam(I, OldLoc.getParam(I)); + TInfo = NewTInfo; + + // and the declarator-id is replaced with operator== + NameInfo.setName( + SemaRef.Context.DeclarationNames.getCXXOperatorName(OO_EqualEqual)); +} + +FunctionDecl *Sema::SubstSpaceshipAsEqualEqual(CXXRecordDecl *RD, + FunctionDecl *Spaceship) { + if (Spaceship->isInvalidDecl()) + return nullptr; + + // C++2a [class.compare.default]p3: + // an == operator function is declared implicitly [...] with the same + // access and function-definition and in the same class scope as the + // three-way comparison operator function + MultiLevelTemplateArgumentList NoTemplateArgs; + TemplateDeclInstantiator Instantiator(*this, RD, NoTemplateArgs); + Decl *R; + if (auto *MD = dyn_cast<CXXMethodDecl>(Spaceship)) { + R = Instantiator.VisitCXXMethodDecl( + MD, nullptr, None, + TemplateDeclInstantiator::RewriteKind::RewriteSpaceshipAsEqualEqual); + } else { + assert(Spaceship->getFriendObjectKind() && + "defaulted spaceship is neither a member nor a friend"); + + R = Instantiator.VisitFunctionDecl( + Spaceship, nullptr, + TemplateDeclInstantiator::RewriteKind::RewriteSpaceshipAsEqualEqual); + if (!R) + return nullptr; + + FriendDecl *FD = + FriendDecl::Create(Context, RD, Spaceship->getLocation(), + cast<NamedDecl>(R), Spaceship->getBeginLoc()); + FD->setAccess(AS_public); + RD->addDecl(FD); + } + return cast_or_null<FunctionDecl>(R); +} + /// Instantiates a nested template parameter list in the current /// instantiation context. /// |