Index: clang/include/clang/AST/DeclBase.h =================================================================== --- clang/include/clang/AST/DeclBase.h +++ clang/include/clang/AST/DeclBase.h @@ -225,8 +225,15 @@ /// module is imported. VisibleWhenImported, + /// This declaration has an owninig module, and is visible to lookups + /// that occurs within that module. And it is reachable to other module + /// when the owninig module is imported. + ReachableWhenImported, + /// This declaration has an owning module, but is only visible to /// lookups that occur within that module. + /// The discarded declarations in global module fragment belongs + /// to this group too. ModulePrivate }; @@ -235,8 +242,8 @@ /// DeclContext. These pointers form the linked list that is /// traversed via DeclContext's decls_begin()/decls_end(). /// - /// The extra two bits are used for the ModuleOwnershipKind. - llvm::PointerIntPair NextInContextAndBits; + /// The extra three bits are used for the ModuleOwnershipKind. + llvm::PointerIntPair NextInContextAndBits; private: friend class DeclContext; @@ -621,6 +628,14 @@ /// export void B::f2(); // isInExportDeclContext() == true bool isInExportDeclContext() const; + bool isInvisibleOutsideTheOwningModule() const { + return getModuleOwnershipKind() > ModuleOwnershipKind::VisibleWhenImported; + } + + /// FIXME: Implement discarding declarations actually in global module + /// fragment. See [module.global.frag]p3,4 for details. + bool isDiscardedInGlobalModuleFragment() const { return false; } + /// Return true if this declaration has an attribute which acts as /// definition of the entity, such as 'alias' or 'ifunc'. bool hasDefiningAttr() const; @@ -798,6 +813,11 @@ return (int)getModuleOwnershipKind() <= (int)ModuleOwnershipKind::Visible; } + bool isReachable() const { + return (int)getModuleOwnershipKind() <= + (int)ModuleOwnershipKind::ReachableWhenImported; + } + /// Set that this declaration is globally visible, even if it came from a /// module that is not visible. void setVisibleDespiteOwningModule() { Index: clang/include/clang/Basic/Module.h =================================================================== --- clang/include/clang/Basic/Module.h +++ clang/include/clang/Basic/Module.h @@ -165,6 +165,8 @@ /// some C++ module. bool isGlobalModule() const { return Kind == GlobalModuleFragment; } + bool isModuleMapModule() const { return Kind == ModuleMapModule; } + private: /// The submodules of this module, indexed by name. std::vector SubModules; Index: clang/include/clang/Sema/Lookup.h =================================================================== --- clang/include/clang/Sema/Lookup.h +++ clang/include/clang/Sema/Lookup.h @@ -362,7 +362,8 @@ if (!D->isInIdentifierNamespace(IDNS)) return nullptr; - if (isVisible(getSema(), D) || isHiddenDeclarationVisible(D)) + if (isVisible(getSema(), D) || isHiddenDeclarationVisible(D) || + isReachableAndAcceptable(D)) return D; return getAcceptableDeclSlow(D); @@ -371,6 +372,29 @@ private: static bool isVisibleSlow(Sema &SemaRef, NamedDecl *D); NamedDecl *getAcceptableDeclSlow(NamedDecl *D) const; + /// Determine whether this lookup is permitted to see reachable + /// declarations. Note that the reachable but not visible declaration + /// inhabit at namespace is not allowed to be seen during name lookup. + /// + /// For example: + /// ``` + /// // m.cppm + /// export module m; + /// struct reachable { int v; } + /// export auto func() { return reachable{43}; } + /// // Use.cpp + /// import m; + /// auto Use() { + /// // Not valid. We couldn't see reachable here. + /// // So isReachableAndAcceptable would return false when we looks + /// 'reachable' here. + /// // return reachable(43).v; + /// // Valid. The field name 'v' is allowed during name lookup. + /// // So isReachableAndAcceptable would return true when we looks 'v' here. + /// return func().v; + /// } + /// ``` + bool isReachableAndAcceptable(NamedDecl *ND) const; public: /// Returns the identifier namespace mask for this lookup. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2276,8 +2276,12 @@ llvm::SmallVectorImpl *Modules = nullptr) { return isVisible(D) || hasVisibleDeclarationSlow(D, Modules); } + bool hasVisibleDeclarationSlow(const NamedDecl *D, llvm::SmallVectorImpl *Modules); + bool + hasReachableDeclaration(const NamedDecl *D, + llvm::SmallVectorImpl *Modules = nullptr); bool hasVisibleMergedDefinition(NamedDecl *Def); bool hasMergedDefinitionInCurrentModule(NamedDecl *Def); @@ -2295,21 +2299,42 @@ return hasVisibleDefinition(const_cast(D), &Hidden); } + /// Determine if \p D has a reachable definition. If not, suggest a + /// declaration that should be made reachable to expose the definition. + bool hasReachableDefinition(NamedDecl *D, NamedDecl **Suggested, + bool OnlyNeedComplete = false); + bool hasReachableDefinition(NamedDecl *D) { + NamedDecl *Hidden; + return hasReachableDefinition(D, &Hidden); + } + /// Determine if the template parameter \p D has a visible default argument. bool hasVisibleDefaultArgument(const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); + /// Determine if the template parameter \p D has a reachable default argument. + bool hasReachableDefaultArgument( + const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); /// Determine if there is a visible declaration of \p D that is an explicit /// specialization declaration for a specialization of a template. (For a /// member specialization, use hasVisibleMemberSpecialization.) bool hasVisibleExplicitSpecialization( const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); + /// Determine if there is a reachable declaration of \p D that is an explicit + /// specialization declaration for a specialization of a template. (For a + /// member specialization, use hasReachableMemberSpecialization.) + bool hasReachableExplicitSpecialization( + const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); /// Determine if there is a visible declaration of \p D that is a member /// specialization declaration (as opposed to an instantiated declaration). bool hasVisibleMemberSpecialization( const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); + /// Determine if there is a reachable declaration of \p D that is a member + /// specialization declaration (as opposed to an instantiated declaration). + bool hasReachableMemberSpecialization( + const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); /// Determine if \p A and \p B are equivalent internal linkage declarations /// from different modules, and thus an ambiguity error can be downgraded to @@ -3048,8 +3073,9 @@ /// We've found a use of a templated declaration that would trigger an /// implicit instantiation. Check that any relevant explicit specializations - /// and partial specializations are visible, and diagnose if not. + /// and partial specializations are visible/reachable, and diagnose if not. void checkSpecializationVisibility(SourceLocation Loc, NamedDecl *Spec); + void checkSpecializationReachability(SourceLocation Loc, NamedDecl *Spec); /// Retrieve a suitable printing policy for diagnostics. PrintingPolicy getPrintingPolicy() const { Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -579,6 +579,7 @@ // FIXME: Handle isModulePrivate. switch (D->getModuleOwnershipKind()) { case Decl::ModuleOwnershipKind::Unowned: + case Decl::ModuleOwnershipKind::ReachableWhenImported: case Decl::ModuleOwnershipKind::ModulePrivate: return false; case Decl::ModuleOwnershipKind::Visible: Index: clang/lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- clang/lib/Sema/SemaCXXScopeSpec.cpp +++ clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -121,7 +121,7 @@ // 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 @@ 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(); Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -16244,7 +16244,12 @@ 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); } Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -17303,7 +17303,7 @@ if (NeedDefinition && (Func->getTemplateSpecializationKind() != TSK_Undeclared || Func->getMemberSpecializationInfo())) - checkSpecializationVisibility(Loc, Func); + checkSpecializationReachability(Loc, Func); if (getLangOpts().CUDA) CheckCUDACall(Loc, Func); Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -1583,17 +1583,21 @@ return false; } -template -static bool -hasVisibleDefaultArgument(Sema &S, const ParmDecl *D, - llvm::SmallVectorImpl *Modules) { +template +static bool hasVisibleDefaultArgument(Sema &S, const ParmDecl *D, + llvm::SmallVectorImpl *Modules, + bool RequireReachable = false) { if (!D->hasDefaultArgument()) return false; while (D) { auto &DefaultArg = D->getDefaultArgStorage(); - if (!DefaultArg.isInherited() && S.isVisible(D)) - return true; + if (!DefaultArg.isInherited()) { + if (S.isVisible(D)) + return true; + if (RequireReachable && S.hasReachableDeclaration(D)) + return true; + } if (!DefaultArg.isInherited() && Modules) { auto *NonConstD = const_cast(D); @@ -1616,10 +1620,26 @@ Modules); } -template +bool Sema::hasReachableDefaultArgument( + const NamedDecl *D, llvm::SmallVectorImpl *Modules) { + // It is meaningless to talk about reachable if we are not in C++20 modules. + if (!getLangOpts().CPlusPlusModules) + return hasVisibleDefaultArgument(D, Modules); + + if (auto *P = dyn_cast(D)) + return ::hasVisibleDefaultArgument(*this, P, Modules, + /*RequireReachable*/ true); + if (auto *P = dyn_cast(D)) + return ::hasVisibleDefaultArgument(*this, P, Modules, + /*RequireReachable*/ true); + return ::hasVisibleDefaultArgument(*this, cast(D), + Modules, /*RequireReachable*/ true); +} + +template static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D, llvm::SmallVectorImpl *Modules, - Filter F) { + Filter F, bool RequireReachable = false) { bool HasFilteredRedecls = false; for (auto *Redecl : D->redecls()) { @@ -1630,6 +1650,9 @@ if (S.isVisible(R)) return true; + if (RequireReachable && S.hasReachableDeclaration(R)) + return true; + HasFilteredRedecls = true; if (Modules) @@ -1672,6 +1695,45 @@ }); } +bool Sema::hasReachableExplicitSpecialization( + const NamedDecl *D, llvm::SmallVectorImpl *Modules) { + return hasVisibleDeclarationImpl( + *this, D, Modules, + [](const NamedDecl *D) { + if (auto *RD = dyn_cast(D)) + return RD->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization; + if (auto *FD = dyn_cast(D)) + return FD->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization; + if (auto *VD = dyn_cast(D)) + return VD->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization; + llvm_unreachable("unknown explicit specialization kind"); + }, + /*RequireReachable=*/true); +} + +bool Sema::hasReachableMemberSpecialization( + const NamedDecl *D, llvm::SmallVectorImpl *Modules) { + assert(isa(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(); + }, + /*RequireReachable=*/true); +} + /// Determine whether a declaration is visible to name lookup. /// /// This routine determines whether the declaration D is visible in the current @@ -1687,8 +1749,9 @@ 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 visible. + if (SemaRef.isModuleVisible(DeclModule, + D->isInvisibleOutsideTheOwningModule())) return true; // Determine whether a decl context is a file context for the purpose of @@ -1756,6 +1819,18 @@ } 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. In this + // case, the global module fragment shouldn't own an AST File. + if (M->isGlobalModule() && M->getASTFile()) + return false; + // 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. @@ -1881,6 +1956,17 @@ [](const NamedDecl *) { return true; }); } +bool Sema::hasReachableDeclaration(const NamedDecl *D, + llvm::SmallVectorImpl *Modules) { + if (!getLangOpts().CPlusPlusModules) + return hasVisibleDeclaration(D, Modules); + if (D->isReachable() || isVisibleSlow(D)) + return true; + return hasVisibleDeclarationImpl( + *this, D, Modules, [](const NamedDecl *) { return true; }, + /*RequireReachable*/ true); +} + NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const { if (auto *ND = dyn_cast(D)) { // Namespaces are a bit of a special case: we expect there to be a lot of @@ -1903,6 +1989,32 @@ return findAcceptableDecl(getSema(), D, IDNS); } +bool LookupResult::isReachableAndAcceptable(NamedDecl *ND) const { + if (!getSema().getLangOpts().CPlusPlusModules) + return false; + + // We should check the visibility at the callsite already. + assert(!isVisible(getSema(), ND)); + + // [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. + if (auto *ECD = dyn_cast(ND)) + return getSema().hasReachableDeclaration( + cast(ECD->getDeclContext())); + + // If ND is not visible and it is at namespace scope, it shouldn't be found + // by name lookup. + if (!ND->getDeclContext()->isRecord()) + 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. + return getSema().hasReachableDeclaration( + cast(ND->getDeclContext())); +} + /// Perform unqualified name lookup starting from a given /// scope. /// @@ -3633,7 +3745,13 @@ } } else if (D->getFriendObjectKind()) { auto *RD = cast(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, + if (AssociatedClasses.count(RD) && hasReachableDeclaration(D)) { Visible = true; break; } Index: clang/lib/Sema/SemaModule.cpp =================================================================== --- clang/lib/Sema/SemaModule.cpp +++ clang/lib/Sema/SemaModule.cpp @@ -90,7 +90,14 @@ // 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. @@ -284,10 +291,16 @@ 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); + if (getLangOpts().CPlusPlusModules) + TU->setModuleOwnershipKind( + Decl::ModuleOwnershipKind::ReachableWhenImported); + else + TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate); TU->setLocalOwningModule(Mod); // We are in the module purview, but before any other (non import) Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -795,8 +795,9 @@ if (PatternDef && !IsEntityBeingDefined) { NamedDecl *SuggestedDef = nullptr; - if (!hasVisibleDefinition(const_cast(PatternDef), &SuggestedDef, - /*OnlyNeedComplete*/false)) { + if (!hasReachableDefinition(const_cast(PatternDef), + &SuggestedDef, + /*OnlyNeedComplete*/ false)) { // If we're allowed to diagnose this and recover, do so. bool Recover = Complain && !isSFINAEContext(); if (Complain) @@ -5237,7 +5238,7 @@ HasDefaultArg = false; if (TemplateTypeParmDecl *TypeParm = dyn_cast(Param)) { - if (!hasVisibleDefaultArgument(TypeParm)) + if (!hasReachableDefaultArgument(TypeParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5254,7 +5255,7 @@ if (NonTypeTemplateParmDecl *NonTypeParm = dyn_cast(Param)) { - if (!hasVisibleDefaultArgument(NonTypeParm)) + if (!hasReachableDefaultArgument(NonTypeParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5272,7 +5273,7 @@ TemplateTemplateParmDecl *TempTempParm = cast(Param); - if (!hasVisibleDefaultArgument(TempTempParm)) + if (!hasReachableDefaultArgument(TempTempParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5610,10 +5611,10 @@ ->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 Modules; - if (D->hasDefaultArgument() && !S.hasVisibleDefaultArgument(D, &Modules)) { + if (D->hasDefaultArgument() && !S.hasReachableDefaultArgument(D, &Modules)) { S.diagnoseMissingImport(Loc, cast(TD), D->getDefaultArgumentLoc(), Modules, Sema::MissingImportKind::DefaultArgument, @@ -5796,7 +5797,7 @@ // (when the template parameter was part of a nested template) into // the default argument. if (TemplateTypeParmDecl *TTP = dyn_cast(*Param)) { - if (!hasVisibleDefaultArgument(TTP)) + if (!hasReachableDefaultArgument(TTP)) return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP, NewArgs); @@ -5813,7 +5814,7 @@ ArgType); } else if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*Param)) { - if (!hasVisibleDefaultArgument(NTTP)) + if (!hasReachableDefaultArgument(NTTP)) return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP, NewArgs); @@ -5831,7 +5832,7 @@ TemplateTemplateParmDecl *TempParm = cast(*Param); - if (!hasVisibleDefaultArgument(TempParm)) + if (!hasReachableDefaultArgument(TempParm)) return diagnoseMissingArgument(*this, TemplateLoc, Template, TempParm, NewArgs); @@ -10986,10 +10987,12 @@ Sema &S; SourceLocation Loc; llvm::SmallVector Modules; + bool CheckReachability = false; public: - ExplicitSpecializationVisibilityChecker(Sema &S, SourceLocation Loc) - : S(S), Loc(Loc) {} + ExplicitSpecializationVisibilityChecker(Sema &S, SourceLocation Loc, + bool Reachability) + : S(S), Loc(Loc), CheckReachability(Reachability) {} void check(NamedDecl *ND) { if (auto *FD = dyn_cast(ND)) @@ -11017,6 +11020,24 @@ S.diagnoseMissingImport(Loc, D, D->getLocation(), Modules, Kind, Recover); } + bool CheckMemberSpecialization(const NamedDecl *D) { + if (CheckReachability) + return S.hasReachableMemberSpecialization(D); + return S.hasVisibleMemberSpecialization(D); + } + + bool CheckExplicitSpecialization(const NamedDecl *D) { + if (CheckReachability) + return S.hasReachableExplicitSpecialization(D); + return S.hasVisibleExplicitSpecialization(D); + } + + bool CheckDeclaration(const NamedDecl *D) { + if (CheckReachability) + return S.hasReachableDeclaration(D); + return S.hasVisibleDeclaration(D); + } + // Check a specific declaration. There are three problematic cases: // // 1) The declaration is an explicit specialization of a template @@ -11033,10 +11054,9 @@ 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); } @@ -11060,7 +11080,7 @@ checkTemplate(TD); else if (auto *TD = From.dyn_cast()) { - if (!S.hasVisibleDeclaration(TD)) + if (!CheckDeclaration(TD)) diagnose(TD, true); checkTemplate(TD); } @@ -11076,7 +11096,7 @@ checkTemplate(TD); else if (auto *TD = From.dyn_cast()) { - if (!S.hasVisibleDeclaration(TD)) + if (!CheckDeclaration(TD)) diagnose(TD, true); checkTemplate(TD); } @@ -11087,7 +11107,7 @@ template void checkTemplate(TemplDecl *TD) { if (TD->isMemberSpecialization()) { - if (!S.hasVisibleMemberSpecialization(TD, &Modules)) + if (!CheckMemberSpecialization(TD)) diagnose(TD->getMostRecentDecl(), false); } } @@ -11098,5 +11118,15 @@ if (!getLangOpts().Modules) return; - ExplicitSpecializationVisibilityChecker(*this, Loc).check(Spec); + ExplicitSpecializationVisibilityChecker(*this, Loc, /*Reachability*/ false) + .check(Spec); +} + +void Sema::checkSpecializationReachability(SourceLocation Loc, + NamedDecl *Spec) { + if (!getLangOpts().CPlusPlusModules) + return checkSpecializationVisibility(Loc, Spec); + + ExplicitSpecializationVisibilityChecker(*this, Loc, /*Reachability*/ true) + .check(Spec); } Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -8600,6 +8600,78 @@ return false; } +// [module.reach]p3.1: +// A declaration D is reachable from a point P if +// - D appears prior to P in the same translation unit, or +// - 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 is reachable if it is reachable from +// any point in the instantiation context ([module.context]). +// +// [module.reach]p2: +// All translation units that are necessarily reachable are reachable. +// 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. +bool Sema::hasReachableDefinition(NamedDecl *D, NamedDecl **Suggested, + bool OnlyNeedComplete) { + bool IsVisible = hasVisibleDefinition(D, Suggested, OnlyNeedComplete); + + // If it didn't introduce C++ Modules, it is meaningless to talk about + // reachable definition. + if (!getLangOpts().CPlusPlusModules) + return IsVisible; + + // Any visible declaration is reachable. + if (IsVisible) + return true; + + // We need to filter the use other than C++20 modules. Note the check abvoe is + // not sufficient. Considering the case we are using clang module + -std=c++20 + // combination. + Module *M = D->getOwningModule(); + if (M && M->isModuleMapModule()) + return false; + + // If D isn't from AST file, it implies that D appears in the same TU. + // So it should be reachable. + if (!D->isFromASTFile()) + return true; + + if (D->isModulePrivate()) + return false; + + // If D isn't in global module fragment and it isn't module private, + // we could infer that D is reachable from [module.reach]p3.1. + if (M && !M->isGlobalModule()) + return true; + + if (D->isDiscardedInGlobalModuleFragment()) + return false; + + // [module.reach]/p1-3 + // 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]). All translation units that are + // necessarily reachable are reachable. 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. + // + // A declaration is reachable if it is reachable from any point in the + // instantiation context ([module.context]). 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. + // + // Since the standard leaves space for compilers to decide whether the + // additional translation unit is reachable, we treat all translation unit as + // reachable here to ease the implementation. This strategy would surprise + // user less and save compilation time. + return true; +} + /// Locks in the inheritance model for the given class and all of its bases. static void assignInheritanceModel(Sema &S, CXXRecordDecl *RD) { RD = RD->getMostRecentNonInjectedDecl(); @@ -8669,20 +8741,19 @@ // Check that any necessary explicit specializations are visible. For an // enum, we just need the declaration, so don't check this. if (Def && !isa(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; Index: clang/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -603,15 +603,22 @@ D->setTopLevelDeclInObjCContainer(Record.readInt()); D->setAccess((AccessSpecifier)Record.readInt()); D->FromASTFile = true; - bool ModulePrivate = Record.readInt(); + auto ModuleOwnership = (Decl::ModuleOwnershipKind)Record.readInt(); + bool ModulePrivate = + (ModuleOwnership == Decl::ModuleOwnershipKind::ModulePrivate); // Determine whether this declaration is part of a (sub)module. If so, it // may not yet be visible. if (unsigned SubmoduleID = readSubmoduleID()) { + if (ModuleOwnership == Decl::ModuleOwnershipKind::Visible) + ModuleOwnership = Decl::ModuleOwnershipKind::VisibleWhenImported; + + if ((int)ModuleOwnership > 4) + llvm::report_fatal_error( + "The size of sizeModuleOwnership is larger than 4.\n"); + + D->setModuleOwnershipKind(ModuleOwnership); // Store the owning submodule ID in the declaration. - D->setModuleOwnershipKind( - ModulePrivate ? Decl::ModuleOwnershipKind::ModulePrivate - : Decl::ModuleOwnershipKind::VisibleWhenImported); D->setOwningModuleID(SubmoduleID); if (ModulePrivate) { Index: clang/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- clang/lib/Serialization/ASTWriterDecl.cpp +++ clang/lib/Serialization/ASTWriterDecl.cpp @@ -310,7 +310,7 @@ Record.push_back(D->isReferenced()); Record.push_back(D->isTopLevelDeclInObjCContainer()); Record.push_back(D->getAccess()); - Record.push_back(D->isModulePrivate()); + Record.push_back((uint64_t)D->getModuleOwnershipKind()); Record.push_back(Writer.getSubmoduleID(D->getOwningModule())); // If this declaration injected a name into a context different from its @@ -1921,7 +1921,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isReferenced Abv->Add(BitCodeAbbrevOp(0)); // TopLevelDeclInObjCContainer Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // AccessSpecifier - Abv->Add(BitCodeAbbrevOp(0)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -1954,7 +1954,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isReferenced Abv->Add(BitCodeAbbrevOp(0)); // TopLevelDeclInObjCContainer Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // AccessSpecifier - Abv->Add(BitCodeAbbrevOp(0)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -1992,7 +1992,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isReferenced Abv->Add(BitCodeAbbrevOp(0)); // TopLevelDeclInObjCContainer Abv->Add(BitCodeAbbrevOp(AS_none)); // C++ AccessSpecifier - Abv->Add(BitCodeAbbrevOp(0)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2042,7 +2042,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isReferenced Abv->Add(BitCodeAbbrevOp(0)); // TopLevelDeclInObjCContainer Abv->Add(BitCodeAbbrevOp(AS_none)); // C++ AccessSpecifier - Abv->Add(BitCodeAbbrevOp(0)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2104,7 +2104,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isReferenced Abv->Add(BitCodeAbbrevOp(0)); // TopLevelDeclInObjCContainer Abv->Add(BitCodeAbbrevOp(AS_none)); // C++ AccessSpecifier - Abv->Add(BitCodeAbbrevOp(0)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2152,7 +2152,7 @@ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isReferenced Abv->Add(BitCodeAbbrevOp(0)); // TopLevelDeclInObjCContainer Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // C++ AccessSpecifier - Abv->Add(BitCodeAbbrevOp(0)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2181,7 +2181,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isReferenced Abv->Add(BitCodeAbbrevOp(0)); // TopLevelDeclInObjCContainer Abv->Add(BitCodeAbbrevOp(AS_none)); // C++ AccessSpecifier - Abv->Add(BitCodeAbbrevOp(0)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2233,7 +2233,7 @@ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Referenced Abv->Add(BitCodeAbbrevOp(0)); // InObjCContainer Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Access - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ModulePrivate + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // ModulePrivate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(DeclarationName::Identifier)); // NameKind Index: clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/Inputs/Friend-in-reachable-class.cppm =================================================================== --- /dev/null +++ clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/Inputs/Friend-in-reachable-class.cppm @@ -0,0 +1,10 @@ +module; +# 3 __FILE__ 1 +struct A { + friend int operator+(const A &lhs, const A &rhs) { + return 0; + } +}; +# 6 "" 2 +export module X; +export using ::A; Index: clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp =================================================================== --- /dev/null +++ clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp @@ -0,0 +1,14 @@ +// This tests for [basic.lookup.argdep]/p4.2: +// 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, +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %S/Inputs/Friend-in-reachable-class.cppm -o %t/X.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %s -verify -fsyntax-only +// expected-no-diagnostics +import X; +int use() { + A a, b; + return a + b; +} Index: clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp =================================================================== --- clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp +++ clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp @@ -30,17 +30,20 @@ void test_early() { in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} - // expected-note@*{{not visible}} + // expected-note@* {{not visible}} global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}} - // expected-note@p2.cpp:16 {{not visible}} + // expected-note@p2.cpp:16 {{not visible}} exported = 1; // expected-error {{must be imported from module 'A'}} - // expected-note@p2.cpp:18 {{not visible}} + // expected-note@p2.cpp:18 {{declaration here is not visible}} - not_exported = 1; // expected-error {{undeclared identifier}} + not_exported = 1; // expected-error {{declaration of 'not_exported' must be imported from module 'A' before it is required}} + // expected-note@p2.cpp:19 {{declaration here is not visible}} - internal = 1; // expected-error {{undeclared identifier}} + // FIXME: We need better diagnostic message for static variable. + internal = 1; // expected-error {{declaration of 'internal' must be imported from module 'A' before it is required}} + // expected-note@p2.cpp:20 {{declaration here is not visible}} not_exported_private = 1; // expected-error {{undeclared identifier}} @@ -55,7 +58,7 @@ void test_late() { in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} - // expected-note@*{{not visible}} + // expected-note@* {{not visible}} global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}} // expected-note@p2.cpp:16 {{not visible}} @@ -64,14 +67,14 @@ not_exported = 1; #ifndef IMPLEMENTATION - // expected-error@-2 {{undeclared identifier 'not_exported'; did you mean 'exported'}} - // expected-note@p2.cpp:18 {{declared here}} + // expected-error@-2 {{declaration of 'not_exported' must be imported from module 'A' before it is required}} + // expected-note@p2.cpp:19 {{declaration here is not visible}} #endif internal = 1; #ifndef IMPLEMENTATION - // FIXME: should not be visible here - // expected-error@-3 {{undeclared identifier}} + // expected-error@-2 {{declaration of 'internal' must be imported from module 'A' before it is required}} + // expected-note@p2.cpp:20 {{declaration here is not visible}} #endif not_exported_private = 1; Index: clang/test/CXX/module/module.interface/Inputs/p7.cppm =================================================================== --- /dev/null +++ clang/test/CXX/module/module.interface/Inputs/p7.cppm @@ -0,0 +1,25 @@ +export module p7; +struct reachable { + constexpr static int sv = 43; + int value = 44; + + static int getValue() { return 43; } + int get() { return 44; } + + template + static bool templ_get(T t) { return false; } + typedef int typedef_type; + using using_type = int; + template + using templ_using_type = int; + bool operator()() { + return false; + } + + enum E { a, + b }; +}; + +export auto getReachable() { + return reachable{}; +} Index: clang/test/CXX/module/module.interface/p2.cpp =================================================================== --- clang/test/CXX/module/module.interface/p2.cpp +++ clang/test/CXX/module/module.interface/p2.cpp @@ -69,22 +69,29 @@ void use() { // namespace A is implicitly exported by the export of A::g. - A::f(); // expected-error {{no member named 'f' in namespace 'A'}} + A::f(); // expected-error {{declaration of 'f' must be imported from module 'p2' before it is required}} + // expected-note@* {{declaration here is not visible}} A::g(); - A::h(); // expected-error {{no member named 'h' in namespace 'A'}} - using namespace A::inner; // expected-error {{expected namespace name}} + A::h(); // expected-error {{declaration of 'h' must be imported from module 'p2' before it is required}} + // expected-note@* {{declaration here is not visible}} + using namespace A::inner; // expected-error {{declaration of 'inner' must be imported from module 'p2' before it is required}} + // expected-note@* {{declaration here is not visible}} // namespace B and B::inner are explicitly exported using namespace B; using namespace B::inner; - B::f(); // expected-error {{no member named 'f' in namespace 'B'}} - f(); // expected-error {{undeclared identifier 'f'}} + B::f(); // expected-error {{declaration of 'f' must be imported from module 'p2' before it is required}} + // expected-note@* {{declaration here is not visible}} + f(); // expected-error {{declaration of 'f' must be imported from module 'p2' before it is required}} + // expected-note@* {{declaration here is not visible}} // namespace C is not exported - using namespace C; // expected-error {{expected namespace name}} + using namespace C; // expected-error {{declaration of 'C' must be imported from module 'p2' before it is required}} + // expected-note@* {{declaration here is not visible}} // namespace D is exported, but D::f is not - D::f(); // expected-error {{no member named 'f' in namespace 'D'}} + D::f(); // expected-error {{declaration of 'f' must be imported from module 'p2' before it is required}} + // expected-note@* {{declaration here is not visible}} } int use_header() { return foo + bar::baz(); } Index: clang/test/CXX/module/module.interface/p7.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.interface/p7.cpp @@ -0,0 +1,19 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/p7.cppm --precompile -o %t/p7.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c +// expected-no-diagnostics +import p7; +void test() { + auto reachable = getReachable(); + int a = decltype(reachable)::sv; + int b = decltype(reachable)::getValue(); + int c = reachable.value; + int d = reachable.get(); + int e = decltype(reachable)::a; + int f = reachable.templ_get(a); + typename decltype(reachable)::typedef_type g; + typename decltype(reachable)::using_type h; + typename decltype(reachable)::template templ_using_type j; + auto value = reachable(); +} Index: clang/test/CXX/module/module.reach/Inputs/p4/bar.cppm =================================================================== --- /dev/null +++ clang/test/CXX/module/module.reach/Inputs/p4/bar.cppm @@ -0,0 +1,5 @@ +export module bar; +import foo; +export auto bar() { + return foo{}; +} \ No newline at end of file Index: clang/test/CXX/module/module.reach/Inputs/p4/foo.cppm =================================================================== --- /dev/null +++ clang/test/CXX/module/module.reach/Inputs/p4/foo.cppm @@ -0,0 +1,3 @@ +export module foo; +export class foo { +}; Index: clang/test/CXX/module/module.reach/Inputs/p5-A.cppm =================================================================== --- /dev/null +++ clang/test/CXX/module/module.reach/Inputs/p5-A.cppm @@ -0,0 +1,3 @@ +export module A; +struct X {}; +export using Y = X; Index: clang/test/CXX/module/module.reach/p4/TransitiveImport.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.reach/p4/TransitiveImport.cpp @@ -0,0 +1,15 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/foo.cppm -emit-module-interface -o %t/foo.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/bar.cppm -emit-module-interface -fprebuilt-module-path=%t -o %t/bar.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s +// expected-no-diagnostics +import bar; +auto foo() { + // [module.reach]Note1: + // While module interface units are reachable even when they + // are only transitively imported via a non-exported import declaration, + // namespace-scope names from such module interface units are not found + // by name lookup ([basic.lookup]). + auto b = bar(); // foo should be reachable here. +} \ No newline at end of file Index: clang/test/CXX/module/module.reach/p5.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.reach/p5.cpp @@ -0,0 +1,10 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/p5-A.cppm --precompile -o %t/A.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c -Xclang -verify + +export module B; +import A; +Y y; // OK, definition of X is reachable +X x; // expected-error {{declaration of 'X' must be imported from module 'A' before it is required}} + // expected-note@Inputs/p5-A.cppm:2 {{declaration here is not visible}} Index: clang/test/CXX/module/module.unit/p7/t6.cpp =================================================================== --- clang/test/CXX/module/module.unit/p7/t6.cpp +++ clang/test/CXX/module/module.unit/p7/t6.cpp @@ -2,14 +2,11 @@ // RUN: mkdir %t // RUN: %clang_cc1 -std=c++20 -emit-module-interface %S/Inputs/CPP.cppm -I%S/Inputs -o %t/X.pcm // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %s -verify +// expected-no-diagnostics module; #include "Inputs/h2.h" export module use; import X; void printX(CPP *cpp) { - cpp->print(); // expected-error {{'CPP' must be defined before it is used}} - // expected-error@-1 {{'CPP' must be defined before it is used}} - // expected-error@-2 {{no member named 'print' in 'CPP'}} - // expected-note@Inputs/CPP.cppm:5 {{definition here is not reachable}} - // expected-note@Inputs/CPP.cppm:5 {{definition here is not reachable}} + cpp->print(); } Index: clang/test/Modules/Inputs/Reachability-Private/Private.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-Private/Private.cppm @@ -0,0 +1,18 @@ +export module Private; +inline void fn_m(); // OK, module-linkage inline function +static void fn_s(); +export struct X; + +export void g(X *x) { + fn_s(); // OK, call to static function in same translation unit + fn_m(); // OK, call to module-linkage inline function +} +export X *factory(); // OK + +module : private; +struct X {}; // definition not reachable from importers of A +X *factory() { + return new X(); +} +void fn_m() {} +void fn_s() {} Index: clang/test/Modules/Inputs/Reachability-func-default-arg/func_default_arg.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-func-default-arg/func_default_arg.cppm @@ -0,0 +1,5 @@ +export module func_default_arg; +struct t {}; +export t foo(t t1 = t()) { + return t1; +} Index: clang/test/Modules/Inputs/Reachability-func-ret/func_ret.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-func-ret/func_ret.cppm @@ -0,0 +1,5 @@ +export module func_ret; +struct t {}; +export t foo() { + return t{}; +} Index: clang/test/Modules/Inputs/Reachability-template-default-arg/template_default_arg.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-template-default-arg/template_default_arg.cppm @@ -0,0 +1,7 @@ +export module template_default_arg; +struct t {}; + +export template +struct A { + T a; +}; Index: clang/test/Modules/Inputs/Reachability-template-instantiation/Templ.h =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-template-instantiation/Templ.h @@ -0,0 +1,8 @@ +#ifndef TEMPL_H +#define TEMPL_H +template +class Wrapper { +public: + T value; +}; +#endif Index: clang/test/Modules/Inputs/Reachability-template-instantiation/Templ.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-template-instantiation/Templ.cppm @@ -0,0 +1,6 @@ +export module Templ; +export template +class Wrapper2 { +public: + T value; +}; Index: clang/test/Modules/Inputs/Reachability-template-instantiation/Use.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-template-instantiation/Use.cppm @@ -0,0 +1,14 @@ +module; +#include "Templ.h" +export module Use; +import Templ; + +export template +class Use { +public: + Wrapper value; + Wrapper2 value2; +}; + +export template +Wrapper wrapper; Index: clang/test/Modules/Inputs/Reachability-using-templates/mod-templates.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-using-templates/mod-templates.cppm @@ -0,0 +1,3 @@ +export module mod.templates; +template struct t {}; +export template using u = t; Index: clang/test/Modules/Inputs/Reachability-using/mod.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/Reachability-using/mod.cppm @@ -0,0 +1,3 @@ +export module mod; +struct t {}; +export using u = t; Index: clang/test/Modules/Inputs/derived_class/bar.h =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/derived_class/bar.h @@ -0,0 +1,16 @@ +struct bar_base { + enum A { + a, + b, + c, + d + }; + constexpr static bool value = false; + static bool get() { return false; } + bool member_value = false; + bool get_func() { return false; } +}; + +template +struct bar : public bar_base { +}; Index: clang/test/Modules/Inputs/derived_class/foo.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/derived_class/foo.cppm @@ -0,0 +1,12 @@ +module; +#include "bar.h" +export module foo; +export template +int foo() { + bool a = bar::value; + bar::get(); + bar b; + b.member_value = a; + bool c = b.get_func(); + return bar::a; +} Index: clang/test/Modules/Inputs/explicitly-specialized-template/X.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/explicitly-specialized-template/X.cppm @@ -0,0 +1,12 @@ +module; +#include "foo.h" +export module X; +export template +class X { + foo x; + +public: + int print() { + return x.getInt(); + } +}; Index: clang/test/Modules/Inputs/explicitly-specialized-template/foo.h =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/explicitly-specialized-template/foo.h @@ -0,0 +1,16 @@ +#ifndef FOO_H +#define FOO_H +template +struct base {}; + +template +struct foo; + +template +struct foo {}; + +template <> +struct foo : base { + int getInt(); +}; +#endif // FOO_H Index: clang/test/Modules/Inputs/template-function-specialization/foo.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/template-function-specialization/foo.cppm @@ -0,0 +1,36 @@ +module; +# 3 __FILE__ 1 // use the next physical line number here (and below) +template +void foo() { +} + +template <> +void foo() { +} + +template +void foo2() { +} + +template <> +void foo2() { +} + +template +void foo3() { +} + +template <> +void foo3(); + +export module foo; +export using ::foo; +export using ::foo3; + +export template +void foo4() { +} + +export template <> +void foo4() { +} Index: clang/test/Modules/Inputs/template_default_argument/B.cppm =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/template_default_argument/B.cppm @@ -0,0 +1,8 @@ +module; +#include "templ.h" +export module B; +export template +templ bar() { + templ_func(); + return {}; +} Index: clang/test/Modules/Inputs/template_default_argument/templ.h =================================================================== --- /dev/null +++ clang/test/Modules/Inputs/template_default_argument/templ.h @@ -0,0 +1,4 @@ +template +class templ {}; +template +void templ_func() {} Index: clang/test/Modules/Reachability-Private.cpp =================================================================== --- /dev/null +++ clang/test/Modules/Reachability-Private.cpp @@ -0,0 +1,19 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/Reachability-Private/Private.cppm --precompile -o %t/Private.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c -Xclang -verify + +import Private; +void foo() { + X x; // expected-error {{definition of 'X' must be imported from module 'Private.' before it is required}} + // expected-error@-1 {{definition of 'X' must be imported from module 'Private.' before it is required}} + // expected-note@Inputs/Reachability-Private/Private.cppm:13 {{definition here is not reachable}} + // expected-note@Inputs/Reachability-Private/Private.cppm:13 {{definition here is not reachable}} + auto _ = factory(); + auto *__ = factory(); + X *___ = factory(); + + g(__); + g(___); + g(factory()); +} Index: clang/test/Modules/Reachability-func-default-arg.cpp =================================================================== --- /dev/null +++ clang/test/Modules/Reachability-func-default-arg.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/Reachability-func-default-arg/func_default_arg.cppm --precompile -o %t/func_default_arg.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c +// expected-no-diagnostics +import func_default_arg; +void bar() { + auto ret = foo(); +} Index: clang/test/Modules/Reachability-func-ret.cpp =================================================================== --- /dev/null +++ clang/test/Modules/Reachability-func-ret.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/Reachability-func-ret/func_ret.cppm --precompile -o %t/func_ret.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c +// expected-no-diagnostics +import func_ret; +void bar() { + auto ret = foo(); +} Index: clang/test/Modules/Reachability-template-default-arg.cpp =================================================================== --- /dev/null +++ clang/test/Modules/Reachability-template-default-arg.cpp @@ -0,0 +1,11 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/Reachability-template-default-arg/template_default_arg.cppm --precompile -o %t/template_default_arg.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c -Xclang -verify + +import template_default_arg; +void bar() { + A<> a0; + A a1; // expected-error {{declaration of 't' must be imported from module 'template_default_arg' before it is required}} + // expected-note@Inputs/Reachability-template-default-arg/template_default_arg.cppm:2 {{declaration here is not visible}} +} Index: clang/test/Modules/Reachability-template-instantiation.cpp =================================================================== --- /dev/null +++ clang/test/Modules/Reachability-template-instantiation.cpp @@ -0,0 +1,16 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/Reachability-template-instantiation/Templ.cppm --precompile -o %t/Templ.pcm +// RUN: %clang -std=c++20 %S/Inputs/Reachability-template-instantiation/Use.cppm -fprebuilt-module-path=%t --precompile -o %t/Use.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t -I%S/Inputs/Reachability-template-instantiation %s -c -Xclang -verify +// expected-no-diagnostics + +module; +#include "Templ.h" +export module User; + +export template +class User { +public: + Wrapper value; +}; Index: clang/test/Modules/Reachability-using-templates.cpp =================================================================== --- /dev/null +++ clang/test/Modules/Reachability-using-templates.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/Reachability-using-templates/mod-templates.cppm --precompile -o %t/mod.templates.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c +// expected-no-diagnostics +import mod.templates; +void foo() { + u v{}; +} Index: clang/test/Modules/Reachability-using.cpp =================================================================== --- /dev/null +++ clang/test/Modules/Reachability-using.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/Reachability-using/mod.cppm --precompile -o %t/mod.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c +// expected-no-diagnostics +import mod; +void foo() { + u v{}; +} Index: clang/test/Modules/cxx20-10-1-ex2.cpp =================================================================== --- clang/test/Modules/cxx20-10-1-ex2.cpp +++ clang/test/Modules/cxx20-10-1-ex2.cpp @@ -53,10 +53,12 @@ //--- std10-1-ex2-tu6.cpp import B; // error, n is module-local and this is not a module. -int &c = n; // expected-error {{use of undeclared identifier}} +int &c = n; // expected-error {{declaration of 'n' must be imported}} + // expected-note@* {{declaration here is not visible}} //--- std10-1-ex2-tu7.cpp module B:X3; // does not implicitly import B import :X2; // X2 is an implementation so exports nothing. // error: n not visible here. -int &c = n; // expected-error {{use of undeclared identifier }} +int &c = n; // expected-error {{declaration of 'n' must be imported}} + // expected-note@* {{declaration here is not visible}} Index: clang/test/Modules/derived_class.cpp =================================================================== --- /dev/null +++ clang/test/Modules/derived_class.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/derived_class/foo.cppm --precompile -o %t/foo.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c +// expected-no-diagnostics +import foo; +void test() { + foo(); +} Index: clang/test/Modules/explicitly-specialized-template.cpp =================================================================== --- /dev/null +++ clang/test/Modules/explicitly-specialized-template.cpp @@ -0,0 +1,12 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/explicitly-specialized-template/X.cppm --precompile -o %t/X.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -fsyntax-only -Xclang -verify -o - + +import X; +foo f; // expected-error {{'foo' must be declared before it is used}} + // expected-note@Inputs/explicitly-specialized-template/foo.h:10 {{declaration here is not visible}} +int bar() { + X x; + return x.print(); +} Index: clang/test/Modules/template-function-specialization.cpp =================================================================== --- /dev/null +++ clang/test/Modules/template-function-specialization.cpp @@ -0,0 +1,18 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %S/Inputs/template-function-specialization/foo.cppm -o %t/foo.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %s -verify -fsyntax-only +import foo; +void use() { + foo(); + foo(); + foo2(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}} + // expected-note@* {{declaration here is not visible}} + foo2(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}} + // expected-note@* {{declaration here is not visible}} + foo3(); + foo3(); + + foo4(); + foo4(); +} Index: clang/test/Modules/template_default_argument.cpp =================================================================== --- /dev/null +++ clang/test/Modules/template_default_argument.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang -std=c++20 %S/Inputs/template_default_argument/B.cppm --precompile -o %t/B.pcm +// RUN: %clang -std=c++20 -fprebuilt-module-path=%t %s -c -o - +// expected-no-diagnostics +import B; +auto foo() { + return bar(); +}