diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -225,8 +225,15 @@ /// module is imported. VisibleWhenImported, + /// This declaration has an owning module, and is visible to lookups + /// that occurs within that module. And it is reachable in other module + /// when the owning module is transitively 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; @@ -622,6 +629,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; @@ -799,6 +814,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() { diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -170,6 +170,8 @@ bool isPrivateModule() const { return Kind == PrivateModuleFragment; } + bool isModuleMapModule() const { return Kind == ModuleMapModule; } + private: /// The submodules of this module, indexed by name. std::vector SubModules; @@ -536,6 +538,10 @@ return Kind == ModuleInterfaceUnit || isModulePartition(); } + bool isModuleInterfaceUnit() const { + return Kind == ModuleInterfaceUnit || Kind == ModulePartitionInterface; + } + /// Get the primary module interface name from a partition. StringRef getPrimaryModuleInterfaceName() const { // Technically, global module fragment belongs to global module. And global diff --git a/clang/include/clang/Sema/Lookup.h b/clang/include/clang/Sema/Lookup.h --- a/clang/include/clang/Sema/Lookup.h +++ b/clang/include/clang/Sema/Lookup.h @@ -346,30 +346,56 @@ /// Determine whether the given declaration is visible to the /// program. - static bool isVisible(Sema &SemaRef, NamedDecl *D) { - // If this declaration is not hidden, it's visible. - if (D->isUnconditionallyVisible()) - return true; + static bool isVisible(Sema &SemaRef, NamedDecl *D); - // During template instantiation, we can refer to hidden declarations, if - // they were visible in any module along the path of instantiation. - return isVisibleSlow(SemaRef, D); + static bool isReachable(Sema &SemaRef, NamedDecl *D); + + static bool isAcceptable(Sema &SemaRef, NamedDecl *D, + Sema::AcceptableKind Kind) { + return Kind == Sema::AcceptableKind::Visible ? isVisible(SemaRef, D) + : isReachable(SemaRef, D); } + /// Determine whether this lookup is permitted to see the declaration. + /// Note that a reachable but not visible declaration inhabiting a 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 isAvailableForLookup would return false when we look + /// up 'reachable' here. + /// // return reachable(43).v; + /// // Valid. The field name 'v' is allowed during name lookup. + /// // So isAvailableForLookup would return true when we look up 'v' here. + /// return func().v; + /// } + /// ``` + static bool isAvailableForLookup(Sema &SemaRef, NamedDecl *ND); + /// Retrieve the accepted (re)declaration of the given declaration, /// if there is one. NamedDecl *getAcceptableDecl(NamedDecl *D) const { if (!D->isInIdentifierNamespace(IDNS)) return nullptr; - if (isVisible(getSema(), D) || isHiddenDeclarationVisible(D)) + if (isAvailableForLookup(getSema(), D) || isHiddenDeclarationVisible(D)) return D; return getAcceptableDeclSlow(D); } private: - static bool isVisibleSlow(Sema &SemaRef, NamedDecl *D); + static bool isAcceptableSlow(Sema &SemaRef, NamedDecl *D, + Sema::AcceptableKind Kind); + static bool isReachableSlow(Sema &SemaRef, NamedDecl *D); NamedDecl *getAcceptableDeclSlow(NamedDecl *D) const; public: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -363,8 +363,6 @@ static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD); - bool isVisibleSlow(const NamedDecl *D); - /// Determine whether two declarations should be linked together, given that /// the old declaration might not be visible and the new declaration might /// not have external linkage. @@ -2218,6 +2216,8 @@ Default = AcceptSizeless }; + enum class AcceptableKind { Visible, Reachable }; + private: /// Methods for marking which expressions involve dereferencing a pointer /// marked with the 'noderef' attribute. Expressions are checked bottom up as @@ -2252,11 +2252,6 @@ /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet DeferredExportedNamespaces; - /// Get the module unit whose scope we are currently within. - Module *getCurrentModule() const { - return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; - } - /// Helper function to judge if we are in module purview. /// Return false if we are not in a module. bool isCurrentModulePurview() const { @@ -2275,7 +2270,14 @@ bool isUsableModule(const Module *M); + bool isAcceptableSlow(const NamedDecl *D, AcceptableKind Kind); + public: + /// Get the module unit whose scope we are currently within. + Module *getCurrentModule() const { + return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; + } + /// Get the module owning an entity. Module *getOwningModule(const Decl *Entity) { return Entity->getOwningModule(); @@ -2299,7 +2301,20 @@ /// Determine whether a declaration is visible to name lookup. bool isVisible(const NamedDecl *D) { - return D->isUnconditionallyVisible() || isVisibleSlow(D); + return D->isUnconditionallyVisible() || + isAcceptableSlow(D, AcceptableKind::Visible); + } + + /// Determine whether a declaration is reachable. + bool isReachable(const NamedDecl *D) { + // All visible declarations are reachable. + return D->isUnconditionallyVisible() || + isAcceptableSlow(D, AcceptableKind::Reachable); + } + + /// Determine whether a declaration is acceptable (visible/reachable). + bool isAcceptable(const NamedDecl *D, AcceptableKind Kind) { + return Kind == AcceptableKind::Visible ? isVisible(D) : isReachable(D); } /// Determine whether any declaration of an entity is visible. @@ -2308,8 +2323,17 @@ llvm::SmallVectorImpl *Modules = nullptr) { return isVisible(D) || hasVisibleDeclarationSlow(D, Modules); } + bool hasVisibleDeclarationSlow(const NamedDecl *D, llvm::SmallVectorImpl *Modules); + /// Determine whether any declaration of an entity is reachable. + bool + hasReachableDeclaration(const NamedDecl *D, + llvm::SmallVectorImpl *Modules = nullptr) { + return isReachable(D) || hasReachableDeclarationSlow(D, Modules); + } + bool hasReachableDeclarationSlow( + const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); bool hasVisibleMergedDefinition(NamedDecl *Def); bool hasMergedDefinitionInCurrentModule(NamedDecl *Def); @@ -2327,21 +2351,54 @@ 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); + } + + bool hasAcceptableDefinition(NamedDecl *D, NamedDecl **Suggested, + AcceptableKind Kind, + bool OnlyNeedComplete = false); + bool hasAcceptableDefinition(NamedDecl *D, AcceptableKind Kind) { + NamedDecl *Hidden; + return hasAcceptableDefinition(D, &Hidden, Kind); + } + /// 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 the template parameter \p D has a reachable default argument. + bool hasAcceptableDefaultArgument(const NamedDecl *D, + llvm::SmallVectorImpl *Modules, + Sema::AcceptableKind Kind); /// 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 @@ -3100,8 +3157,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 { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -588,6 +588,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: diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/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(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16310,7 +16310,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); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17943,7 +17943,7 @@ 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 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1607,16 +1607,17 @@ return false; } -template +template static bool -hasVisibleDefaultArgument(Sema &S, const ParmDecl *D, - llvm::SmallVectorImpl *Modules) { +hasAcceptableDefaultArgument(Sema &S, const ParmDecl *D, + llvm::SmallVectorImpl *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 @@ return false; } -bool Sema::hasVisibleDefaultArgument(const NamedDecl *D, - llvm::SmallVectorImpl *Modules) { +bool Sema::hasAcceptableDefaultArgument( + const NamedDecl *D, llvm::SmallVectorImpl *Modules, + Sema::AcceptableKind Kind) { if (auto *P = dyn_cast(D)) - return ::hasVisibleDefaultArgument(*this, P, Modules); + return ::hasAcceptableDefaultArgument(*this, P, Modules, Kind); + if (auto *P = dyn_cast(D)) - return ::hasVisibleDefaultArgument(*this, P, Modules); - return ::hasVisibleDefaultArgument(*this, cast(D), - Modules); + return ::hasAcceptableDefaultArgument(*this, P, Modules, Kind); + + return ::hasAcceptableDefaultArgument( + *this, cast(D), Modules, Kind); +} + +bool Sema::hasVisibleDefaultArgument(const NamedDecl *D, + llvm::SmallVectorImpl *Modules) { + return hasAcceptableDefaultArgument(D, Modules, + Sema::AcceptableKind::Visible); +} + +bool Sema::hasReachableDefaultArgument( + const NamedDecl *D, llvm::SmallVectorImpl *Modules) { + return hasAcceptableDefaultArgument(D, Modules, + Sema::AcceptableKind::Reachable); } -template -static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D, - llvm::SmallVectorImpl *Modules, - Filter F) { +template +static bool +hasAcceptableDeclarationImpl(Sema &S, const NamedDecl *D, + llvm::SmallVectorImpl *Modules, Filter F, + Sema::AcceptableKind Kind) { bool HasFilteredRedecls = false; for (auto *Redecl : D->redecls()) { @@ -1651,7 +1668,7 @@ if (!F(R)) continue; - if (S.isVisible(R)) + if (S.isAcceptable(R, Kind)) return true; HasFilteredRedecls = true; @@ -1667,74 +1684,115 @@ return true; } +static bool +hasAcceptableExplicitSpecialization(Sema &S, const NamedDecl *D, + llvm::SmallVectorImpl *Modules, + Sema::AcceptableKind Kind) { + return hasAcceptableDeclarationImpl( + S, 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"); + }, + Kind); +} + bool Sema::hasVisibleExplicitSpecialization( 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"); - }); + return ::hasAcceptableExplicitSpecialization(*this, D, Modules, + Sema::AcceptableKind::Visible); } -bool Sema::hasVisibleMemberSpecialization( +bool Sema::hasReachableExplicitSpecialization( const NamedDecl *D, llvm::SmallVectorImpl *Modules) { + return ::hasAcceptableExplicitSpecialization(*this, D, Modules, + Sema::AcceptableKind::Reachable); +} + +static bool +hasAcceptableMemberSpecialization(Sema &S, const NamedDecl *D, + llvm::SmallVectorImpl *Modules, + Sema::AcceptableKind Kind) { 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(); - }); + 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 *Modules) { + return hasAcceptableMemberSpecialization(*this, D, Modules, + Sema::AcceptableKind::Visible); +} + +bool Sema::hasReachableMemberSpecialization( + const NamedDecl *D, llvm::SmallVectorImpl *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(DC) || isa(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(DC)) { @@ -1745,41 +1803,63 @@ } } if (SearchDefinitions) - VisibleWithinParent = SemaRef.hasVisibleDefinition(cast(DC)); + AcceptableWithinParent = + SemaRef.hasAcceptableDefinition(cast(DC), Kind); else - VisibleWithinParent = isVisible(SemaRef, cast(DC)); + AcceptableWithinParent = + isAcceptable(SemaRef, cast(DC), Kind); } else if (isa(D) || (isa(DC) && !SemaRef.getLangOpts().CPlusPlus)) - VisibleWithinParent = isVisible(SemaRef, cast(DC)); + AcceptableWithinParent = isAcceptable(SemaRef, cast(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(DC))) { - VisibleWithinParent = true; + AcceptableWithinParent = true; break; } DC = DC->getLexicalParent(); } while (!IsEffectivelyFileContext(DC)); } else { - VisibleWithinParent = SemaRef.hasVisibleDefinition(cast(DC)); + AcceptableWithinParent = + SemaRef.hasAcceptableDefinition(cast(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::isVisibleSlow(const NamedDecl *D) { - return LookupResult::isVisible(*this, const_cast(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(D), Kind); } bool Sema::shouldLinkPossiblyHiddenDecl(LookupResult &R, const NamedDecl *New) { @@ -1862,7 +2008,7 @@ /// 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 @@ // 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 @@ bool Sema::hasVisibleDeclarationSlow(const NamedDecl *D, llvm::SmallVectorImpl *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 *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 @@ 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(DC)) + return SemaRef.hasReachableDefinition(TD); + + return false; +} + /// Perform unqualified name lookup starting from a given /// scope. /// @@ -3641,7 +3850,16 @@ } } 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, + // + // 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 --- a/clang/lib/Sema/SemaModule.cpp +++ b/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. @@ -325,10 +332,12 @@ 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 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -799,8 +799,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) @@ -5255,7 +5256,7 @@ HasDefaultArg = false; if (TemplateTypeParmDecl *TypeParm = dyn_cast(Param)) { - if (!hasVisibleDefaultArgument(TypeParm)) + if (!hasReachableDefaultArgument(TypeParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5272,7 +5273,7 @@ if (NonTypeTemplateParmDecl *NonTypeParm = dyn_cast(Param)) { - if (!hasVisibleDefaultArgument(NonTypeParm)) + if (!hasReachableDefaultArgument(NonTypeParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5290,7 +5291,7 @@ TemplateTemplateParmDecl *TempTempParm = cast(Param); - if (!hasVisibleDefaultArgument(TempTempParm)) + if (!hasReachableDefaultArgument(TempTempParm)) return TemplateArgumentLoc(); HasDefaultArg = true; @@ -5628,10 +5629,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, @@ -5814,7 +5815,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); @@ -5831,7 +5832,7 @@ ArgType); } else if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*Param)) { - if (!hasVisibleDefaultArgument(NTTP)) + if (!hasReachableDefaultArgument(NTTP)) return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP, NewArgs); @@ -5849,7 +5850,7 @@ TemplateTemplateParmDecl *TempParm = cast(*Param); - if (!hasVisibleDefaultArgument(TempParm)) + if (!hasReachableDefaultArgument(TempParm)) return diagnoseMissingArgument(*this, TemplateLoc, Template, TempParm, NewArgs); @@ -11006,10 +11007,12 @@ Sema &S; SourceLocation Loc; llvm::SmallVector 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(ND)) @@ -11037,6 +11040,23 @@ 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 @@ 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 @@ checkTemplate(TD); else if (auto *TD = From.dyn_cast()) { - if (!S.hasVisibleDeclaration(TD)) + if (!CheckDeclaration(TD)) diagnose(TD, true); checkTemplate(TD); } @@ -11096,7 +11115,7 @@ checkTemplate(TD); else if (auto *TD = From.dyn_cast()) { - if (!S.hasVisibleDeclaration(TD)) + if (!CheckDeclaration(TD)) diagnose(TD, true); checkTemplate(TD); } @@ -11107,7 +11126,7 @@ template void checkTemplate(TemplDecl *TD) { if (TD->isMemberSpecialization()) { - if (!S.hasVisibleMemberSpecialization(TD, &Modules)) + if (!CheckMemberSpecialization(TD)) diagnose(TD->getMostRecentDecl(), false); } } @@ -11118,5 +11137,17 @@ 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 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8642,17 +8642,8 @@ 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 @@ 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 @@ 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 @@ // 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; diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -605,15 +605,27 @@ 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()) { + + switch (ModuleOwnership) { + case Decl::ModuleOwnershipKind::Visible: + ModuleOwnership = Decl::ModuleOwnershipKind::VisibleWhenImported; + break; + case Decl::ModuleOwnershipKind::Unowned: + case Decl::ModuleOwnershipKind::VisibleWhenImported: + case Decl::ModuleOwnershipKind::ReachableWhenImported: + case Decl::ModuleOwnershipKind::ModulePrivate: + break; + } + + 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) { diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -311,7 +311,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 @@ -1928,7 +1928,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -1961,7 +1961,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -1999,7 +1999,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2049,7 +2049,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2111,7 +2111,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2159,7 +2159,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2188,7 +2188,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(0)); // NameKind = Identifier @@ -2240,7 +2240,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)); // ModuleOwnershipKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID // NamedDecl Abv->Add(BitCodeAbbrevOp(DeclarationName::Identifier)); // NameKind diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp @@ -0,0 +1,31 @@ +// 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: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/Friend-in-reachable-class.cppm -o %t/X.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only +// +//--- Friend-in-reachable-class.cppm +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; + +//--- Use.cpp +// expected-no-diagnostics +import X; +int use() { + A a, b; + return a + b; +} diff --git a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp --- a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp +++ b/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; diff --git a/clang/test/CXX/module/module.context/p7.cpp b/clang/test/CXX/module/module.context/p7.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.context/p7.cpp @@ -0,0 +1,66 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t + +// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header-unit-header std-10-6-ex1-decl.h \ +// RUN: -o decl.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header-unit-header std-10-6-ex1-defn.h \ +// RUN: -o defn.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface std-10-6-ex1-stuff.cpp \ +// RUN: -o stuff.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface std-10-6-ex1-M1.cpp \ +// RUN: -fmodule-file=stuff.pcm -o M1.pcm -fmodule-file=defn.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface std-10-6-ex1-M2.cpp \ +// RUN: -fmodule-file=stuff.pcm -o M2.pcm -fmodule-file=decl.pcm + +// RUN: %clang_cc1 -std=c++20 std-10-6-ex1-use.cpp \ +// RUN: -fmodule-file=M1.pcm -fmodule-file=M2.pcm -fsyntax-only -verify + +//--- std-10-6-ex1-decl.h +struct X; + +//--- std-10-6-ex1-defn.h +struct X {}; + +//--- std-10-6-ex1-stuff.cpp +export module stuff; +export template void foo(T, U u) { auto v = u; } +export template void bar(T, U u) { auto v = *u; } + +//--- std-10-6-ex1-M1.cpp +export module M1; +import "std-10-6-ex1-defn.h"; // provides struct X {}; +import stuff; + +export template void f(T t) { + X x; + foo(t, x); +} + +//--- std-10-6-ex1-M2.cpp +export module M2; +import "std-10-6-ex1-decl.h"; // provides struct X; (not a definition) + +import stuff; +export template void g(T t) { + X *x; + bar(t, x); +} + +//--- std-10-6-ex1-use.cpp +import M1; +import M2; + +void test() { + f(0); + // It is unspecified whether the instantiation of g(0) is valid here. + // We choose to make it invalid here. + g(0); // expected-error@* {{definition of 'X' must be imported from module}} + // expected-note@* {{in instantiation of function template specialization 'bar'}} + // expected-note@* {{in instantiation of function template specialization}} + // expected-note@* {{definition here is not reachable}} +} diff --git a/clang/test/CXX/module/module.import/p2.cpp b/clang/test/CXX/module/module.import/p2.cpp --- a/clang/test/CXX/module/module.import/p2.cpp +++ b/clang/test/CXX/module/module.import/p2.cpp @@ -23,7 +23,10 @@ //--- Use.cpp import M; void test() { - A a; // expected-error {{unknown type name 'A'}} + A a; // expected-error {{declaration of 'A' must be imported from module 'M:impl'}} + // expected-error@-1 {{definition of 'A' must be imported from module 'M:impl'}} expected-error@-1 {{}} + // expected-note@impl.cppm:2 {{declaration here is not visible}} + // expected-note@impl.cppm:2 {{definition here is not reachable}} expected-note@impl.cppm:2 {{}} } //--- UseInPartA.cppm @@ -38,7 +41,10 @@ export module B; import M; void test() { - A a; // expected-error {{unknown type name 'A'}} + A a; // expected-error {{declaration of 'A' must be imported from module 'M:impl'}} + // expected-error@-1 {{definition of 'A' must be imported from module 'M:impl'}} expected-error@-1 {{}} + // expected-note@impl.cppm:2 {{declaration here is not visible}} + // expected-note@impl.cppm:2 {{definition here is not reachable}} expected-note@impl.cppm:2 {{}} } //--- Private.cppm diff --git a/clang/test/CXX/module/module.interface/p2.cpp b/clang/test/CXX/module/module.interface/p2.cpp --- a/clang/test/CXX/module/module.interface/p2.cpp +++ b/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(); } diff --git a/clang/test/CXX/module/module.interface/p7.cpp b/clang/test/CXX/module/module.interface/p7.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.interface/p7.cpp @@ -0,0 +1,68 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/p7.cppm -emit-module-interface -o %t/p7.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify + +//--- p7.cppm +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{}; +} + +export enum E1 { e1 }; +enum E2 { e2 }; +export using E2U = E2; +enum E3 { e3 }; +export E3 func(); + +//--- Use.cpp +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(); +} + +void test2() { + auto a = E1::e1; // OK, namespace-scope name E1 is visible and e1 is reachable + auto b = e1; // OK, namespace-scope name e1 is visible + auto c = E2::e2; // expected-error {{declaration of 'E2' must be imported from module}} + // expected-note@* {{declaration here is not visible}} + auto d = e2; // should be error, namespace-scope name e2 is not visible + auto e = E2U::e2; // OK, namespace-scope name E2U is visible and E2::e2 is reachable + auto f = E3::e3; // expected-error {{declaration of 'E3' must be imported from module 'p7' before it is required}} + // expected-note@* {{declaration here is not visible}} + auto g = e3; // should be error, namespace-scope name e3 is not visible + auto h = decltype(func())::e3; // OK, namespace-scope name f is visible and E3::e3 is reachable +} diff --git a/clang/test/CXX/module/module.reach/ex1.cpp b/clang/test/CXX/module/module.reach/ex1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/ex1.cpp @@ -0,0 +1,43 @@ +// From [module.reach]p4, example 1 +// +// RUN: rm -fr %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/M-A.cppm -o %t/M-A.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/M-B-impl.cppm -o %t/M-B.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/M-C.cppm -fsyntax-only -verify +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/M.cppm -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/X.cppm -fsyntax-only -verify +// +//--- M-A.cppm +export module M:A; +export struct B; + +//--- M-B-impl.cppm +module M:B; +struct B { + operator int(); +}; + +//--- M-C.cppm +module M:C; +import :A; +B b1; // expected-error {{variable has incomplete type 'B'}} + // expected-note@* {{forward declaration of 'B'}} + +//--- M.cppm +export module M; +export import :A; +import :B; +B b2; +export void f(B b = B()); + +//--- X.cppm +export module X; +import M; +B b3; // expected-error {{definition of 'B' must be imported from module 'M:B' before it is required}} expected-error {{}} + // expected-note@* {{definition here is not reachable}} expected-note@* {{}} +// FIXME: We should emit an error for unreachable definition of B. +void g() { f(); } diff --git a/clang/test/CXX/module/module.reach/p2.cpp b/clang/test/CXX/module/module.reach/p2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/p2.cpp @@ -0,0 +1,22 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -std=c++20 %t/impl.cppm -emit-module-interface -o %t/M-impl.pcm +// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-module-interface -fprebuilt-module-path=%t -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 %t/UseStrict.cpp -fprebuilt-module-path=%t -verify -fsyntax-only + +//--- impl.cppm +module M:impl; +class A {}; + +//--- M.cppm +export module M; +import :impl; +export A f(); + +//--- UseStrict.cpp +import M; +void test() { + auto a = f(); // expected-error {{definition of 'A' must be imported from module 'M:impl' before it is required}} expected-error{{}} + // expected-note@* {{definition here is not reachable}} expected-note@* {{}} +} diff --git a/clang/test/CXX/module/module.reach/p4/TransitiveImport.cpp b/clang/test/CXX/module/module.reach/p4/TransitiveImport.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/p4/TransitiveImport.cpp @@ -0,0 +1,31 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/foo.cppm -emit-module-interface -o %t/foo.pcm +// RUN: %clang_cc1 -std=c++20 %t/bar.cppm -emit-module-interface -fprebuilt-module-path=%t -o %t/bar.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %t/Use.cpp -fsyntax-only +// +//--- foo.cppm +export module foo; +export class foo { +}; + +//--- bar.cppm +export module bar; +import foo; +export auto bar() { + return foo{}; +} + +//--- Use.cpp +// 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. +} diff --git a/clang/test/CXX/module/module.reach/p5.cpp b/clang/test/CXX/module/module.reach/p5.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/p5.cpp @@ -0,0 +1,18 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/B.cppm -fsyntax-only -verify +// +//--- A.cppm +export module A; +struct X {}; +export using Y = X; + +//--- B.cppm +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@* {{declaration here is not visible}} diff --git a/clang/test/CXX/module/module.unit/p7/t6.cpp b/clang/test/CXX/module/module.unit/p7/t6.cpp --- a/clang/test/CXX/module/module.unit/p7/t6.cpp +++ b/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(); } diff --git a/clang/test/CXX/modules-ts/basic/basic.link/p2/other.cpp b/clang/test/CXX/modules-ts/basic/basic.link/p2/other.cpp --- a/clang/test/CXX/modules-ts/basic/basic.link/p2/other.cpp +++ b/clang/test/CXX/modules-ts/basic/basic.link/p2/other.cpp @@ -4,12 +4,13 @@ void use_from_module_impl() { external_linkage_fn(); - module_linkage_fn(); // expected-error {{undeclared identifier}} - internal_linkage_fn(); // expected-error {{undeclared identifier}} + module_linkage_fn(); // expected-error {{declaration of 'module_linkage_fn' must be imported}} + internal_linkage_fn(); // expected-error {{declaration of 'internal_linkage_fn' must be imported}} (void)external_linkage_class{}; (void)module_linkage_class{}; // expected-error {{undeclared identifier}} expected-error 0+{{}} (void)internal_linkage_class{}; // expected-error {{undeclared identifier}} expected-error 0+{{}} - // expected-note@module.cppm:9 {{here}} + // expected-note@module.cppm:10 {{declaration here is not visible}} + // expected-note@module.cppm:11 {{declaration here is not visible}} (void)external_linkage_var; (void)module_linkage_var; // expected-error {{undeclared identifier}} (void)internal_linkage_var; // expected-error {{undeclared identifier}} diff --git a/clang/test/Modules/Reachability-Private.cpp b/clang/test/Modules/Reachability-Private.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Reachability-Private.cpp @@ -0,0 +1,44 @@ +// Tests that the definition in private module fragment is not reachable to its users. +// +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/Private.cppm -emit-module-interface -o %t/Private.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only + +//--- Private.cppm +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() {} + +//--- Use.cpp +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@* {{definition here is not reachable}} + // expected-note@* {{definition here is not reachable}} + auto _ = factory(); + auto *__ = factory(); + X *___ = factory(); + + g(__); + g(___); + g(factory()); +} diff --git a/clang/test/Modules/Reachability-func-default-arg.cpp b/clang/test/Modules/Reachability-func-default-arg.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Reachability-func-default-arg.cpp @@ -0,0 +1,20 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/func_default_arg.cppm -emit-module-interface -o %t/func_default_arg.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only +// +//--- func_default_arg.cppm +export module func_default_arg; +struct t {}; +export t foo(t t1 = t()) { + return t1; +} + +//--- Use.cpp +// expected-no-diagnostics +import func_default_arg; +void bar() { + auto ret = foo(); +} diff --git a/clang/test/Modules/Reachability-func-ret.cpp b/clang/test/Modules/Reachability-func-ret.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Reachability-func-ret.cpp @@ -0,0 +1,20 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/func_ret.cppm -emit-module-interface -o %t/func_ret.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only +// +//--- func_ret.cppm +export module func_ret; +struct t {}; +export t foo() { + return t{}; +} + +//--- Use.cpp +// expected-no-diagnostics +import func_ret; +void bar() { + auto ret = foo(); +} diff --git a/clang/test/Modules/Reachability-template-default-arg.cpp b/clang/test/Modules/Reachability-template-default-arg.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Reachability-template-default-arg.cpp @@ -0,0 +1,23 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/template_default_arg.cppm -emit-module-interface -o %t/template_default_arg.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify +// +//--- template_default_arg.cppm +export module template_default_arg; +struct t {}; + +export template +struct A { + T a; +}; + +//--- Use.cpp +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@* {{declaration here is not visible}} +} diff --git a/clang/test/Modules/Reachability-template-instantiation.cpp b/clang/test/Modules/Reachability-template-instantiation.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Reachability-template-instantiation.cpp @@ -0,0 +1,53 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-module-interface -o %t/Templ.pcm +// RUN: %clang_cc1 -std=c++20 %t/Use.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/Use.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use.cpp -verify -fsyntax-only +// +//--- Templ.h +#ifndef TEMPL_H +#define TEMPL_H +template +class Wrapper { +public: + T value; +}; +#endif + +//--- Templ.cppm +export module Templ; +export template +class Wrapper2 { +public: + T value; +}; + +//--- Use.cppm +module; +#include "Templ.h" +export module Use; +import Templ; + +export template +class Use { +public: + Wrapper value; + Wrapper2 value2; +}; + +export template +Wrapper wrapper; + +//--- Use.cpp +// expected-no-diagnostics +module; +#include "Templ.h" +export module User; + +export template +class User { +public: + Wrapper value; +}; diff --git a/clang/test/Modules/Reachability-using-templates.cpp b/clang/test/Modules/Reachability-using-templates.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Reachability-using-templates.cpp @@ -0,0 +1,18 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/mod.templates.cppm -emit-module-interface -o %t/mod.templates.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify +// +//--- mod.templates.cppm +export module mod.templates; +template struct t {}; +export template using u = t; + +//--- Use.cpp +// expected-no-diagnostics +import mod.templates; +void foo() { + u v{}; +} diff --git a/clang/test/Modules/Reachability-using.cpp b/clang/test/Modules/Reachability-using.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Reachability-using.cpp @@ -0,0 +1,18 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/mod.cppm -emit-module-interface -o %t/mod.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify +// +//--- mod.cppm +export module mod; +struct t {}; +export using u = t; + +//--- Use.cpp +// expected-no-diagnostics +import mod; +void foo() { + u v{}; +} diff --git a/clang/test/Modules/cxx20-10-1-ex2.cpp b/clang/test/Modules/cxx20-10-1-ex2.cpp --- a/clang/test/Modules/cxx20-10-1-ex2.cpp +++ b/clang/test/Modules/cxx20-10-1-ex2.cpp @@ -53,7 +53,8 @@ //--- 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 // expected-no-diagnostics diff --git a/clang/test/Modules/derived_class.cpp b/clang/test/Modules/derived_class.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/derived_class.cpp @@ -0,0 +1,45 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/foo.cppm -emit-module-interface -o %t/foo.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify +// +//--- bar.h +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 { +}; + +//--- foo.cppm +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; +} + +//--- Use.cpp +// expected-no-diagnostics +import foo; +void test() { + foo(); +} diff --git a/clang/test/Modules/explicitly-specialized-template.cpp b/clang/test/Modules/explicitly-specialized-template.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/explicitly-specialized-template.cpp @@ -0,0 +1,47 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/X.cppm -emit-module-interface -o %t/X.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify +// +//--- foo.h +#ifndef FOO_H +#define FOO_H +template +struct base {}; + +template +struct foo; + +template +struct foo {}; + +template <> +struct foo : base { + int getInt(); +}; +#endif // FOO_H + +//--- X.cppm +module; +#include "foo.h" +export module X; +export template +class X { + foo x; + +public: + int print() { + return x.getInt(); + } +}; + +//--- Use.cpp +import X; +foo f; // expected-error {{'foo' must be declared before it is used}} + // expected-note@* {{declaration here is not visible}} +int bar() { + X x; + return x.print(); +} diff --git a/clang/test/Modules/module-private.cpp b/clang/test/Modules/module-private.cpp --- a/clang/test/Modules/module-private.cpp +++ b/clang/test/Modules/module-private.cpp @@ -21,8 +21,8 @@ vector vec; // expected-error{{no template named 'vector'}} VisibleStruct vs; - vs.field = 0; // expected-error{{no member named 'field' in 'VisibleStruct'}} - vs.setField(1); // expected-error{{no member named 'setField' in 'VisibleStruct'}} + vs.field = 0; + vs.setField(1); return hidden_var; // expected-error{{use of undeclared identifier 'hidden_var'}} } diff --git a/clang/test/Modules/template-function-specialization.cpp b/clang/test/Modules/template-function-specialization.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/template-function-specialization.cpp @@ -0,0 +1,60 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/foo.cppm -o %t/foo.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only +// +//--- foo.cppm +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() { +} + +//--- Use.cpp +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(); +} diff --git a/clang/test/Modules/template_default_argument.cpp b/clang/test/Modules/template_default_argument.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/template_default_argument.cpp @@ -0,0 +1,29 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-module-interface -o %t/B.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify +// +//--- templ.h +template +class templ {}; +template +void templ_func() {} + +//--- B.cppm +module; +#include "templ.h" +export module B; +export template +templ bar() { + templ_func(); + return {}; +} + +//--- Use.cpp +// expected-no-diagnostics +import B; +auto foo() { + return bar(); +}