diff options
| -rw-r--r-- | clang/include/clang/Sema/Sema.h | 5 | ||||
| -rw-r--r-- | clang/lib/AST/ASTDumper.cpp | 9 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 16 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 3 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 90 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 59 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaType.cpp | 18 | ||||
| -rw-r--r-- | clang/test/Modules/submodules-merge-defs.cpp | 2 |
9 files changed, 126 insertions, 78 deletions
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f48623b4edc..293b5481015 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1562,7 +1562,7 @@ public: /// visible at the specified location. void makeMergedDefinitionVisible(NamedDecl *ND); - bool isModuleVisible(const Module *M) { return VisibleModules.isVisible(M); } + bool isModuleVisible(const Module *M, bool ModulePrivate = false); /// Determine whether a declaration is visible to name lookup. bool isVisible(const NamedDecl *D) { @@ -6192,7 +6192,8 @@ public: bool CheckTemplateParameterList(TemplateParameterList *NewParams, TemplateParameterList *OldParams, - TemplateParamListContext TPC); + TemplateParamListContext TPC, + SkipBodyInfo *SkipBody = nullptr); TemplateParameterList *MatchTemplateParametersToScopeSpecifier( SourceLocation DeclStartLoc, SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId, diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index d9093e757b2..2f08f58ade6 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -1641,6 +1641,9 @@ void ASTDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { dumpName(D); if (D->hasDefaultArgument()) dumpTemplateArgument(D->getDefaultArgument()); + if (auto *From = D->getDefaultArgStorage().getInheritedFrom()) + dumpDeclRef(From, D->defaultArgumentWasInherited() ? "inherited from" + : "previous"); } void ASTDumper::VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) { @@ -1651,6 +1654,9 @@ void ASTDumper::VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) { dumpName(D); if (D->hasDefaultArgument()) dumpTemplateArgument(D->getDefaultArgument()); + if (auto *From = D->getDefaultArgStorage().getInheritedFrom()) + dumpDeclRef(From, D->defaultArgumentWasInherited() ? "inherited from" + : "previous"); } void ASTDumper::VisitTemplateTemplateParmDecl( @@ -1662,6 +1668,9 @@ void ASTDumper::VisitTemplateTemplateParmDecl( dumpTemplateParameters(D->getTemplateParameters()); if (D->hasDefaultArgument()) dumpTemplateArgumentLoc(D->getDefaultArgument()); + if (auto *From = D->getDefaultArgStorage().getInheritedFrom()) + dumpDeclRef(From, D->defaultArgumentWasInherited() ? "inherited from" + : "previous"); } void ASTDumper::VisitUsingDecl(const UsingDecl *D) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 496d06c6aca..f1e9286b7ec 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12706,6 +12706,7 @@ Sema::CheckForFunctionRedefinition(FunctionDecl *FD, Definition->getDescribedFunctionTemplate() || Definition->getNumTemplateParameterLists())) { SkipBody->ShouldSkip = true; + SkipBody->Previous = const_cast<FunctionDecl*>(Definition); if (auto *TD = Definition->getDescribedFunctionTemplate()) makeMergedDefinitionVisible(TD); makeMergedDefinitionVisible(const_cast<FunctionDecl*>(Definition)); @@ -14034,7 +14035,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // many points during the parsing of a struct declaration (because // the #pragma tokens are effectively skipped over during the // parsing of the struct). - if (TUK == TUK_Definition) { + if (TUK == TUK_Definition && (!SkipBody || !SkipBody->ShouldSkip)) { AddAlignmentAttributesForRecord(RD); AddMsStructLayoutForRecord(RD); } @@ -14465,12 +14466,15 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // comparison. SkipBody->CheckSameAsPrevious = true; SkipBody->New = createTagFromNewDecl(); - SkipBody->Previous = Hidden; + SkipBody->Previous = Def; + return Def; } else { SkipBody->ShouldSkip = true; + SkipBody->Previous = Def; makeMergedDefinitionVisible(Hidden); + // Carry on and handle it like a normal definition. We'll + // skip starting the definitiion later. } - return Def; } else if (!IsExplicitSpecializationAfterInstantiation) { // A redeclaration in function prototype scope in C isn't // visible elsewhere, so merely issue a warning. @@ -14699,7 +14703,7 @@ CreateNewDecl: // many points during the parsing of a struct declaration (because // the #pragma tokens are effectively skipped over during the // parsing of the struct). - if (TUK == TUK_Definition) { + if (TUK == TUK_Definition && (!SkipBody || !SkipBody->ShouldSkip)) { AddAlignmentAttributesForRecord(RD); AddMsStructLayoutForRecord(RD); } @@ -14761,7 +14765,7 @@ CreateNewDecl: if (PrevDecl) CheckRedeclarationModuleOwnership(New, PrevDecl); - if (TUK == TUK_Definition) + if (TUK == TUK_Definition && (!SkipBody || !SkipBody->ShouldSkip)) New->startDefinition(); ProcessDeclAttributeList(S, New, Attrs); @@ -14811,6 +14815,8 @@ CreateNewDecl: if (auto RD = dyn_cast<RecordDecl>(New)) RD->completeDefinition(); return nullptr; + } else if (SkipBody && SkipBody->ShouldSkip) { + return SkipBody->Previous; } else { return New; } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 37a5b7b7f6d..fd0f4874592 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -10496,7 +10496,8 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS, OldDecl->getTemplateParameters(), /*Complain=*/true, TPL_TemplateMatch)) - OldTemplateParams = OldDecl->getTemplateParameters(); + OldTemplateParams = + OldDecl->getMostRecentDecl()->getTemplateParameters(); else Invalid = true; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 8cf8dae886c..f7f5957f964 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1392,8 +1392,17 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() { return LookupModulesCache; } +/// Determine whether the module M is part of the current module from the +/// perspective of a module-private visibility check. +static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) { + // If M is the global module fragment of a module that we've not yet finished + // parsing, then it must be part of the current module. + return M->getTopLevelModuleName() == LangOpts.CurrentModule || + (M->Kind == Module::GlobalModuleFragment && !M->Parent); +} + bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) { - for (Module *Merged : Context.getModulesWithMergedDefinition(Def)) + for (const Module *Merged : Context.getModulesWithMergedDefinition(Def)) if (isModuleVisible(Merged)) return true; return false; @@ -1407,8 +1416,8 @@ bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) { if (Def->getModuleOwnershipKind() == Decl::ModuleOwnershipKind::Visible && getLangOpts().ModulesLocalVisibility) return true; - for (Module *Merged : Context.getModulesWithMergedDefinition(Def)) - if (Merged->getTopLevelModuleName() == getLangOpts().CurrentModule) + for (const Module *Merged : Context.getModulesWithMergedDefinition(Def)) + if (isInCurrentModule(Merged, getLangOpts())) return true; return false; } @@ -1428,8 +1437,6 @@ hasVisibleDefaultArgument(Sema &S, const ParmDecl *D, if (!DefaultArg.isInherited() && Modules) { auto *NonConstD = const_cast<ParmDecl*>(D); Modules->push_back(S.getOwningModule(NonConstD)); - const auto &Merged = S.Context.getModulesWithMergedDefinition(NonConstD); - Modules->insert(Modules->end(), Merged.begin(), Merged.end()); } // If there was a previous default argument, maybe its parameter is visible. @@ -1464,11 +1471,8 @@ static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D, HasFilteredRedecls = true; - if (Modules) { + if (Modules) Modules->push_back(R->getOwningModule()); - const auto &Merged = S.Context.getModulesWithMergedDefinition(R); - Modules->insert(Modules->end(), Merged.begin(), Merged.end()); - } } // Only return false if there is at least one redecl that is not filtered out. @@ -1519,27 +1523,11 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { assert(D->isHidden() && "should not call this: not in slow case"); Module *DeclModule = SemaRef.getOwningModule(D); - if (!DeclModule) { - // A module-private declaration with no owning module means this is in the - // global module in the C++ Modules TS. This is visible within the same - // translation unit only. - // FIXME: Don't assume that "same translation unit" means the same thing - // as "not from an AST file". - assert(D->isModulePrivate() && "hidden decl has no module"); - if (!D->isFromASTFile() || SemaRef.hasMergedDefinitionInCurrentModule(D)) - return true; - } else { - // If the owning module is visible, and the decl is not module private, - // then the decl is visible too. (Module private is ignored within the same - // top-level module.) - if (D->isModulePrivate() - ? DeclModule->getTopLevelModuleName() == - SemaRef.getLangOpts().CurrentModule || - SemaRef.hasMergedDefinitionInCurrentModule(D) - : SemaRef.isModuleVisible(DeclModule) || - SemaRef.hasVisibleMergedDefinition(D)) - return true; - } + assert(DeclModule && "hidden decl has no owning module"); + + // If the owning module is visible, the decl is visible. + if (SemaRef.isModuleVisible(DeclModule, D->isModulePrivate())) + return true; // Determine whether a decl context is a file context for the purpose of // visibility. This looks through some (export and linkage spec) transparent @@ -1589,29 +1577,41 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { return VisibleWithinParent; } - // FIXME: All uses of DeclModule below this point should also check merged - // modules. - if (!DeclModule) - return false; + return false; +} + +bool Sema::isModuleVisible(const Module *M, bool ModulePrivate) { + // The module might be ordinarily visible. For a module-private query, that + // means it is part of the current module. For any other query, that means it + // is in our visible module set. + if (ModulePrivate) { + if (isInCurrentModule(M, getLangOpts())) + return true; + } else { + if (VisibleModules.isVisible(M)) + return true; + } + + // Otherwise, it might be visible by virtue of the query being within a + // template instantiation or similar that is permitted to look inside M. // Find the extra places where we need to look. - const auto &LookupModules = SemaRef.getLookupModules(); + const auto &LookupModules = getLookupModules(); if (LookupModules.empty()) return false; - // If our lookup set contains the decl's module, it's visible. - if (LookupModules.count(DeclModule)) + // If our lookup set contains the module, it's visible. + if (LookupModules.count(M)) return true; - // If the declaration isn't exported, it's not visible in any other module. - if (D->isModulePrivate()) + // For a module-private query, that's everywhere we get to look. + if (ModulePrivate) return false; - // Check whether DeclModule is transitively exported to an import of - // the lookup set. + // Check whether M is transitively exported to an import of the lookup set. return std::any_of(LookupModules.begin(), LookupModules.end(), - [&](const Module *M) { - return M->isModuleVisible(DeclModule); }); + [&](const Module *LookupM) { + return LookupM->isModuleVisible(M); }); } bool Sema::isVisibleSlow(const NamedDecl *D) { @@ -5061,12 +5061,12 @@ void Sema::diagnoseMissingImport(SourceLocation Loc, NamedDecl *Decl, if (!Def) Def = Decl; - Module *Owner = getOwningModule(Decl); + Module *Owner = getOwningModule(Def); assert(Owner && "definition of hidden declaration is not in a module"); llvm::SmallVector<Module*, 8> OwningModules; OwningModules.push_back(Owner); - auto Merged = Context.getModulesWithMergedDefinition(Decl); + auto Merged = Context.getModulesWithMergedDefinition(Def); OwningModules.insert(OwningModules.end(), Merged.begin(), Merged.end()); diagnoseMissingImport(Loc, Decl, Decl->getLocation(), OwningModules, MIK, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c826b836a70..61e51de349e 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1487,19 +1487,19 @@ DeclResult Sema::CheckClassTemplate( NamedDecl *Hidden = nullptr; if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { SkipBody->ShouldSkip = true; + SkipBody->Previous = Def; auto *Tmpl = cast<CXXRecordDecl>(Hidden)->getDescribedClassTemplate(); assert(Tmpl && "original definition of a class template is not a " "class template?"); makeMergedDefinitionVisible(Hidden); makeMergedDefinitionVisible(Tmpl); - return Def; + } else { + Diag(NameLoc, diag::err_redefinition) << Name; + Diag(Def->getLocation(), diag::note_previous_definition); + // FIXME: Would it make sense to try to "forget" the previous + // definition, as part of error recovery? + return true; } - - Diag(NameLoc, diag::err_redefinition) << Name; - Diag(Def->getLocation(), diag::note_previous_definition); - // FIXME: Would it make sense to try to "forget" the previous - // definition, as part of error recovery? - return true; } } } else if (PrevDecl) { @@ -1520,13 +1520,14 @@ DeclResult Sema::CheckClassTemplate( if (!(TUK == TUK_Friend && CurContext->isDependentContext()) && CheckTemplateParameterList( TemplateParams, - PrevClassTemplate ? PrevClassTemplate->getTemplateParameters() - : nullptr, + PrevClassTemplate + ? PrevClassTemplate->getMostRecentDecl()->getTemplateParameters() + : nullptr, (SS.isSet() && SemanticContext && SemanticContext->isRecord() && SemanticContext->isDependentContext()) ? TPC_ClassTemplateMember - : TUK == TUK_Friend ? TPC_FriendClassTemplate - : TPC_ClassTemplate)) + : TUK == TUK_Friend ? TPC_FriendClassTemplate : TPC_ClassTemplate, + SkipBody)) Invalid = true; if (SS.isSet()) { @@ -1561,7 +1562,7 @@ DeclResult Sema::CheckClassTemplate( // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. - if (TUK == TUK_Definition) { + if (TUK == TUK_Definition && (!SkipBody || !SkipBody->ShouldSkip)) { AddAlignmentAttributesForRecord(NewClass); AddMsStructLayoutForRecord(NewClass); } @@ -1604,7 +1605,7 @@ DeclResult Sema::CheckClassTemplate( NewClass->setLexicalDeclContext(CurContext); NewTemplate->setLexicalDeclContext(CurContext); - if (TUK == TUK_Definition) + if (TUK == TUK_Definition && (!SkipBody || !SkipBody->ShouldSkip)) NewClass->startDefinition(); ProcessDeclAttributeList(S, NewClass, Attr); @@ -1653,6 +1654,9 @@ DeclResult Sema::CheckClassTemplate( ActOnDocumentableDecl(NewTemplate); + if (SkipBody && SkipBody->ShouldSkip) + return SkipBody->Previous; + return NewTemplate; } @@ -2150,10 +2154,17 @@ static bool DiagnoseUnexpandedParameterPacks(Sema &S, /// \param TPC Describes the context in which we are checking the given /// template parameter list. /// +/// \param SkipBody If we might have already made a prior merged definition +/// of this template visible, the corresponding body-skipping information. +/// Default argument redefinition is not an error when skipping such a body, +/// because (under the ODR) we can assume the default arguments are the same +/// as the prior merged definition. +/// /// \returns true if an error occurred, false otherwise. bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, TemplateParameterList *OldParams, - TemplateParamListContext TPC) { + TemplateParamListContext TPC, + SkipBodyInfo *SkipBody) { bool Invalid = false; // C++ [temp.param]p10: @@ -2203,7 +2214,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, "Parameter packs can't have a default argument!"); SawParameterPack = true; } else if (OldTypeParm && hasVisibleDefaultArgument(OldTypeParm) && - NewTypeParm->hasDefaultArgument()) { + NewTypeParm->hasDefaultArgument() && + (!SkipBody || !SkipBody->ShouldSkip)) { OldDefaultLoc = OldTypeParm->getDefaultArgumentLoc(); NewDefaultLoc = NewTypeParm->getDefaultArgumentLoc(); SawDefaultArgument = true; @@ -2247,7 +2259,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, if (!NewNonTypeParm->isPackExpansion()) SawParameterPack = true; } else if (OldNonTypeParm && hasVisibleDefaultArgument(OldNonTypeParm) && - NewNonTypeParm->hasDefaultArgument()) { + NewNonTypeParm->hasDefaultArgument() && + (!SkipBody || !SkipBody->ShouldSkip)) { OldDefaultLoc = OldNonTypeParm->getDefaultArgumentLoc(); NewDefaultLoc = NewNonTypeParm->getDefaultArgumentLoc(); SawDefaultArgument = true; @@ -2290,7 +2303,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, SawParameterPack = true; } else if (OldTemplateParm && hasVisibleDefaultArgument(OldTemplateParm) && - NewTemplateParm->hasDefaultArgument()) { + NewTemplateParm->hasDefaultArgument() && + (!SkipBody || !SkipBody->ShouldSkip)) { OldDefaultLoc = OldTemplateParm->getDefaultArgument().getLocation(); NewDefaultLoc = NewTemplateParm->getDefaultArgument().getLocation(); SawDefaultArgument = true; @@ -7689,9 +7703,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization( NamedDecl *Hidden = nullptr; if (Def && SkipBody && !hasVisibleDefinition(Def, &Hidden)) { SkipBody->ShouldSkip = true; + SkipBody->Previous = Def; makeMergedDefinitionVisible(Hidden); - // From here on out, treat this as just a redeclaration. - TUK = TUK_Declaration; } else if (Def) { SourceRange Range(TemplateNameLoc, RAngleLoc); Diag(TemplateNameLoc, diag::err_redefinition) << Specialization << Range; @@ -7705,7 +7718,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization( // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. - if (TUK == TUK_Definition) { + if (TUK == TUK_Definition && (!SkipBody || !SkipBody->ShouldSkip)) { AddAlignmentAttributesForRecord(Specialization); AddMsStructLayoutForRecord(Specialization); } @@ -7741,7 +7754,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization( Specialization->setLexicalDeclContext(CurContext); // We may be starting the definition of this specialization. - if (TUK == TUK_Definition) + if (TUK == TUK_Definition && (!SkipBody || !SkipBody->ShouldSkip)) Specialization->startDefinition(); if (TUK == TUK_Friend) { @@ -7757,6 +7770,10 @@ DeclResult Sema::ActOnClassTemplateSpecialization( // context. However, specializations are not found by name lookup. CurContext->addDecl(Specialization); } + + if (SkipBody && SkipBody->ShouldSkip) + return SkipBody->Previous; + return Specialization; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 3ed9eeed16e..2c29b8cbcc1 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1228,7 +1228,7 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) { } TemplateParameterList *PrevParams - = PrevClassTemplate->getTemplateParameters(); + = PrevClassTemplate->getMostRecentDecl()->getTemplateParameters(); // Make sure the parameter lists match. if (!SemaRef.TemplateParameterListsAreEqual(InstParams, PrevParams, diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 5fefac6ee34..900ccb18c7e 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7615,14 +7615,28 @@ bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested, assert(D && "missing definition for pattern of instantiated definition"); *Suggested = D; - if (isVisible(D)) + + auto DefinitionIsVisible = [&] { + // The (primary) definition might be in a visible module. + if (isVisible(D)) + return true; + + // A visible module might have a merged definition instead. + if (D->isModulePrivate() ? hasMergedDefinitionInCurrentModule(D) + : hasVisibleMergedDefinition(D)) + return true; + + return false; + }; + + if (DefinitionIsVisible()) return true; // The external source may have additional definitions of this entity that are // visible, so complete the redeclaration chain now and ask again. if (auto *Source = Context.getExternalSource()) { Source->CompleteRedeclChain(D); - return isVisible(D); + return DefinitionIsVisible(); } return false; diff --git a/clang/test/Modules/submodules-merge-defs.cpp b/clang/test/Modules/submodules-merge-defs.cpp index 4ab822a022b..795150208c9 100644 --- a/clang/test/Modules/submodules-merge-defs.cpp +++ b/clang/test/Modules/submodules-merge-defs.cpp @@ -65,7 +65,7 @@ using pre_i = I<>; // expected-error +{{must be imported}} J<> pre_j; // expected-error {{declaration of 'J' must be imported}} #ifdef IMPORT_USE_2 -// expected-error-re@-2 {{default argument of 'J' must be imported from one of {{.*}}stuff.use{{.*}}stuff.use-2}} +// expected-error-re@-2 {{default argument of 'J' must be imported from one of {{.*}}stuff.use-2{{.*}}stuff.use}} #elif EARLY_INDIRECT_INCLUDE // expected-error@-4 {{default argument of 'J' must be imported from module 'merged-defs'}} #else |

