diff options
Diffstat (limited to 'clang/lib/Sema/SemaDecl.cpp')
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 477 |
1 files changed, 303 insertions, 174 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4b9b735f1cfb..029ccf944c51 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1493,7 +1493,7 @@ void Sema::ActOnExitFunctionContext() { /// /// This routine determines whether overloading is possible, not /// whether a new declaration actually overloads a previous one. -/// It will return true in C++ (where overloads are alway permitted) +/// It will return true in C++ (where overloads are always permitted) /// or, as a C extension, when either the new declaration or a /// previous one is declared with the 'overloadable' attribute. static bool AllowOverloadingOfFunction(const LookupResult &Previous, @@ -1663,8 +1663,7 @@ bool Sema::CheckRedeclarationModuleOwnership(NamedDecl *New, NamedDecl *Old) { // Partitions are part of the module, but a partition could import another // module, so verify that the PMIs agree. if ((NewM->isModulePartition() || OldM->isModulePartition()) && - NewM->getPrimaryModuleInterfaceName() == - OldM->getPrimaryModuleInterfaceName()) + getASTContext().isInSameModule(NewM, OldM)) return false; } @@ -3912,6 +3911,49 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, return true; } + const auto OldFX = Old->getFunctionEffects(); + const auto NewFX = New->getFunctionEffects(); + QualType OldQTypeForComparison = OldQType; + if (OldFX != NewFX) { + const auto Diffs = FunctionEffectDifferences(OldFX, NewFX); + for (const auto &Diff : Diffs) { + if (Diff.shouldDiagnoseRedeclaration(*Old, OldFX, *New, NewFX)) { + Diag(New->getLocation(), + diag::warn_mismatched_func_effect_redeclaration) + << Diff.effectName(); + Diag(Old->getLocation(), diag::note_previous_declaration); + } + } + // Following a warning, we could skip merging effects from the previous + // declaration, but that would trigger an additional "conflicting types" + // error. + if (const auto *NewFPT = NewQType->getAs<FunctionProtoType>()) { + FunctionEffectSet::Conflicts MergeErrs; + FunctionEffectSet MergedFX = + FunctionEffectSet::getUnion(OldFX, NewFX, MergeErrs); + if (!MergeErrs.empty()) + diagnoseFunctionEffectMergeConflicts(MergeErrs, New->getLocation(), + Old->getLocation()); + + FunctionProtoType::ExtProtoInfo EPI = NewFPT->getExtProtoInfo(); + EPI.FunctionEffects = FunctionEffectsRef(MergedFX); + QualType ModQT = Context.getFunctionType(NewFPT->getReturnType(), + NewFPT->getParamTypes(), EPI); + + New->setType(ModQT); + NewQType = New->getType(); + + // Revise OldQTForComparison to include the merged effects, + // so as not to fail due to differences later. + if (const auto *OldFPT = OldQType->getAs<FunctionProtoType>()) { + EPI = OldFPT->getExtProtoInfo(); + EPI.FunctionEffects = FunctionEffectsRef(MergedFX); + OldQTypeForComparison = Context.getFunctionType( + OldFPT->getReturnType(), OldFPT->getParamTypes(), EPI); + } + } + } + if (getLangOpts().CPlusPlus) { OldQType = Context.getCanonicalType(Old->getType()); NewQType = Context.getCanonicalType(New->getType()); @@ -4076,9 +4118,8 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, // We also want to respect all the extended bits except noreturn. // noreturn should now match unless the old type info didn't have it. - QualType OldQTypeForComparison = OldQType; if (!OldTypeInfo.getNoReturn() && NewTypeInfo.getNoReturn()) { - auto *OldType = OldQType->castAs<FunctionProtoType>(); + auto *OldType = OldQTypeForComparison->castAs<FunctionProtoType>(); const FunctionType *OldTypeForComparison = Context.adjustFunctionType(OldType, OldTypeInfo.withNoReturn(true)); OldQTypeForComparison = QualType(OldTypeForComparison, 0); @@ -4147,7 +4188,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, // If we are merging two functions where only one of them has a prototype, // we may have enough information to decide to issue a diagnostic that the - // function without a protoype will change behavior in C23. This handles + // function without a prototype will change behavior in C23. This handles // cases like: // void i(); void i(int j); // void i(int j); void i(); @@ -7609,80 +7650,8 @@ NamedDecl *Sema::ActOnVariableDeclarator( NTCUC_AutoVar, NTCUK_Destruct); } else { bool Invalid = false; - - if (DC->isRecord() && !CurContext->isRecord()) { - // This is an out-of-line definition of a static data member. - switch (SC) { - case SC_None: - break; - case SC_Static: - Diag(D.getDeclSpec().getStorageClassSpecLoc(), - diag::err_static_out_of_line) - << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); - break; - case SC_Auto: - case SC_Register: - case SC_Extern: - // [dcl.stc] p2: The auto or register specifiers shall be applied only - // to names of variables declared in a block or to function parameters. - // [dcl.stc] p6: The extern specifier cannot be used in the declaration - // of class members - - Diag(D.getDeclSpec().getStorageClassSpecLoc(), - diag::err_storage_class_for_static_member) - << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); - break; - case SC_PrivateExtern: - llvm_unreachable("C storage class in c++!"); - } - } - - if (SC == SC_Static && CurContext->isRecord()) { - if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) { - // Walk up the enclosing DeclContexts to check for any that are - // incompatible with static data members. - const DeclContext *FunctionOrMethod = nullptr; - const CXXRecordDecl *AnonStruct = nullptr; - for (DeclContext *Ctxt = DC; Ctxt; Ctxt = Ctxt->getParent()) { - if (Ctxt->isFunctionOrMethod()) { - FunctionOrMethod = Ctxt; - break; - } - const CXXRecordDecl *ParentDecl = dyn_cast<CXXRecordDecl>(Ctxt); - if (ParentDecl && !ParentDecl->getDeclName()) { - AnonStruct = ParentDecl; - break; - } - } - if (FunctionOrMethod) { - // C++ [class.static.data]p5: A local class shall not have static data - // members. - Diag(D.getIdentifierLoc(), - diag::err_static_data_member_not_allowed_in_local_class) - << Name << RD->getDeclName() - << llvm::to_underlying(RD->getTagKind()); - } else if (AnonStruct) { - // C++ [class.static.data]p4: Unnamed classes and classes contained - // directly or indirectly within unnamed classes shall not contain - // static data members. - Diag(D.getIdentifierLoc(), - diag::err_static_data_member_not_allowed_in_anon_struct) - << Name << llvm::to_underlying(AnonStruct->getTagKind()); - Invalid = true; - } else if (RD->isUnion()) { - // C++98 [class.union]p1: If a union contains a static data member, - // the program is ill-formed. C++11 drops this restriction. - Diag(D.getIdentifierLoc(), - getLangOpts().CPlusPlus11 - ? diag::warn_cxx98_compat_static_data_member_in_union - : diag::ext_static_data_member_in_union) << Name; - } - } - } - // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. - bool InvalidScope = false; TemplateParams = MatchTemplateParametersToScopeSpecifier( D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), D.getCXXScopeSpec(), @@ -7690,8 +7659,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( ? D.getName().TemplateId : nullptr, TemplateParamLists, - /*never a friend*/ false, IsMemberSpecialization, InvalidScope); - Invalid |= InvalidScope; + /*never a friend*/ false, IsMemberSpecialization, Invalid); if (TemplateParams) { if (!TemplateParams->size() && @@ -7734,6 +7702,102 @@ NamedDecl *Sema::ActOnVariableDeclarator( "should have a 'template<>' for this decl"); } + bool IsExplicitSpecialization = + IsVariableTemplateSpecialization && !IsPartialSpecialization; + + // C++ [temp.expl.spec]p2: + // The declaration in an explicit-specialization shall not be an + // export-declaration. An explicit specialization shall not use a + // storage-class-specifier other than thread_local. + // + // We use the storage-class-specifier from DeclSpec because we may have + // added implicit 'extern' for declarations with __declspec(dllimport)! + if (SCSpec != DeclSpec::SCS_unspecified && + (IsExplicitSpecialization || IsMemberSpecialization)) { + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + diag::ext_explicit_specialization_storage_class) + << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); + } + + if (CurContext->isRecord()) { + if (SC == SC_Static) { + if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) { + // Walk up the enclosing DeclContexts to check for any that are + // incompatible with static data members. + const DeclContext *FunctionOrMethod = nullptr; + const CXXRecordDecl *AnonStruct = nullptr; + for (DeclContext *Ctxt = DC; Ctxt; Ctxt = Ctxt->getParent()) { + if (Ctxt->isFunctionOrMethod()) { + FunctionOrMethod = Ctxt; + break; + } + const CXXRecordDecl *ParentDecl = dyn_cast<CXXRecordDecl>(Ctxt); + if (ParentDecl && !ParentDecl->getDeclName()) { + AnonStruct = ParentDecl; + break; + } + } + if (FunctionOrMethod) { + // C++ [class.static.data]p5: A local class shall not have static + // data members. + Diag(D.getIdentifierLoc(), + diag::err_static_data_member_not_allowed_in_local_class) + << Name << RD->getDeclName() + << llvm::to_underlying(RD->getTagKind()); + } else if (AnonStruct) { + // C++ [class.static.data]p4: Unnamed classes and classes contained + // directly or indirectly within unnamed classes shall not contain + // static data members. + Diag(D.getIdentifierLoc(), + diag::err_static_data_member_not_allowed_in_anon_struct) + << Name << llvm::to_underlying(AnonStruct->getTagKind()); + Invalid = true; + } else if (RD->isUnion()) { + // C++98 [class.union]p1: If a union contains a static data member, + // the program is ill-formed. C++11 drops this restriction. + Diag(D.getIdentifierLoc(), + getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_static_data_member_in_union + : diag::ext_static_data_member_in_union) + << Name; + } + } + } else if (IsVariableTemplate || IsPartialSpecialization) { + // There is no such thing as a member field template. + Diag(D.getIdentifierLoc(), diag::err_template_member) + << II << TemplateParams->getSourceRange(); + // Recover by pretending this is a static data member template. + SC = SC_Static; + } + } else if (DC->isRecord()) { + // This is an out-of-line definition of a static data member. + switch (SC) { + case SC_None: + break; + case SC_Static: + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + diag::err_static_out_of_line) + << FixItHint::CreateRemoval( + D.getDeclSpec().getStorageClassSpecLoc()); + break; + case SC_Auto: + case SC_Register: + case SC_Extern: + // [dcl.stc] p2: The auto or register specifiers shall be applied only + // to names of variables declared in a block or to function parameters. + // [dcl.stc] p6: The extern specifier cannot be used in the declaration + // of class members + + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + diag::err_storage_class_for_static_member) + << FixItHint::CreateRemoval( + D.getDeclSpec().getStorageClassSpecLoc()); + break; + case SC_PrivateExtern: + llvm_unreachable("C storage class in c++!"); + } + } + if (IsVariableTemplateSpecialization) { SourceLocation TemplateKWLoc = TemplateParamLists.size() > 0 @@ -7779,8 +7843,6 @@ NamedDecl *Sema::ActOnVariableDeclarator( // the variable (matching the scope specifier), store them. // An explicit variable template specialization does not own any template // parameter lists. - bool IsExplicitSpecialization = - IsVariableTemplateSpecialization && !IsPartialSpecialization; unsigned VDTemplateParamLists = (TemplateParams && !IsExplicitSpecialization) ? 1 : 0; if (TemplateParamLists.size() > VDTemplateParamLists) @@ -10210,25 +10272,45 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD->setImplicitlyInline(ImplicitInlineCXX20); } - if (SC == SC_Static && isa<CXXMethodDecl>(NewFD) && - !CurContext->isRecord()) { - // C++ [class.static]p1: - // A data or function member of a class may be declared static - // in a class definition, in which case it is a static member of - // the class. + if (!isFriend && SC != SC_None) { + // C++ [temp.expl.spec]p2: + // The declaration in an explicit-specialization shall not be an + // export-declaration. An explicit specialization shall not use a + // storage-class-specifier other than thread_local. + // + // We diagnose friend declarations with storage-class-specifiers + // elsewhere. + if (isFunctionTemplateSpecialization || isMemberSpecialization) { + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + diag::ext_explicit_specialization_storage_class) + << FixItHint::CreateRemoval( + D.getDeclSpec().getStorageClassSpecLoc()); + } - // Complain about the 'static' specifier if it's on an out-of-line - // member function definition. + if (SC == SC_Static && !CurContext->isRecord() && DC->isRecord()) { + assert(isa<CXXMethodDecl>(NewFD) && + "Out-of-line member function should be a CXXMethodDecl"); + // C++ [class.static]p1: + // A data or function member of a class may be declared static + // in a class definition, in which case it is a static member of + // the class. - // MSVC permits the use of a 'static' storage specifier on an out-of-line - // member function template declaration and class member template - // declaration (MSVC versions before 2015), warn about this. - Diag(D.getDeclSpec().getStorageClassSpecLoc(), - ((!getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && - cast<CXXRecordDecl>(DC)->getDescribedClassTemplate()) || - (getLangOpts().MSVCCompat && NewFD->getDescribedFunctionTemplate())) - ? diag::ext_static_out_of_line : diag::err_static_out_of_line) - << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); + // Complain about the 'static' specifier if it's on an out-of-line + // member function definition. + + // MSVC permits the use of a 'static' storage specifier on an + // out-of-line member function template declaration and class member + // template declaration (MSVC versions before 2015), warn about this. + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + ((!getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && + cast<CXXRecordDecl>(DC)->getDescribedClassTemplate()) || + (getLangOpts().MSVCCompat && + NewFD->getDescribedFunctionTemplate())) + ? diag::ext_static_out_of_line + : diag::err_static_out_of_line) + << FixItHint::CreateRemoval( + D.getDeclSpec().getStorageClassSpecLoc()); + } } // C++11 [except.spec]p15: @@ -10553,7 +10635,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (getLangOpts().CUDA && !isFunctionTemplateSpecialization) CUDA().maybeAddHostDeviceAttrs(NewFD, Previous); - // Handle explict specializations of function templates + // Handle explicit specializations of function templates // and friend function declarations with an explicit // template argument list. if (isFunctionTemplateSpecialization) { @@ -10596,27 +10678,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, Previous)) NewFD->setInvalidDecl(); } - - // C++ [dcl.stc]p1: - // A storage-class-specifier shall not be specified in an explicit - // specialization (14.7.3) - // FIXME: We should be checking this for dependent specializations. - FunctionTemplateSpecializationInfo *Info = - NewFD->getTemplateSpecializationInfo(); - if (Info && SC != SC_None) { - if (SC != Info->getTemplate()->getTemplatedDecl()->getStorageClass()) - Diag(NewFD->getLocation(), - diag::err_explicit_specialization_inconsistent_storage_class) - << SC - << FixItHint::CreateRemoval( - D.getDeclSpec().getStorageClassSpecLoc()); - - else - Diag(NewFD->getLocation(), - diag::ext_explicit_specialization_storage_class) - << FixItHint::CreateRemoval( - D.getDeclSpec().getStorageClassSpecLoc()); - } } else if (isMemberSpecialization && isa<CXXMethodDecl>(NewFD)) { if (CheckMemberSpecialization(NewFD, Previous)) NewFD->setInvalidDecl(); @@ -12601,7 +12662,7 @@ void Sema::CheckMSVCRTEntryPoint(FunctionDecl *FD) { if (FD->getName() != "DllMain") FD->setHasImplicitReturnZero(true); - // Explicity specified calling conventions are applied to MSVC entry points + // Explicitly specified calling conventions are applied to MSVC entry points if (!hasExplicitCallingConv(T)) { if (isDefaultStdCall(FD, *this)) { if (FT->getCallConv() != CC_X86StdCall) { @@ -13674,12 +13735,12 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { CreateRecoveryExpr(Init->getBeginLoc(), Init->getEndLoc(), Args); if (RecoveryExpr.get()) VDecl->setInit(RecoveryExpr.get()); - // In general, for error recovery purposes, the initalizer doesn't play + // In general, for error recovery purposes, the initializer doesn't play // part in the valid bit of the declaration. There are a few exceptions: // 1) if the var decl has a deduced auto type, and the type cannot be // deduced by an invalid initializer; - // 2) if the var decl is decompsition decl with a non-deduced type, and - // the initialization fails (e.g. `int [a] = {1, 2};`); + // 2) if the var decl is a decomposition decl with a non-deduced type, + // and the initialization fails (e.g. `int [a] = {1, 2};`); // Case 1) was already handled elsewhere. if (isa<DecompositionDecl>(VDecl)) // Case 2) VDecl->setInvalidDecl(); @@ -13897,9 +13958,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { } } else if (VDecl->isFileVarDecl()) { // In C, extern is typically used to avoid tentative definitions when - // declaring variables in headers, but adding an intializer makes it a + // declaring variables in headers, but adding an initializer makes it a // definition. This is somewhat confusing, so GCC and Clang both warn on it. - // In C++, extern is often used to give implictly static const variables + // In C++, extern is often used to give implicitly static const variables // external linkage, so don't warn in that case. If selectany is present, // this might be header code intended for C and C++ inclusion, so apply the // C++ rules. @@ -14093,7 +14154,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { return; } } - // The declaration is unitialized, no need for further checks. + // The declaration is uninitialized, no need for further checks. return; } @@ -14911,53 +14972,53 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, DeclaratorDecl *FirstNonDeducedAutoInGroup = nullptr; bool DiagnosedNonDeducedAuto = false; - for (unsigned i = 0, e = Group.size(); i != e; ++i) { - if (Decl *D = Group[i]) { - // Check if the Decl has been declared in '#pragma omp declare target' - // directive and has static storage duration. - if (auto *VD = dyn_cast<VarDecl>(D); - LangOpts.OpenMP && VD && VD->hasAttr<OMPDeclareTargetDeclAttr>() && - VD->hasGlobalStorage()) - OpenMP().ActOnOpenMPDeclareTargetInitializer(D); - // For declarators, there are some additional syntactic-ish checks we need - // to perform. - if (auto *DD = dyn_cast<DeclaratorDecl>(D)) { - if (!FirstDeclaratorInGroup) - FirstDeclaratorInGroup = DD; - if (!FirstDecompDeclaratorInGroup) - FirstDecompDeclaratorInGroup = dyn_cast<DecompositionDecl>(D); - if (!FirstNonDeducedAutoInGroup && DS.hasAutoTypeSpec() && - !hasDeducedAuto(DD)) - FirstNonDeducedAutoInGroup = DD; - - if (FirstDeclaratorInGroup != DD) { - // A decomposition declaration cannot be combined with any other - // declaration in the same group. - if (FirstDecompDeclaratorInGroup && !DiagnosedMultipleDecomps) { - Diag(FirstDecompDeclaratorInGroup->getLocation(), - diag::err_decomp_decl_not_alone) - << FirstDeclaratorInGroup->getSourceRange() - << DD->getSourceRange(); - DiagnosedMultipleDecomps = true; - } + for (Decl *D : Group) { + if (!D) + continue; + // Check if the Decl has been declared in '#pragma omp declare target' + // directive and has static storage duration. + if (auto *VD = dyn_cast<VarDecl>(D); + LangOpts.OpenMP && VD && VD->hasAttr<OMPDeclareTargetDeclAttr>() && + VD->hasGlobalStorage()) + OpenMP().ActOnOpenMPDeclareTargetInitializer(D); + // For declarators, there are some additional syntactic-ish checks we need + // to perform. + if (auto *DD = dyn_cast<DeclaratorDecl>(D)) { + if (!FirstDeclaratorInGroup) + FirstDeclaratorInGroup = DD; + if (!FirstDecompDeclaratorInGroup) + FirstDecompDeclaratorInGroup = dyn_cast<DecompositionDecl>(D); + if (!FirstNonDeducedAutoInGroup && DS.hasAutoTypeSpec() && + !hasDeducedAuto(DD)) + FirstNonDeducedAutoInGroup = DD; + + if (FirstDeclaratorInGroup != DD) { + // A decomposition declaration cannot be combined with any other + // declaration in the same group. + if (FirstDecompDeclaratorInGroup && !DiagnosedMultipleDecomps) { + Diag(FirstDecompDeclaratorInGroup->getLocation(), + diag::err_decomp_decl_not_alone) + << FirstDeclaratorInGroup->getSourceRange() + << DD->getSourceRange(); + DiagnosedMultipleDecomps = true; + } - // A declarator that uses 'auto' in any way other than to declare a - // variable with a deduced type cannot be combined with any other - // declarator in the same group. - if (FirstNonDeducedAutoInGroup && !DiagnosedNonDeducedAuto) { - Diag(FirstNonDeducedAutoInGroup->getLocation(), - diag::err_auto_non_deduced_not_alone) - << FirstNonDeducedAutoInGroup->getType() - ->hasAutoForTrailingReturnType() - << FirstDeclaratorInGroup->getSourceRange() - << DD->getSourceRange(); - DiagnosedNonDeducedAuto = true; - } + // A declarator that uses 'auto' in any way other than to declare a + // variable with a deduced type cannot be combined with any other + // declarator in the same group. + if (FirstNonDeducedAutoInGroup && !DiagnosedNonDeducedAuto) { + Diag(FirstNonDeducedAutoInGroup->getLocation(), + diag::err_auto_non_deduced_not_alone) + << FirstNonDeducedAutoInGroup->getType() + ->hasAutoForTrailingReturnType() + << FirstDeclaratorInGroup->getSourceRange() + << DD->getSourceRange(); + DiagnosedNonDeducedAuto = true; } } - - Decls.push_back(D); } + + Decls.push_back(D); } if (DeclSpec::isDeclRep(DS.getTypeSpecType())) { @@ -16324,7 +16385,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, FSI->ObjCWarnForNoDesignatedInitChain = false; } if (FSI->ObjCWarnForNoInitDelegation) { - // Don't issue this warning for unavaialable inits. + // Don't issue this warning for unavailable inits. if (!MD->isUnavailable()) Diag(MD->getLocation(), diag::warn_objc_secondary_init_missing_init_call); @@ -17876,7 +17937,7 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, SkipBody->Previous = Def; makeMergedDefinitionVisible(Hidden); // Carry on and handle it like a normal definition. We'll - // skip starting the definitiion later. + // skip starting the definition later. } } else if (!IsExplicitSpecializationAfterInstantiation) { // A redeclaration in function prototype scope in C isn't @@ -18333,6 +18394,15 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD, if (NumInitMethods > 1 || !Def->hasInitMethod()) Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method); } + + // If we're defining a dynamic class in a module interface unit, we always + // need to produce the vtable for it even if the vtable is not used in the + // current TU. + // + // The case that the current class is not dynamic is handled in + // MarkVTableUsed. + if (getCurrentModule() && getCurrentModule()->isInterfaceOrPartition()) + MarkVTableUsed(RD->getLocation(), RD, /*DefinitionRequired=*/true); } // Exit this scope of this tag's definition. @@ -20475,7 +20545,7 @@ Sema::FunctionEmissionStatus Sema::getEmissionStatus(const FunctionDecl *FD, } else if (LangOpts.OpenMP > 45) { // In OpenMP host compilation prior to 5.0 everything was an emitted host // function. In 5.0, no_host was introduced which might cause a function to - // be ommitted. + // be omitted. std::optional<OMPDeclareTargetDeclAttr::DevTypeTy> DevTy = OMPDeclareTargetDeclAttr::getDeviceType(FD->getCanonicalDecl()); if (DevTy) @@ -20518,3 +20588,62 @@ bool Sema::shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee) { return LangOpts.CUDA && !LangOpts.CUDAIsDevice && CUDA().IdentifyTarget(Callee) == CUDAFunctionTarget::Global; } + +// Report a failure to merge function effects between declarations due to a +// conflict. +void Sema::diagnoseFunctionEffectMergeConflicts( + const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc, + SourceLocation OldLoc) { + for (const FunctionEffectSet::Conflict &Conflict : Errs) { + Diag(NewLoc, diag::warn_conflicting_func_effects) + << Conflict.Kept.description() << Conflict.Rejected.description(); + Diag(OldLoc, diag::note_previous_declaration); + } +} + +// Warn and return true if adding an effect to a set would create a conflict. +bool Sema::diagnoseConflictingFunctionEffect( + const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC, + SourceLocation NewAttrLoc) { + // If the new effect has a condition, we can't detect conflicts until the + // condition is resolved. + if (NewEC.Cond.getCondition() != nullptr) + return false; + + // Diagnose the new attribute as incompatible with a previous one. + auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) { + Diag(NewAttrLoc, diag::err_attributes_are_not_compatible) + << ("'" + NewEC.description() + "'") + << ("'" + PrevEC.description() + "'") << false; + // We don't necessarily have the location of the previous attribute, + // so no note. + return true; + }; + + // Compare against previous attributes. + FunctionEffect::Kind NewKind = NewEC.Effect.kind(); + + for (const FunctionEffectWithCondition &PrevEC : FX) { + // Again, can't check yet when the effect is conditional. + if (PrevEC.Cond.getCondition() != nullptr) + continue; + + FunctionEffect::Kind PrevKind = PrevEC.Effect.kind(); + // Note that we allow PrevKind == NewKind; it's redundant and ignored. + + if (PrevEC.Effect.oppositeKind() == NewKind) + return Incompatible(PrevEC); + + // A new allocating is incompatible with a previous nonblocking. + if (PrevKind == FunctionEffect::Kind::NonBlocking && + NewKind == FunctionEffect::Kind::Allocating) + return Incompatible(PrevEC); + + // A new nonblocking is incompatible with a previous allocating. + if (PrevKind == FunctionEffect::Kind::Allocating && + NewKind == FunctionEffect::Kind::NonBlocking) + return Incompatible(PrevEC); + } + + return false; +} |
