diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaCXXScopeSpec.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 7 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 354 | ||||
-rw-r--r-- | clang/lib/Sema/SemaModule.cpp | 15 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 71 | ||||
-rw-r--r-- | clang/lib/Sema/SemaType.cpp | 67 |
7 files changed, 404 insertions, 118 deletions
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 97783eb..3f8fedda 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -121,7 +121,7 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS, // entering the context, and that can't happen in a SFINAE context. assert(!isSFINAEContext() && "partial specialization scope specifier in SFINAE context?"); - if (!hasVisibleDeclaration(PartialSpec)) + if (!hasReachableDefinition(PartialSpec)) diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec, MissingImportKind::PartialSpecialization, /*Recover*/true); @@ -243,8 +243,8 @@ bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L, if (EnumD->isCompleteDefinition()) { // If we know about the definition but it is not visible, complain. NamedDecl *SuggestedDef = nullptr; - if (!hasVisibleDefinition(EnumD, &SuggestedDef, - /*OnlyNeedComplete*/false)) { + if (!hasReachableDefinition(EnumD, &SuggestedDef, + /*OnlyNeedComplete*/ false)) { // If the user is going to see an error here, recover by making the // definition visible. bool TreatAsComplete = !isSFINAEContext(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 99dfebd..7ed338d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16310,7 +16310,12 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc, if (getLangOpts().CPlusPlusModules && isCurrentModulePurview()) { Module *GlobalModule = PushGlobalModuleFragment(ExternLoc, /*IsImplicit=*/true); - D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate); + /// According to [module.reach]p3.2, + /// The declaration in global module fragment is reachable if it is not + /// discarded. And the discarded declaration should be deleted. So it + /// doesn't matter mark the declaration in global module fragment as + /// reachable here. + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported); D->setLocalOwningModule(GlobalModule); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ce7706a..40fb60c 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17943,7 +17943,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, if (NeedDefinition && (Func->getTemplateSpecializationKind() != TSK_Undeclared || Func->getMemberSpecializationInfo())) - checkSpecializationVisibility(Loc, Func); + checkSpecializationReachability(Loc, Func); if (getLangOpts().CUDA) CheckCUDACall(Loc, Func); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 9f2e1ea..47c7a61 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1607,16 +1607,17 @@ bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) { return false; } -template<typename ParmDecl> +template <typename ParmDecl> static bool -hasVisibleDefaultArgument(Sema &S, const ParmDecl *D, - llvm::SmallVectorImpl<Module *> *Modules) { +hasAcceptableDefaultArgument(Sema &S, const ParmDecl *D, + llvm::SmallVectorImpl<Module *> *Modules, + Sema::AcceptableKind Kind) { if (!D->hasDefaultArgument()) return false; while (D) { auto &DefaultArg = D->getDefaultArgStorage(); - if (!DefaultArg.isInherited() && S.isVisible(D)) + if (!DefaultArg.isInherited() && S.isAcceptable(D, Kind)) return true; if (!DefaultArg.isInherited() && Modules) { @@ -1630,20 +1631,36 @@ hasVisibleDefaultArgument(Sema &S, const ParmDecl *D, return false; } -bool Sema::hasVisibleDefaultArgument(const NamedDecl *D, - llvm::SmallVectorImpl<Module *> *Modules) { +bool Sema::hasAcceptableDefaultArgument( + const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules, + Sema::AcceptableKind Kind) { if (auto *P = dyn_cast<TemplateTypeParmDecl>(D)) - return ::hasVisibleDefaultArgument(*this, P, Modules); + return ::hasAcceptableDefaultArgument(*this, P, Modules, Kind); + if (auto *P = dyn_cast<NonTypeTemplateParmDecl>(D)) - return ::hasVisibleDefaultArgument(*this, P, Modules); - return ::hasVisibleDefaultArgument(*this, cast<TemplateTemplateParmDecl>(D), - Modules); + return ::hasAcceptableDefaultArgument(*this, P, Modules, Kind); + + return ::hasAcceptableDefaultArgument( + *this, cast<TemplateTemplateParmDecl>(D), Modules, Kind); +} + +bool Sema::hasVisibleDefaultArgument(const NamedDecl *D, + llvm::SmallVectorImpl<Module *> *Modules) { + return hasAcceptableDefaultArgument(D, Modules, + Sema::AcceptableKind::Visible); +} + +bool Sema::hasReachableDefaultArgument( + const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) { + return hasAcceptableDefaultArgument(D, Modules, + Sema::AcceptableKind::Reachable); } -template<typename Filter> -static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D, - llvm::SmallVectorImpl<Module *> *Modules, - Filter F) { +template <typename Filter> +static bool +hasAcceptableDeclarationImpl(Sema &S, const NamedDecl *D, + llvm::SmallVectorImpl<Module *> *Modules, Filter F, + Sema::AcceptableKind Kind) { bool HasFilteredRedecls = false; for (auto *Redecl : D->redecls()) { @@ -1651,7 +1668,7 @@ static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D, if (!F(R)) continue; - if (S.isVisible(R)) + if (S.isAcceptable(R, Kind)) return true; HasFilteredRedecls = true; @@ -1667,74 +1684,115 @@ static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D, return true; } +static bool +hasAcceptableExplicitSpecialization(Sema &S, const NamedDecl *D, + llvm::SmallVectorImpl<Module *> *Modules, + Sema::AcceptableKind Kind) { + return hasAcceptableDeclarationImpl( + S, D, Modules, + [](const NamedDecl *D) { + if (auto *RD = dyn_cast<CXXRecordDecl>(D)) + return RD->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization; + if (auto *FD = dyn_cast<FunctionDecl>(D)) + return FD->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization; + if (auto *VD = dyn_cast<VarDecl>(D)) + return VD->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization; + llvm_unreachable("unknown explicit specialization kind"); + }, + Kind); +} + bool Sema::hasVisibleExplicitSpecialization( const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) { - return hasVisibleDeclarationImpl(*this, D, Modules, [](const NamedDecl *D) { - if (auto *RD = dyn_cast<CXXRecordDecl>(D)) - return RD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization; - if (auto *FD = dyn_cast<FunctionDecl>(D)) - return FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization; - if (auto *VD = dyn_cast<VarDecl>(D)) - return VD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization; - llvm_unreachable("unknown explicit specialization kind"); - }); + return ::hasAcceptableExplicitSpecialization(*this, D, Modules, + Sema::AcceptableKind::Visible); } -bool Sema::hasVisibleMemberSpecialization( +bool Sema::hasReachableExplicitSpecialization( const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) { + return ::hasAcceptableExplicitSpecialization(*this, D, Modules, + Sema::AcceptableKind::Reachable); +} + +static bool +hasAcceptableMemberSpecialization(Sema &S, const NamedDecl *D, + llvm::SmallVectorImpl<Module *> *Modules, + Sema::AcceptableKind Kind) { assert(isa<CXXRecordDecl>(D->getDeclContext()) && "not a member specialization"); - return hasVisibleDeclarationImpl(*this, D, Modules, [](const NamedDecl *D) { - // If the specialization is declared at namespace scope, then it's a member - // specialization declaration. If it's lexically inside the class - // definition then it was instantiated. - // - // FIXME: This is a hack. There should be a better way to determine this. - // FIXME: What about MS-style explicit specializations declared within a - // class definition? - return D->getLexicalDeclContext()->isFileContext(); - }); + return hasAcceptableDeclarationImpl( + S, D, Modules, + [](const NamedDecl *D) { + // If the specialization is declared at namespace scope, then it's a + // member specialization declaration. If it's lexically inside the class + // definition then it was instantiated. + // + // FIXME: This is a hack. There should be a better way to determine + // this. + // FIXME: What about MS-style explicit specializations declared within a + // class definition? + return D->getLexicalDeclContext()->isFileContext(); + }, + Kind); +} + +bool Sema::hasVisibleMemberSpecialization( + const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) { + return hasAcceptableMemberSpecialization(*this, D, Modules, + Sema::AcceptableKind::Visible); +} + +bool Sema::hasReachableMemberSpecialization( + const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) { + return hasAcceptableMemberSpecialization(*this, D, Modules, + Sema::AcceptableKind::Reachable); } -/// Determine whether a declaration is visible to name lookup. +/// Determine whether a declaration is acceptable to name lookup. /// -/// This routine determines whether the declaration D is visible in the current -/// lookup context, taking into account the current template instantiation -/// stack. During template instantiation, a declaration is visible if it is -/// visible from a module containing any entity on the template instantiation -/// path (by instantiating a template, you allow it to see the declarations that -/// your module can see, including those later on in your module). -bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { +/// This routine determines whether the declaration D is acceptable in the +/// current lookup context, taking into account the current template +/// instantiation stack. During template instantiation, a declaration is +/// acceptable if it is acceptable from a module containing any entity on the +/// template instantiation path (by instantiating a template, you allow it to +/// see the declarations that your module can see, including those later on in +/// your module). +bool LookupResult::isAcceptableSlow(Sema &SemaRef, NamedDecl *D, + Sema::AcceptableKind Kind) { assert(!D->isUnconditionallyVisible() && "should not call this: not in slow case"); Module *DeclModule = SemaRef.getOwningModule(D); assert(DeclModule && "hidden decl has no owning module"); - if (SemaRef.isModuleVisible(DeclModule, D->isModulePrivate())) - // If the owning module is visible, the decl is visible. + // If the owning module is visible, the decl is acceptable. + if (SemaRef.isModuleVisible(DeclModule, + D->isInvisibleOutsideTheOwningModule())) 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 - // contexts, but not others (enums). + // visibility/reachability. This looks through some (export and linkage spec) + // transparent contexts, but not others (enums). auto IsEffectivelyFileContext = [](const DeclContext *DC) { return DC->isFileContext() || isa<LinkageSpecDecl>(DC) || isa<ExportDecl>(DC); }; // If this declaration is not at namespace scope - // then it is visible if its lexical parent has a visible definition. + // then it is acceptable if its lexical parent has a acceptable definition. DeclContext *DC = D->getLexicalDeclContext(); if (DC && !IsEffectivelyFileContext(DC)) { // For a parameter, check whether our current template declaration's - // lexical context is visible, not whether there's some other visible + // lexical context is acceptable, not whether there's some other acceptable // definition of it, because parameters aren't "within" the definition. // - // In C++ we need to check for a visible definition due to ODR merging, + // In C++ we need to check for a acceptable definition due to ODR merging, // and in C we must not because each declaration of a function gets its own // set of declarations for tags in prototype scope. - bool VisibleWithinParent; + bool AcceptableWithinParent; if (D->isTemplateParameter()) { bool SearchDefinitions = true; if (const auto *DCD = dyn_cast<Decl>(DC)) { @@ -1745,41 +1803,63 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { } } if (SearchDefinitions) - VisibleWithinParent = SemaRef.hasVisibleDefinition(cast<NamedDecl>(DC)); + AcceptableWithinParent = + SemaRef.hasAcceptableDefinition(cast<NamedDecl>(DC), Kind); else - VisibleWithinParent = isVisible(SemaRef, cast<NamedDecl>(DC)); + AcceptableWithinParent = + isAcceptable(SemaRef, cast<NamedDecl>(DC), Kind); } else if (isa<ParmVarDecl>(D) || (isa<FunctionDecl>(DC) && !SemaRef.getLangOpts().CPlusPlus)) - VisibleWithinParent = isVisible(SemaRef, cast<NamedDecl>(DC)); + AcceptableWithinParent = isAcceptable(SemaRef, cast<NamedDecl>(DC), Kind); else if (D->isModulePrivate()) { - // A module-private declaration is only visible if an enclosing lexical + // A module-private declaration is only acceptable if an enclosing lexical // parent was merged with another definition in the current module. - VisibleWithinParent = false; + AcceptableWithinParent = false; do { if (SemaRef.hasMergedDefinitionInCurrentModule(cast<NamedDecl>(DC))) { - VisibleWithinParent = true; + AcceptableWithinParent = true; break; } DC = DC->getLexicalParent(); } while (!IsEffectivelyFileContext(DC)); } else { - VisibleWithinParent = SemaRef.hasVisibleDefinition(cast<NamedDecl>(DC)); + AcceptableWithinParent = + SemaRef.hasAcceptableDefinition(cast<NamedDecl>(DC), Kind); } - if (VisibleWithinParent && SemaRef.CodeSynthesisContexts.empty() && + if (AcceptableWithinParent && SemaRef.CodeSynthesisContexts.empty() && + Kind == Sema::AcceptableKind::Visible && // FIXME: Do something better in this case. !SemaRef.getLangOpts().ModulesLocalVisibility) { // Cache the fact that this declaration is implicitly visible because // its parent has a visible definition. D->setVisibleDespiteOwningModule(); } - return VisibleWithinParent; + return AcceptableWithinParent; } - return false; + if (Kind == Sema::AcceptableKind::Visible) + return false; + + assert(Kind == Sema::AcceptableKind::Reachable && + "Additional Sema::AcceptableKind?"); + return isReachableSlow(SemaRef, D); } bool Sema::isModuleVisible(const Module *M, bool ModulePrivate) { + // [module.global.frag]p2: + // A global-module-fragment specifies the contents of the global module + // fragment for a module unit. The global module fragment can be used to + // provide declarations that are attached to the global module and usable + // within the module unit. + // + // Global module fragment is special. Global Module fragment is only usable + // within the module unit it got defined [module.global.frag]p2. So here we + // check if the Module is the global module fragment in current translation + // unit. + if (M->isGlobalModule() && M != this->GlobalModuleFragment) + return false; + // The module might be ordinarily visible. For a module-private query, that // means it is part of the current module. if (ModulePrivate && isUsableModule(M)) @@ -1812,8 +1892,74 @@ bool Sema::isModuleVisible(const Module *M, bool ModulePrivate) { }); } -bool Sema::isVisibleSlow(const NamedDecl *D) { - return LookupResult::isVisible(*this, const_cast<NamedDecl*>(D)); +// FIXME: Return false directly if we don't have an interface dependency on the +// translation unit containing D. +bool LookupResult::isReachableSlow(Sema &SemaRef, NamedDecl *D) { + assert(!isVisible(SemaRef, D) && "Shouldn't call the slow case.\n"); + + Module *DeclModule = SemaRef.getOwningModule(D); + assert(DeclModule && "hidden decl has no owning module"); + + // Entities in module map modules are reachable only if they're visible. + if (DeclModule->isModuleMapModule()) + return false; + + // If D comes from a module and SemaRef doesn't own a module, it implies D + // comes from another TU. In case SemaRef owns a module, we could judge if D + // comes from another TU by comparing the module unit. + // + // FIXME: It would look better if we have direct method to judge whether D is + // in another TU. + if (SemaRef.getCurrentModule() && + SemaRef.getCurrentModule()->getTopLevelModule() == + DeclModule->getTopLevelModule()) + return true; + + // [module.reach]/p3: + // A declaration D is reachable from a point P if: + // ... + // - D is not discarded ([module.global.frag]), appears in a translation unit + // that is reachable from P, and does not appear within a private module + // fragment. + // + // A declaration that's discarded in the GMF should be module-private. + if (D->isModulePrivate()) + return false; + + // [module.reach]/p1 + // A translation unit U is necessarily reachable from a point P if U is a + // module interface unit on which the translation unit containing P has an + // interface dependency, or the translation unit containing P imports U, in + // either case prior to P ([module.import]). + // + // [module.import]/p10 + // A translation unit has an interface dependency on a translation unit U if + // it contains a declaration (possibly a module-declaration) that imports U + // or if it has an interface dependency on a translation unit that has an + // interface dependency on U. + // + // So we could conclude the module unit U is necessarily reachable if: + // (1) The module unit U is module interface unit. + // (2) The current unit has an interface dependency on the module unit U. + // + // Here we only check for the first condition. Since we couldn't see + // DeclModule if it isn't (transitively) imported. + if (DeclModule->getTopLevelModule()->isModuleInterfaceUnit()) + return true; + + // [module.reach]/p2 + // Additional translation units on + // which the point within the program has an interface dependency may be + // considered reachable, but it is unspecified which are and under what + // circumstances. + // + // The decision here is to treat all additional tranditional units as + // unreachable. + return false; +} + +bool Sema::isAcceptableSlow(const NamedDecl *D, Sema::AcceptableKind Kind) { + return LookupResult::isAcceptable(*this, const_cast<NamedDecl *>(D), Kind); } bool Sema::shouldLinkPossiblyHiddenDecl(LookupResult &R, const NamedDecl *New) { @@ -1862,7 +2008,7 @@ bool Sema::shouldLinkPossiblyHiddenDecl(LookupResult &R, const NamedDecl *New) { /// and visible. If no declaration of D is visible, returns null. static NamedDecl *findAcceptableDecl(Sema &SemaRef, NamedDecl *D, unsigned IDNS) { - assert(!LookupResult::isVisible(SemaRef, D) && "not in slow case"); + assert(!LookupResult::isAvailableForLookup(SemaRef, D) && "not in slow case"); for (auto RD : D->redecls()) { // Don't bother with extra checks if we already know this one isn't visible. @@ -1874,7 +2020,7 @@ static NamedDecl *findAcceptableDecl(Sema &SemaRef, NamedDecl *D, // visible in the same scope as D. This needs to be done much more // carefully. if (ND->isInIdentifierNamespace(IDNS) && - LookupResult::isVisible(SemaRef, ND)) + LookupResult::isAvailableForLookup(SemaRef, ND)) return ND; } @@ -1884,8 +2030,17 @@ static NamedDecl *findAcceptableDecl(Sema &SemaRef, NamedDecl *D, bool Sema::hasVisibleDeclarationSlow(const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) { assert(!isVisible(D) && "not in slow case"); - return hasVisibleDeclarationImpl(*this, D, Modules, - [](const NamedDecl *) { return true; }); + return hasAcceptableDeclarationImpl( + *this, D, Modules, [](const NamedDecl *) { return true; }, + Sema::AcceptableKind::Visible); +} + +bool Sema::hasReachableDeclarationSlow( + const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) { + assert(!isReachable(D) && "not in slow case"); + return hasAcceptableDeclarationImpl( + *this, D, Modules, [](const NamedDecl *) { return true; }, + Sema::AcceptableKind::Reachable); } NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const { @@ -1910,6 +2065,60 @@ NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const { return findAcceptableDecl(getSema(), D, IDNS); } +bool LookupResult::isVisible(Sema &SemaRef, NamedDecl *D) { + // If this declaration is already visible, return it directly. + if (D->isUnconditionallyVisible()) + return true; + + // During template instantiation, we can refer to hidden declarations, if + // they were visible in any module along the path of instantiation. + return isAcceptableSlow(SemaRef, D, Sema::AcceptableKind::Visible); +} + +bool LookupResult::isReachable(Sema &SemaRef, NamedDecl *D) { + if (D->isUnconditionallyVisible()) + return true; + + return isAcceptableSlow(SemaRef, D, Sema::AcceptableKind::Reachable); +} + +bool LookupResult::isAvailableForLookup(Sema &SemaRef, NamedDecl *ND) { + // We should check the visibility at the callsite already. + if (isVisible(SemaRef, ND)) + return true; + + auto *DC = ND->getDeclContext(); + // If ND is not visible and it is at namespace scope, it shouldn't be found + // by name lookup. + if (DC->isFileContext()) + return false; + + // [module.interface]p7 + // Class and enumeration member names can be found by name lookup in any + // context in which a definition of the type is reachable. + // + // FIXME: The current implementation didn't consider about scope. For example, + // ``` + // // m.cppm + // export module m; + // enum E1 { e1 }; + // // Use.cpp + // import m; + // void test() { + // auto a = E1::e1; // Error as expected. + // auto b = e1; // Should be error. namespace-scope name e1 is not visible + // } + // ``` + // For the above example, the current implementation would emit error for `a` + // correctly. However, the implementation wouldn't diagnose about `b` now. + // Since we only check the reachability for the parent only. + // See clang/test/CXX/module/module.interface/p7.cpp for example. + if (auto *TD = dyn_cast<TagDecl>(DC)) + return SemaRef.hasReachableDefinition(TD); + + return false; +} + /// Perform unqualified name lookup starting from a given /// scope. /// @@ -3641,7 +3850,16 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, SourceLocation Loc, } } else if (D->getFriendObjectKind()) { auto *RD = cast<CXXRecordDecl>(D->getLexicalDeclContext()); - if (AssociatedClasses.count(RD) && isVisible(D)) { + // [basic.lookup.argdep]p4: + // Argument-dependent lookup finds all declarations of functions and + // function templates that + // - ... + // - are declared as a friend ([class.friend]) of any class with a + // reachable definition in the set of associated entities, + // + // FIXME: If there's a merged definition of D that is reachable, then + // the friend declaration should be considered. + if (AssociatedClasses.count(RD) && isReachable(D)) { Visible = true; break; } diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index b0e7d30..3aa124d 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -90,7 +90,14 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) { // All declarations created from now on are owned by the global module. auto *TU = Context.getTranslationUnitDecl(); - TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible); + // [module.global.frag]p2 + // A global-module-fragment specifies the contents of the global module + // fragment for a module unit. The global module fragment can be used to + // provide declarations that are attached to the global module and usable + // within the module unit. + // + // So the declations in the global module shouldn't be visible by default. + TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported); TU->setLocalOwningModule(GlobalModule); // FIXME: Consider creating an explicit representation of this declaration. @@ -325,10 +332,12 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, VisibleModules.setVisible(Mod, ModuleLoc); // From now on, we have an owning module for all declarations we see. - // However, those declarations are module-private unless explicitly + // In C++20 modules, those declaration would be reachable when imported + // unless explicitily exported. + // Otherwise, those declarations are module-private unless explicitly // exported. auto *TU = Context.getTranslationUnitDecl(); - TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate); + TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported); TU->setLocalOwningModule(Mod); // We are in the module purview, but before any other (non import) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 250dbcd..dbfe616 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -799,8 +799,9 @@ bool Sema::DiagnoseUninstantiableTemplate(SourceLocation PointOfInstantiation, if (PatternDef && !IsEntityBeingDefined) { NamedDecl *SuggestedDef = nullptr; - if (!hasVisibleDefinition(const_cast<NamedDecl*>(PatternDef), &SuggestedDef, - /*OnlyNeedComplete*/false)) { + if (!hasReachableDefinition(const_cast<NamedDecl *>(PatternDef), + &SuggestedDef, + /*OnlyNeedComplete*/ false)) { // If we're allowed to diagnose this and recover, do so. bool Recover = Complain && !isSFINAEContext(); if (Complain) @@ -5255,7 +5256,7 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template, HasDefaultArg = false; if (TemplateTypeParmDecl *TypeParm = dyn_cast<TemplateTypeParmDecl>(Param)) { - if (!hasVisibleDefaultArgument(TypeParm)) + if (!hasReachableDefaultArgument(TypeParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5272,7 +5273,7 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template, if (NonTypeTemplateParmDecl *NonTypeParm = dyn_cast<NonTypeTemplateParmDecl>(Param)) { - if (!hasVisibleDefaultArgument(NonTypeParm)) + if (!hasReachableDefaultArgument(NonTypeParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5290,7 +5291,7 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template, TemplateTemplateParmDecl *TempTempParm = cast<TemplateTemplateParmDecl>(Param); - if (!hasVisibleDefaultArgument(TempTempParm)) + if (!hasReachableDefaultArgument(TempTempParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5628,10 +5629,10 @@ static bool diagnoseMissingArgument(Sema &S, SourceLocation Loc, ->getTemplateParameters() ->getParam(D->getIndex())); - // If there's a default argument that's not visible, diagnose that we're + // If there's a default argument that's not reachable, diagnose that we're // missing a module import. llvm::SmallVector<Module*, 8> Modules; - if (D->hasDefaultArgument() && !S.hasVisibleDefaultArgument(D, &Modules)) { + if (D->hasDefaultArgument() && !S.hasReachableDefaultArgument(D, &Modules)) { S.diagnoseMissingImport(Loc, cast<NamedDecl>(TD), D->getDefaultArgumentLoc(), Modules, Sema::MissingImportKind::DefaultArgument, @@ -5814,7 +5815,7 @@ bool Sema::CheckTemplateArgumentList( // (when the template parameter was part of a nested template) into // the default argument. if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param)) { - if (!hasVisibleDefaultArgument(TTP)) + if (!hasReachableDefaultArgument(TTP)) return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP, NewArgs); @@ -5831,7 +5832,7 @@ bool Sema::CheckTemplateArgumentList( ArgType); } else if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*Param)) { - if (!hasVisibleDefaultArgument(NTTP)) + if (!hasReachableDefaultArgument(NTTP)) return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP, NewArgs); @@ -5849,7 +5850,7 @@ bool Sema::CheckTemplateArgumentList( TemplateTemplateParmDecl *TempParm = cast<TemplateTemplateParmDecl>(*Param); - if (!hasVisibleDefaultArgument(TempParm)) + if (!hasReachableDefaultArgument(TempParm)) return diagnoseMissingArgument(*this, TemplateLoc, Template, TempParm, NewArgs); @@ -11006,10 +11007,12 @@ class ExplicitSpecializationVisibilityChecker { Sema &S; SourceLocation Loc; llvm::SmallVector<Module *, 8> Modules; + Sema::AcceptableKind Kind; public: - ExplicitSpecializationVisibilityChecker(Sema &S, SourceLocation Loc) - : S(S), Loc(Loc) {} + ExplicitSpecializationVisibilityChecker(Sema &S, SourceLocation Loc, + Sema::AcceptableKind Kind) + : S(S), Loc(Loc), Kind(Kind) {} void check(NamedDecl *ND) { if (auto *FD = dyn_cast<FunctionDecl>(ND)) @@ -11037,6 +11040,23 @@ private: S.diagnoseMissingImport(Loc, D, D->getLocation(), Modules, Kind, Recover); } + bool CheckMemberSpecialization(const NamedDecl *D) { + return Kind == Sema::AcceptableKind::Visible + ? S.hasVisibleMemberSpecialization(D) + : S.hasReachableMemberSpecialization(D); + } + + bool CheckExplicitSpecialization(const NamedDecl *D) { + return Kind == Sema::AcceptableKind::Visible + ? S.hasVisibleExplicitSpecialization(D) + : S.hasReachableExplicitSpecialization(D); + } + + bool CheckDeclaration(const NamedDecl *D) { + return Kind == Sema::AcceptableKind::Visible ? S.hasVisibleDeclaration(D) + : S.hasReachableDeclaration(D); + } + // Check a specific declaration. There are three problematic cases: // // 1) The declaration is an explicit specialization of a template @@ -11053,10 +11073,9 @@ private: void checkImpl(SpecDecl *Spec) { bool IsHiddenExplicitSpecialization = false; if (Spec->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { - IsHiddenExplicitSpecialization = - Spec->getMemberSpecializationInfo() - ? !S.hasVisibleMemberSpecialization(Spec, &Modules) - : !S.hasVisibleExplicitSpecialization(Spec, &Modules); + IsHiddenExplicitSpecialization = Spec->getMemberSpecializationInfo() + ? !CheckMemberSpecialization(Spec) + : !CheckExplicitSpecialization(Spec); } else { checkInstantiated(Spec); } @@ -11080,7 +11099,7 @@ private: checkTemplate(TD); else if (auto *TD = From.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) { - if (!S.hasVisibleDeclaration(TD)) + if (!CheckDeclaration(TD)) diagnose(TD, true); checkTemplate(TD); } @@ -11096,7 +11115,7 @@ private: checkTemplate(TD); else if (auto *TD = From.dyn_cast<VarTemplatePartialSpecializationDecl *>()) { - if (!S.hasVisibleDeclaration(TD)) + if (!CheckDeclaration(TD)) diagnose(TD, true); checkTemplate(TD); } @@ -11107,7 +11126,7 @@ private: template<typename TemplDecl> void checkTemplate(TemplDecl *TD) { if (TD->isMemberSpecialization()) { - if (!S.hasVisibleMemberSpecialization(TD, &Modules)) + if (!CheckMemberSpecialization(TD)) diagnose(TD->getMostRecentDecl(), false); } } @@ -11118,5 +11137,17 @@ void Sema::checkSpecializationVisibility(SourceLocation Loc, NamedDecl *Spec) { if (!getLangOpts().Modules) return; - ExplicitSpecializationVisibilityChecker(*this, Loc).check(Spec); + ExplicitSpecializationVisibilityChecker(*this, Loc, + Sema::AcceptableKind::Visible) + .check(Spec); +} + +void Sema::checkSpecializationReachability(SourceLocation Loc, + NamedDecl *Spec) { + if (!getLangOpts().CPlusPlusModules) + return checkSpecializationVisibility(Loc, Spec); + + ExplicitSpecializationVisibilityChecker(*this, Loc, + Sema::AcceptableKind::Reachable) + .check(Spec); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 0015d6a..3edce94 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8642,17 +8642,8 @@ bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) { return Ctx.IsEquivalent(D, Suggested); } -/// Determine whether there is any declaration of \p D that was ever a -/// definition (perhaps before module merging) and is currently visible. -/// \param D The definition of the entity. -/// \param Suggested Filled in with the declaration that should be made visible -/// in order to provide a definition of this entity. -/// \param OnlyNeedComplete If \c true, we only need the type to be complete, -/// not defined. This only matters for enums with a fixed underlying -/// type, since in all other cases, a type is complete if and only if it -/// is defined. -bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested, - bool OnlyNeedComplete) { +bool Sema::hasAcceptableDefinition(NamedDecl *D, NamedDecl **Suggested, + AcceptableKind Kind, bool OnlyNeedComplete) { // Easy case: if we don't have modules, all declarations are visible. if (!getLangOpts().Modules && !getLangOpts().ModulesLocalVisibility) return true; @@ -8696,13 +8687,14 @@ bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested, VD = Pattern; D = VD->getDefinition(); } + assert(D && "missing definition for pattern of instantiated definition"); *Suggested = D; - auto DefinitionIsVisible = [&] { + auto DefinitionIsAcceptable = [&] { // The (primary) definition might be in a visible module. - if (isVisible(D)) + if (isAcceptable(D, Kind)) return true; // A visible module might have a merged definition instead. @@ -8720,19 +8712,51 @@ bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested, return false; }; - if (DefinitionIsVisible()) + if (DefinitionIsAcceptable()) 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 DefinitionIsVisible(); + return DefinitionIsAcceptable(); } return false; } +/// Determine whether there is any declaration of \p D that was ever a +/// definition (perhaps before module merging) and is currently visible. +/// \param D The definition of the entity. +/// \param Suggested Filled in with the declaration that should be made visible +/// in order to provide a definition of this entity. +/// \param OnlyNeedComplete If \c true, we only need the type to be complete, +/// not defined. This only matters for enums with a fixed underlying +/// type, since in all other cases, a type is complete if and only if it +/// is defined. +bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested, + bool OnlyNeedComplete) { + return hasAcceptableDefinition(D, Suggested, Sema::AcceptableKind::Visible, + OnlyNeedComplete); +} + +/// Determine whether there is any declaration of \p D that was ever a +/// definition (perhaps before module merging) and is currently +/// reachable. +/// \param D The definition of the entity. +/// \param Suggested Filled in with the declaration that should be made +/// reachable +/// in order to provide a definition of this entity. +/// \param OnlyNeedComplete If \c true, we only need the type to be complete, +/// not defined. This only matters for enums with a fixed underlying +/// type, since in all other cases, a type is complete if and only if it +/// is defined. +bool Sema::hasReachableDefinition(NamedDecl *D, NamedDecl **Suggested, + bool OnlyNeedComplete) { + return hasAcceptableDefinition(D, Suggested, Sema::AcceptableKind::Reachable, + OnlyNeedComplete); +} + /// Locks in the inheritance model for the given class and all of its bases. static void assignInheritanceModel(Sema &S, CXXRecordDecl *RD) { RD = RD->getMostRecentNonInjectedDecl(); @@ -8802,20 +8826,19 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T, // Check that any necessary explicit specializations are visible. For an // enum, we just need the declaration, so don't check this. if (Def && !isa<EnumDecl>(Def)) - checkSpecializationVisibility(Loc, Def); + checkSpecializationReachability(Loc, Def); // If we have a complete type, we're done. if (!Incomplete) { - // If we know about the definition but it is not visible, complain. - NamedDecl *SuggestedDef = nullptr; + NamedDecl *Suggested = nullptr; if (Def && - !hasVisibleDefinition(Def, &SuggestedDef, /*OnlyNeedComplete*/true)) { + !hasReachableDefinition(Def, &Suggested, /*OnlyNeedComplete=*/true)) { // If the user is going to see an error here, recover by making the // definition visible. bool TreatAsComplete = Diagnoser && !isSFINAEContext(); - if (Diagnoser && SuggestedDef) - diagnoseMissingImport(Loc, SuggestedDef, MissingImportKind::Definition, - /*Recover*/TreatAsComplete); + if (Diagnoser && Suggested) + diagnoseMissingImport(Loc, Suggested, MissingImportKind::Definition, + /*Recover*/ TreatAsComplete); return !TreatAsComplete; } else if (Def && !TemplateInstCallbacks.empty()) { CodeSynthesisContext TempInst; |