[clang] a223ba0 - [C++20] [Modules] Implement Reachable initiallly
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 28 21:43:06 PDT 2022
Author: Chuanqi Xu
Date: 2022-06-29T12:32:31+08:00
New Revision: a223ba0a697c1598b434cf2495c9cd9ec5640fc7
URL: https://github.com/llvm/llvm-project/commit/a223ba0a697c1598b434cf2495c9cd9ec5640fc7
DIFF: https://github.com/llvm/llvm-project/commit/a223ba0a697c1598b434cf2495c9cd9ec5640fc7.diff
LOG: [C++20] [Modules] Implement Reachable initiallly
Added:
clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp
clang/test/CXX/module/module.context/p7.cpp
clang/test/CXX/module/module.interface/p7.cpp
clang/test/CXX/module/module.reach/ex1.cpp
clang/test/CXX/module/module.reach/p2.cpp
clang/test/CXX/module/module.reach/p4/TransitiveImport.cpp
clang/test/CXX/module/module.reach/p5.cpp
clang/test/Modules/Reachability-Private.cpp
clang/test/Modules/Reachability-func-default-arg.cpp
clang/test/Modules/Reachability-func-ret.cpp
clang/test/Modules/Reachability-template-default-arg.cpp
clang/test/Modules/Reachability-template-instantiation.cpp
clang/test/Modules/Reachability-using-templates.cpp
clang/test/Modules/Reachability-using.cpp
clang/test/Modules/derived_class.cpp
clang/test/Modules/explicitly-specialized-template.cpp
clang/test/Modules/template-function-specialization.cpp
clang/test/Modules/template_default_argument.cpp
Modified:
clang/include/clang/AST/DeclBase.h
clang/include/clang/Basic/Module.h
clang/include/clang/Sema/Lookup.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/Decl.cpp
clang/lib/Sema/SemaCXXScopeSpec.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaLookup.cpp
clang/lib/Sema/SemaModule.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaType.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
clang/test/CXX/module/module.import/p2.cpp
clang/test/CXX/module/module.interface/p2.cpp
clang/test/CXX/module/module.unit/p7/t6.cpp
clang/test/CXX/modules-ts/basic/basic.link/p2/other.cpp
clang/test/Modules/cxx20-10-1-ex2.cpp
clang/test/Modules/module-private.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 0611cf5ccb000..52fe8dd6b1e57 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -225,8 +225,15 @@ class alignas(8) Decl {
/// 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 @@ class alignas(8) Decl {
/// 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<Decl *, 2, ModuleOwnershipKind> NextInContextAndBits;
+ /// The extra three bits are used for the ModuleOwnershipKind.
+ llvm::PointerIntPair<Decl *, 3, ModuleOwnershipKind> NextInContextAndBits;
private:
friend class DeclContext;
@@ -622,6 +629,14 @@ class alignas(8) Decl {
/// 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 @@ class alignas(8) Decl {
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
index 5ce5fea45c670..a1778baa04530 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -170,6 +170,8 @@ class Module {
bool isPrivateModule() const { return Kind == PrivateModuleFragment; }
+ bool isModuleMapModule() const { return Kind == ModuleMapModule; }
+
private:
/// The submodules of this module, indexed by name.
std::vector<Module *> SubModules;
@@ -536,6 +538,10 @@ class Module {
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
index 1f43ff0b5aaf6..8b11273416c55 100644
--- a/clang/include/clang/Sema/Lookup.h
+++ b/clang/include/clang/Sema/Lookup.h
@@ -346,30 +346,56 @@ class LookupResult {
/// 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
index f3b134953d400..07f3d2c654c1b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -363,8 +363,6 @@ class Sema final {
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 @@ class Sema final {
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 @@ class Sema final {
/// Namespace definitions that we will export when they finish.
llvm::SmallPtrSet<const NamespaceDecl*, 8> 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 @@ class Sema final {
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 @@ class Sema final {
/// 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 @@ class Sema final {
llvm::SmallVectorImpl<Module *> *Modules = nullptr) {
return isVisible(D) || hasVisibleDeclarationSlow(D, Modules);
}
+
bool hasVisibleDeclarationSlow(const NamedDecl *D,
llvm::SmallVectorImpl<Module *> *Modules);
+ /// Determine whether any declaration of an entity is reachable.
+ bool
+ hasReachableDeclaration(const NamedDecl *D,
+ llvm::SmallVectorImpl<Module *> *Modules = nullptr) {
+ return isReachable(D) || hasReachableDeclarationSlow(D, Modules);
+ }
+ bool hasReachableDeclarationSlow(
+ const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules = nullptr);
bool hasVisibleMergedDefinition(NamedDecl *Def);
bool hasMergedDefinitionInCurrentModule(NamedDecl *Def);
@@ -2327,21 +2351,54 @@ class Sema final {
return hasVisibleDefinition(const_cast<NamedDecl*>(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<Module *> *Modules = nullptr);
+ /// Determine if the template parameter \p D has a reachable default argument.
+ bool hasReachableDefaultArgument(
+ const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules = nullptr);
+ /// Determine if the template parameter \p D has a reachable default argument.
+ bool hasAcceptableDefaultArgument(const NamedDecl *D,
+ llvm::SmallVectorImpl<Module *> *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<Module *> *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<Module *> *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<Module *> *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<Module *> *Modules = nullptr);
/// Determine if \p A and \p B are equivalent internal linkage declarations
/// from
diff erent modules, and thus an ambiguity error can be downgraded to
@@ -3100,8 +3157,9 @@ class Sema final {
/// 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
index 94f5d5cb89a24..5e5101203e6cf 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -588,6 +588,7 @@ static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) {
// 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
index 97783ebd6b530..3f8fedda7174e 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -121,7 +121,7 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
// entering the context, and that can't happen in a SFINAE context.
assert(!isSFINAEContext() &&
"partial specialization scope specifier in SFINAE context?");
- if (!hasVisibleDeclaration(PartialSpec))
+ if (!hasReachableDefinition(PartialSpec))
diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec,
MissingImportKind::PartialSpecialization,
/*Recover*/true);
@@ -243,8 +243,8 @@ bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L,
if (EnumD->isCompleteDefinition()) {
// If we know about the definition but it is not visible, complain.
NamedDecl *SuggestedDef = nullptr;
- if (!hasVisibleDefinition(EnumD, &SuggestedDef,
- /*OnlyNeedComplete*/false)) {
+ if (!hasReachableDefinition(EnumD, &SuggestedDef,
+ /*OnlyNeedComplete*/ false)) {
// If the user is going to see an error here, recover by making the
// definition visible.
bool TreatAsComplete = !isSFINAEContext();
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 99dfebd603ffc..7ed338d49a869 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16310,7 +16310,12 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc,
if (getLangOpts().CPlusPlusModules && isCurrentModulePurview()) {
Module *GlobalModule =
PushGlobalModuleFragment(ExternLoc, /*IsImplicit=*/true);
- D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate);
+ /// According to [module.reach]p3.2,
+ /// The declaration in global module fragment is reachable if it is not
+ /// discarded. And the discarded declaration should be deleted. So it
+ /// doesn't matter mark the declaration in global module fragment as
+ /// reachable here.
+ D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported);
D->setLocalOwningModule(GlobalModule);
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ce7706ab96cac..40fb60c729bc6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -17943,7 +17943,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
if (NeedDefinition &&
(Func->getTemplateSpecializationKind() != TSK_Undeclared ||
Func->getMemberSpecializationInfo()))
- checkSpecializationVisibility(Loc, Func);
+ checkSpecializationReachability(Loc, Func);
if (getLangOpts().CUDA)
CheckCUDACall(Loc, Func);
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 9f2e1eac41be6..47c7a61f8072f 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -1607,16 +1607,17 @@ bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) {
return false;
}
-template<typename ParmDecl>
+template <typename ParmDecl>
static bool
-hasVisibleDefaultArgument(Sema &S, const ParmDecl *D,
- llvm::SmallVectorImpl<Module *> *Modules) {
+hasAcceptableDefaultArgument(Sema &S, const ParmDecl *D,
+ llvm::SmallVectorImpl<Module *> *Modules,
+ Sema::AcceptableKind Kind) {
if (!D->hasDefaultArgument())
return false;
while (D) {
auto &DefaultArg = D->getDefaultArgStorage();
- if (!DefaultArg.isInherited() && S.isVisible(D))
+ if (!DefaultArg.isInherited() && S.isAcceptable(D, Kind))
return true;
if (!DefaultArg.isInherited() && Modules) {
@@ -1630,20 +1631,36 @@ hasVisibleDefaultArgument(Sema &S, const ParmDecl *D,
return false;
}
-bool Sema::hasVisibleDefaultArgument(const NamedDecl *D,
- llvm::SmallVectorImpl<Module *> *Modules) {
+bool Sema::hasAcceptableDefaultArgument(
+ const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules,
+ Sema::AcceptableKind Kind) {
if (auto *P = dyn_cast<TemplateTypeParmDecl>(D))
- return ::hasVisibleDefaultArgument(*this, P, Modules);
+ return ::hasAcceptableDefaultArgument(*this, P, Modules, Kind);
+
if (auto *P = dyn_cast<NonTypeTemplateParmDecl>(D))
- return ::hasVisibleDefaultArgument(*this, P, Modules);
- return ::hasVisibleDefaultArgument(*this, cast<TemplateTemplateParmDecl>(D),
- Modules);
+ return ::hasAcceptableDefaultArgument(*this, P, Modules, Kind);
+
+ return ::hasAcceptableDefaultArgument(
+ *this, cast<TemplateTemplateParmDecl>(D), Modules, Kind);
+}
+
+bool Sema::hasVisibleDefaultArgument(const NamedDecl *D,
+ llvm::SmallVectorImpl<Module *> *Modules) {
+ return hasAcceptableDefaultArgument(D, Modules,
+ Sema::AcceptableKind::Visible);
+}
+
+bool Sema::hasReachableDefaultArgument(
+ const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) {
+ return hasAcceptableDefaultArgument(D, Modules,
+ Sema::AcceptableKind::Reachable);
}
-template<typename Filter>
-static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D,
- llvm::SmallVectorImpl<Module *> *Modules,
- Filter F) {
+template <typename Filter>
+static bool
+hasAcceptableDeclarationImpl(Sema &S, const NamedDecl *D,
+ llvm::SmallVectorImpl<Module *> *Modules, Filter F,
+ Sema::AcceptableKind Kind) {
bool HasFilteredRedecls = false;
for (auto *Redecl : D->redecls()) {
@@ -1651,7 +1668,7 @@ static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D,
if (!F(R))
continue;
- if (S.isVisible(R))
+ if (S.isAcceptable(R, Kind))
return true;
HasFilteredRedecls = true;
@@ -1667,74 +1684,115 @@ static bool hasVisibleDeclarationImpl(Sema &S, const NamedDecl *D,
return true;
}
+static bool
+hasAcceptableExplicitSpecialization(Sema &S, const NamedDecl *D,
+ llvm::SmallVectorImpl<Module *> *Modules,
+ Sema::AcceptableKind Kind) {
+ return hasAcceptableDeclarationImpl(
+ S, D, Modules,
+ [](const NamedDecl *D) {
+ if (auto *RD = dyn_cast<CXXRecordDecl>(D))
+ return RD->getTemplateSpecializationKind() ==
+ TSK_ExplicitSpecialization;
+ if (auto *FD = dyn_cast<FunctionDecl>(D))
+ return FD->getTemplateSpecializationKind() ==
+ TSK_ExplicitSpecialization;
+ if (auto *VD = dyn_cast<VarDecl>(D))
+ return VD->getTemplateSpecializationKind() ==
+ TSK_ExplicitSpecialization;
+ llvm_unreachable("unknown explicit specialization kind");
+ },
+ Kind);
+}
+
bool Sema::hasVisibleExplicitSpecialization(
const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) {
- return hasVisibleDeclarationImpl(*this, D, Modules, [](const NamedDecl *D) {
- if (auto *RD = dyn_cast<CXXRecordDecl>(D))
- return RD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization;
- if (auto *FD = dyn_cast<FunctionDecl>(D))
- return FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization;
- if (auto *VD = dyn_cast<VarDecl>(D))
- return VD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization;
- llvm_unreachable("unknown explicit specialization kind");
- });
+ return ::hasAcceptableExplicitSpecialization(*this, D, Modules,
+ Sema::AcceptableKind::Visible);
}
-bool Sema::hasVisibleMemberSpecialization(
+bool Sema::hasReachableExplicitSpecialization(
const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) {
+ return ::hasAcceptableExplicitSpecialization(*this, D, Modules,
+ Sema::AcceptableKind::Reachable);
+}
+
+static bool
+hasAcceptableMemberSpecialization(Sema &S, const NamedDecl *D,
+ llvm::SmallVectorImpl<Module *> *Modules,
+ Sema::AcceptableKind Kind) {
assert(isa<CXXRecordDecl>(D->getDeclContext()) &&
"not a member specialization");
- return hasVisibleDeclarationImpl(*this, D, Modules, [](const NamedDecl *D) {
- // If the specialization is declared at namespace scope, then it's a member
- // specialization declaration. If it's lexically inside the class
- // definition then it was instantiated.
- //
- // FIXME: This is a hack. There should be a better way to determine this.
- // FIXME: What about MS-style explicit specializations declared within a
- // class definition?
- return D->getLexicalDeclContext()->isFileContext();
- });
+ return hasAcceptableDeclarationImpl(
+ S, D, Modules,
+ [](const NamedDecl *D) {
+ // If the specialization is declared at namespace scope, then it's a
+ // member specialization declaration. If it's lexically inside the class
+ // definition then it was instantiated.
+ //
+ // FIXME: This is a hack. There should be a better way to determine
+ // this.
+ // FIXME: What about MS-style explicit specializations declared within a
+ // class definition?
+ return D->getLexicalDeclContext()->isFileContext();
+ },
+ Kind);
+}
+
+bool Sema::hasVisibleMemberSpecialization(
+ const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) {
+ return hasAcceptableMemberSpecialization(*this, D, Modules,
+ Sema::AcceptableKind::Visible);
+}
+
+bool Sema::hasReachableMemberSpecialization(
+ const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) {
+ return hasAcceptableMemberSpecialization(*this, D, Modules,
+ Sema::AcceptableKind::Reachable);
}
-/// Determine whether a declaration is visible to name lookup.
+/// Determine whether a declaration is acceptable to name lookup.
///
-/// This routine determines whether the declaration D is visible in the current
-/// lookup context, taking into account the current template instantiation
-/// stack. During template instantiation, a declaration is visible if it is
-/// visible from a module containing any entity on the template instantiation
-/// path (by instantiating a template, you allow it to see the declarations that
-/// your module can see, including those later on in your module).
-bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) {
+/// This routine determines whether the declaration D is acceptable in the
+/// current lookup context, taking into account the current template
+/// instantiation stack. During template instantiation, a declaration is
+/// acceptable if it is acceptable from a module containing any entity on the
+/// template instantiation path (by instantiating a template, you allow it to
+/// see the declarations that your module can see, including those later on in
+/// your module).
+bool LookupResult::isAcceptableSlow(Sema &SemaRef, NamedDecl *D,
+ Sema::AcceptableKind Kind) {
assert(!D->isUnconditionallyVisible() &&
"should not call this: not in slow case");
Module *DeclModule = SemaRef.getOwningModule(D);
assert(DeclModule && "hidden decl has no owning module");
- if (SemaRef.isModuleVisible(DeclModule, D->isModulePrivate()))
- // If the owning module is visible, the decl is visible.
+ // If the owning module is visible, the decl is acceptable.
+ if (SemaRef.isModuleVisible(DeclModule,
+ D->isInvisibleOutsideTheOwningModule()))
return true;
// Determine whether a decl context is a file context for the purpose of
- // visibility. This looks through some (export and linkage spec) transparent
- // contexts, but not others (enums).
+ // visibility/reachability. This looks through some (export and linkage spec)
+ // transparent contexts, but not others (enums).
auto IsEffectivelyFileContext = [](const DeclContext *DC) {
return DC->isFileContext() || isa<LinkageSpecDecl>(DC) ||
isa<ExportDecl>(DC);
};
// If this declaration is not at namespace scope
- // then it is visible if its lexical parent has a visible definition.
+ // then it is acceptable if its lexical parent has a acceptable definition.
DeclContext *DC = D->getLexicalDeclContext();
if (DC && !IsEffectivelyFileContext(DC)) {
// For a parameter, check whether our current template declaration's
- // lexical context is visible, not whether there's some other visible
+ // lexical context is acceptable, not whether there's some other acceptable
// definition of it, because parameters aren't "within" the definition.
//
- // In C++ we need to check for a visible definition due to ODR merging,
+ // In C++ we need to check for a acceptable definition due to ODR merging,
// and in C we must not because each declaration of a function gets its own
// set of declarations for tags in prototype scope.
- bool VisibleWithinParent;
+ bool AcceptableWithinParent;
if (D->isTemplateParameter()) {
bool SearchDefinitions = true;
if (const auto *DCD = dyn_cast<Decl>(DC)) {
@@ -1745,41 +1803,63 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) {
}
}
if (SearchDefinitions)
- VisibleWithinParent = SemaRef.hasVisibleDefinition(cast<NamedDecl>(DC));
+ AcceptableWithinParent =
+ SemaRef.hasAcceptableDefinition(cast<NamedDecl>(DC), Kind);
else
- VisibleWithinParent = isVisible(SemaRef, cast<NamedDecl>(DC));
+ AcceptableWithinParent =
+ isAcceptable(SemaRef, cast<NamedDecl>(DC), Kind);
} else if (isa<ParmVarDecl>(D) ||
(isa<FunctionDecl>(DC) && !SemaRef.getLangOpts().CPlusPlus))
- VisibleWithinParent = isVisible(SemaRef, cast<NamedDecl>(DC));
+ AcceptableWithinParent = isAcceptable(SemaRef, cast<NamedDecl>(DC), Kind);
else if (D->isModulePrivate()) {
- // A module-private declaration is only visible if an enclosing lexical
+ // A module-private declaration is only acceptable if an enclosing lexical
// parent was merged with another definition in the current module.
- VisibleWithinParent = false;
+ AcceptableWithinParent = false;
do {
if (SemaRef.hasMergedDefinitionInCurrentModule(cast<NamedDecl>(DC))) {
- VisibleWithinParent = true;
+ AcceptableWithinParent = true;
break;
}
DC = DC->getLexicalParent();
} while (!IsEffectivelyFileContext(DC));
} else {
- VisibleWithinParent = SemaRef.hasVisibleDefinition(cast<NamedDecl>(DC));
+ AcceptableWithinParent =
+ SemaRef.hasAcceptableDefinition(cast<NamedDecl>(DC), Kind);
}
- if (VisibleWithinParent && SemaRef.CodeSynthesisContexts.empty() &&
+ if (AcceptableWithinParent && SemaRef.CodeSynthesisContexts.empty() &&
+ Kind == Sema::AcceptableKind::Visible &&
// FIXME: Do something better in this case.
!SemaRef.getLangOpts().ModulesLocalVisibility) {
// Cache the fact that this declaration is implicitly visible because
// its parent has a visible definition.
D->setVisibleDespiteOwningModule();
}
- return VisibleWithinParent;
+ return AcceptableWithinParent;
}
- return false;
+ if (Kind == Sema::AcceptableKind::Visible)
+ return false;
+
+ assert(Kind == Sema::AcceptableKind::Reachable &&
+ "Additional Sema::AcceptableKind?");
+ return isReachableSlow(SemaRef, D);
}
bool Sema::isModuleVisible(const Module *M, bool ModulePrivate) {
+ // [module.global.frag]p2:
+ // A global-module-fragment specifies the contents of the global module
+ // fragment for a module unit. The global module fragment can be used to
+ // provide declarations that are attached to the global module and usable
+ // within the module unit.
+ //
+ // Global module fragment is special. Global Module fragment is only usable
+ // within the module unit it got defined [module.global.frag]p2. So here we
+ // check if the Module is the global module fragment in current translation
+ // unit.
+ if (M->isGlobalModule() && M != this->GlobalModuleFragment)
+ return false;
+
// The module might be ordinarily visible. For a module-private query, that
// means it is part of the current module.
if (ModulePrivate && isUsableModule(M))
@@ -1812,8 +1892,74 @@ bool Sema::isModuleVisible(const Module *M, bool ModulePrivate) {
});
}
-bool Sema::isVisibleSlow(const NamedDecl *D) {
- return LookupResult::isVisible(*this, const_cast<NamedDecl*>(D));
+// FIXME: Return false directly if we don't have an interface dependency on the
+// translation unit containing D.
+bool LookupResult::isReachableSlow(Sema &SemaRef, NamedDecl *D) {
+ assert(!isVisible(SemaRef, D) && "Shouldn't call the slow case.\n");
+
+ Module *DeclModule = SemaRef.getOwningModule(D);
+ assert(DeclModule && "hidden decl has no owning module");
+
+ // Entities in module map modules are reachable only if they're visible.
+ if (DeclModule->isModuleMapModule())
+ return false;
+
+ // If D comes from a module and SemaRef doesn't own a module, it implies D
+ // comes from another TU. In case SemaRef owns a module, we could judge if D
+ // comes from another TU by comparing the module unit.
+ //
+ // FIXME: It would look better if we have direct method to judge whether D is
+ // in another TU.
+ if (SemaRef.getCurrentModule() &&
+ SemaRef.getCurrentModule()->getTopLevelModule() ==
+ DeclModule->getTopLevelModule())
+ return true;
+
+ // [module.reach]/p3:
+ // A declaration D is reachable from a point P if:
+ // ...
+ // - D is not discarded ([module.global.frag]), appears in a translation unit
+ // that is reachable from P, and does not appear within a private module
+ // fragment.
+ //
+ // A declaration that's discarded in the GMF should be module-private.
+ if (D->isModulePrivate())
+ return false;
+
+ // [module.reach]/p1
+ // A translation unit U is necessarily reachable from a point P if U is a
+ // module interface unit on which the translation unit containing P has an
+ // interface dependency, or the translation unit containing P imports U, in
+ // either case prior to P ([module.import]).
+ //
+ // [module.import]/p10
+ // A translation unit has an interface dependency on a translation unit U if
+ // it contains a declaration (possibly a module-declaration) that imports U
+ // or if it has an interface dependency on a translation unit that has an
+ // interface dependency on U.
+ //
+ // So we could conclude the module unit U is necessarily reachable if:
+ // (1) The module unit U is module interface unit.
+ // (2) The current unit has an interface dependency on the module unit U.
+ //
+ // Here we only check for the first condition. Since we couldn't see
+ // DeclModule if it isn't (transitively) imported.
+ if (DeclModule->getTopLevelModule()->isModuleInterfaceUnit())
+ return true;
+
+ // [module.reach]/p2
+ // Additional translation units on
+ // which the point within the program has an interface dependency may be
+ // considered reachable, but it is unspecified which are and under what
+ // circumstances.
+ //
+ // The decision here is to treat all additional tranditional units as
+ // unreachable.
+ return false;
+}
+
+bool Sema::isAcceptableSlow(const NamedDecl *D, Sema::AcceptableKind Kind) {
+ return LookupResult::isAcceptable(*this, const_cast<NamedDecl *>(D), Kind);
}
bool Sema::shouldLinkPossiblyHiddenDecl(LookupResult &R, const NamedDecl *New) {
@@ -1862,7 +2008,7 @@ bool Sema::shouldLinkPossiblyHiddenDecl(LookupResult &R, const NamedDecl *New) {
/// and visible. If no declaration of D is visible, returns null.
static NamedDecl *findAcceptableDecl(Sema &SemaRef, NamedDecl *D,
unsigned IDNS) {
- assert(!LookupResult::isVisible(SemaRef, D) && "not in slow case");
+ assert(!LookupResult::isAvailableForLookup(SemaRef, D) && "not in slow case");
for (auto RD : D->redecls()) {
// Don't bother with extra checks if we already know this one isn't visible.
@@ -1874,7 +2020,7 @@ static NamedDecl *findAcceptableDecl(Sema &SemaRef, NamedDecl *D,
// visible in the same scope as D. This needs to be done much more
// carefully.
if (ND->isInIdentifierNamespace(IDNS) &&
- LookupResult::isVisible(SemaRef, ND))
+ LookupResult::isAvailableForLookup(SemaRef, ND))
return ND;
}
@@ -1884,8 +2030,17 @@ static NamedDecl *findAcceptableDecl(Sema &SemaRef, NamedDecl *D,
bool Sema::hasVisibleDeclarationSlow(const NamedDecl *D,
llvm::SmallVectorImpl<Module *> *Modules) {
assert(!isVisible(D) && "not in slow case");
- return hasVisibleDeclarationImpl(*this, D, Modules,
- [](const NamedDecl *) { return true; });
+ return hasAcceptableDeclarationImpl(
+ *this, D, Modules, [](const NamedDecl *) { return true; },
+ Sema::AcceptableKind::Visible);
+}
+
+bool Sema::hasReachableDeclarationSlow(
+ const NamedDecl *D, llvm::SmallVectorImpl<Module *> *Modules) {
+ assert(!isReachable(D) && "not in slow case");
+ return hasAcceptableDeclarationImpl(
+ *this, D, Modules, [](const NamedDecl *) { return true; },
+ Sema::AcceptableKind::Reachable);
}
NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const {
@@ -1910,6 +2065,60 @@ NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const {
return findAcceptableDecl(getSema(), D, IDNS);
}
+bool LookupResult::isVisible(Sema &SemaRef, NamedDecl *D) {
+ // If this declaration is already visible, return it directly.
+ if (D->isUnconditionallyVisible())
+ return true;
+
+ // During template instantiation, we can refer to hidden declarations, if
+ // they were visible in any module along the path of instantiation.
+ return isAcceptableSlow(SemaRef, D, Sema::AcceptableKind::Visible);
+}
+
+bool LookupResult::isReachable(Sema &SemaRef, NamedDecl *D) {
+ if (D->isUnconditionallyVisible())
+ return true;
+
+ return isAcceptableSlow(SemaRef, D, Sema::AcceptableKind::Reachable);
+}
+
+bool LookupResult::isAvailableForLookup(Sema &SemaRef, NamedDecl *ND) {
+ // We should check the visibility at the callsite already.
+ if (isVisible(SemaRef, ND))
+ return true;
+
+ auto *DC = ND->getDeclContext();
+ // If ND is not visible and it is at namespace scope, it shouldn't be found
+ // by name lookup.
+ if (DC->isFileContext())
+ return false;
+
+ // [module.interface]p7
+ // Class and enumeration member names can be found by name lookup in any
+ // context in which a definition of the type is reachable.
+ //
+ // FIXME: The current implementation didn't consider about scope. For example,
+ // ```
+ // // m.cppm
+ // export module m;
+ // enum E1 { e1 };
+ // // Use.cpp
+ // import m;
+ // void test() {
+ // auto a = E1::e1; // Error as expected.
+ // auto b = e1; // Should be error. namespace-scope name e1 is not visible
+ // }
+ // ```
+ // For the above example, the current implementation would emit error for `a`
+ // correctly. However, the implementation wouldn't diagnose about `b` now.
+ // Since we only check the reachability for the parent only.
+ // See clang/test/CXX/module/module.interface/p7.cpp for example.
+ if (auto *TD = dyn_cast<TagDecl>(DC))
+ return SemaRef.hasReachableDefinition(TD);
+
+ return false;
+}
+
/// Perform unqualified name lookup starting from a given
/// scope.
///
@@ -3641,7 +3850,16 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, SourceLocation Loc,
}
} else if (D->getFriendObjectKind()) {
auto *RD = cast<CXXRecordDecl>(D->getLexicalDeclContext());
- if (AssociatedClasses.count(RD) && isVisible(D)) {
+ // [basic.lookup.argdep]p4:
+ // Argument-dependent lookup finds all declarations of functions and
+ // function templates that
+ // - ...
+ // - are declared as a friend ([class.friend]) of any class with a
+ // reachable definition in the set of associated entities,
+ //
+ // FIXME: If there's a merged definition of D that is reachable, then
+ // the friend declaration should be considered.
+ if (AssociatedClasses.count(RD) && isReachable(D)) {
Visible = true;
break;
}
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index b0e7d30526a3c..3aa124d457b03 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -90,7 +90,14 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
// All declarations created from now on are owned by the global module.
auto *TU = Context.getTranslationUnitDecl();
- TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible);
+ // [module.global.frag]p2
+ // A global-module-fragment specifies the contents of the global module
+ // fragment for a module unit. The global module fragment can be used to
+ // provide declarations that are attached to the global module and usable
+ // within the module unit.
+ //
+ // So the declations in the global module shouldn't be visible by default.
+ TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported);
TU->setLocalOwningModule(GlobalModule);
// FIXME: Consider creating an explicit representation of this declaration.
@@ -325,10 +332,12 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
VisibleModules.setVisible(Mod, ModuleLoc);
// From now on, we have an owning module for all declarations we see.
- // However, those declarations are module-private unless explicitly
+ // In C++20 modules, those declaration would be reachable when imported
+ // unless explicitily exported.
+ // Otherwise, those declarations are module-private unless explicitly
// exported.
auto *TU = Context.getTranslationUnitDecl();
- TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate);
+ TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported);
TU->setLocalOwningModule(Mod);
// We are in the module purview, but before any other (non import)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 250dbcd4aba63..dbfe6164bda2c 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -799,8 +799,9 @@ bool Sema::DiagnoseUninstantiableTemplate(SourceLocation PointOfInstantiation,
if (PatternDef && !IsEntityBeingDefined) {
NamedDecl *SuggestedDef = nullptr;
- if (!hasVisibleDefinition(const_cast<NamedDecl*>(PatternDef), &SuggestedDef,
- /*OnlyNeedComplete*/false)) {
+ if (!hasReachableDefinition(const_cast<NamedDecl *>(PatternDef),
+ &SuggestedDef,
+ /*OnlyNeedComplete*/ false)) {
// If we're allowed to diagnose this and recover, do so.
bool Recover = Complain && !isSFINAEContext();
if (Complain)
@@ -5255,7 +5256,7 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template,
HasDefaultArg = false;
if (TemplateTypeParmDecl *TypeParm = dyn_cast<TemplateTypeParmDecl>(Param)) {
- if (!hasVisibleDefaultArgument(TypeParm))
+ if (!hasReachableDefaultArgument(TypeParm))
return TemplateArgumentLoc();
HasDefaultArg = true;
@@ -5272,7 +5273,7 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template,
if (NonTypeTemplateParmDecl *NonTypeParm
= dyn_cast<NonTypeTemplateParmDecl>(Param)) {
- if (!hasVisibleDefaultArgument(NonTypeParm))
+ if (!hasReachableDefaultArgument(NonTypeParm))
return TemplateArgumentLoc();
HasDefaultArg = true;
@@ -5290,7 +5291,7 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template,
TemplateTemplateParmDecl *TempTempParm
= cast<TemplateTemplateParmDecl>(Param);
- if (!hasVisibleDefaultArgument(TempTempParm))
+ if (!hasReachableDefaultArgument(TempTempParm))
return TemplateArgumentLoc();
HasDefaultArg = true;
@@ -5628,10 +5629,10 @@ static bool diagnoseMissingArgument(Sema &S, SourceLocation Loc,
->getTemplateParameters()
->getParam(D->getIndex()));
- // If there's a default argument that's not visible, diagnose that we're
+ // If there's a default argument that's not reachable, diagnose that we're
// missing a module import.
llvm::SmallVector<Module*, 8> Modules;
- if (D->hasDefaultArgument() && !S.hasVisibleDefaultArgument(D, &Modules)) {
+ if (D->hasDefaultArgument() && !S.hasReachableDefaultArgument(D, &Modules)) {
S.diagnoseMissingImport(Loc, cast<NamedDecl>(TD),
D->getDefaultArgumentLoc(), Modules,
Sema::MissingImportKind::DefaultArgument,
@@ -5814,7 +5815,7 @@ bool Sema::CheckTemplateArgumentList(
// (when the template parameter was part of a nested template) into
// the default argument.
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param)) {
- if (!hasVisibleDefaultArgument(TTP))
+ if (!hasReachableDefaultArgument(TTP))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP,
NewArgs);
@@ -5831,7 +5832,7 @@ bool Sema::CheckTemplateArgumentList(
ArgType);
} else if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
- if (!hasVisibleDefaultArgument(NTTP))
+ if (!hasReachableDefaultArgument(NTTP))
return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP,
NewArgs);
@@ -5849,7 +5850,7 @@ bool Sema::CheckTemplateArgumentList(
TemplateTemplateParmDecl *TempParm
= cast<TemplateTemplateParmDecl>(*Param);
- if (!hasVisibleDefaultArgument(TempParm))
+ if (!hasReachableDefaultArgument(TempParm))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TempParm,
NewArgs);
@@ -11006,10 +11007,12 @@ class ExplicitSpecializationVisibilityChecker {
Sema &S;
SourceLocation Loc;
llvm::SmallVector<Module *, 8> Modules;
+ Sema::AcceptableKind Kind;
public:
- ExplicitSpecializationVisibilityChecker(Sema &S, SourceLocation Loc)
- : S(S), Loc(Loc) {}
+ ExplicitSpecializationVisibilityChecker(Sema &S, SourceLocation Loc,
+ Sema::AcceptableKind Kind)
+ : S(S), Loc(Loc), Kind(Kind) {}
void check(NamedDecl *ND) {
if (auto *FD = dyn_cast<FunctionDecl>(ND))
@@ -11037,6 +11040,23 @@ class ExplicitSpecializationVisibilityChecker {
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 @@ class ExplicitSpecializationVisibilityChecker {
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 @@ class ExplicitSpecializationVisibilityChecker {
checkTemplate(TD);
else if (auto *TD =
From.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
- if (!S.hasVisibleDeclaration(TD))
+ if (!CheckDeclaration(TD))
diagnose(TD, true);
checkTemplate(TD);
}
@@ -11096,7 +11115,7 @@ class ExplicitSpecializationVisibilityChecker {
checkTemplate(TD);
else if (auto *TD =
From.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
- if (!S.hasVisibleDeclaration(TD))
+ if (!CheckDeclaration(TD))
diagnose(TD, true);
checkTemplate(TD);
}
@@ -11107,7 +11126,7 @@ class ExplicitSpecializationVisibilityChecker {
template<typename TemplDecl>
void checkTemplate(TemplDecl *TD) {
if (TD->isMemberSpecialization()) {
- if (!S.hasVisibleMemberSpecialization(TD, &Modules))
+ if (!CheckMemberSpecialization(TD))
diagnose(TD->getMostRecentDecl(), false);
}
}
@@ -11118,5 +11137,17 @@ void Sema::checkSpecializationVisibility(SourceLocation Loc, NamedDecl *Spec) {
if (!getLangOpts().Modules)
return;
- ExplicitSpecializationVisibilityChecker(*this, Loc).check(Spec);
+ ExplicitSpecializationVisibilityChecker(*this, Loc,
+ Sema::AcceptableKind::Visible)
+ .check(Spec);
+}
+
+void Sema::checkSpecializationReachability(SourceLocation Loc,
+ NamedDecl *Spec) {
+ if (!getLangOpts().CPlusPlusModules)
+ return checkSpecializationVisibility(Loc, Spec);
+
+ ExplicitSpecializationVisibilityChecker(*this, Loc,
+ Sema::AcceptableKind::Reachable)
+ .check(Spec);
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 0015d6a870b98..3edce941c3817 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8642,17 +8642,8 @@ bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) {
return Ctx.IsEquivalent(D, Suggested);
}
-/// Determine whether there is any declaration of \p D that was ever a
-/// definition (perhaps before module merging) and is currently visible.
-/// \param D The definition of the entity.
-/// \param Suggested Filled in with the declaration that should be made visible
-/// in order to provide a definition of this entity.
-/// \param OnlyNeedComplete If \c true, we only need the type to be complete,
-/// not defined. This only matters for enums with a fixed underlying
-/// type, since in all other cases, a type is complete if and only if it
-/// is defined.
-bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested,
- bool OnlyNeedComplete) {
+bool Sema::hasAcceptableDefinition(NamedDecl *D, NamedDecl **Suggested,
+ AcceptableKind Kind, bool OnlyNeedComplete) {
// Easy case: if we don't have modules, all declarations are visible.
if (!getLangOpts().Modules && !getLangOpts().ModulesLocalVisibility)
return true;
@@ -8696,13 +8687,14 @@ bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested,
VD = Pattern;
D = VD->getDefinition();
}
+
assert(D && "missing definition for pattern of instantiated definition");
*Suggested = D;
- auto DefinitionIsVisible = [&] {
+ auto DefinitionIsAcceptable = [&] {
// The (primary) definition might be in a visible module.
- if (isVisible(D))
+ if (isAcceptable(D, Kind))
return true;
// A visible module might have a merged definition instead.
@@ -8720,19 +8712,51 @@ bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested,
return false;
};
- if (DefinitionIsVisible())
+ if (DefinitionIsAcceptable())
return true;
// The external source may have additional definitions of this entity that are
// visible, so complete the redeclaration chain now and ask again.
if (auto *Source = Context.getExternalSource()) {
Source->CompleteRedeclChain(D);
- return DefinitionIsVisible();
+ return DefinitionIsAcceptable();
}
return false;
}
+/// Determine whether there is any declaration of \p D that was ever a
+/// definition (perhaps before module merging) and is currently visible.
+/// \param D The definition of the entity.
+/// \param Suggested Filled in with the declaration that should be made visible
+/// in order to provide a definition of this entity.
+/// \param OnlyNeedComplete If \c true, we only need the type to be complete,
+/// not defined. This only matters for enums with a fixed underlying
+/// type, since in all other cases, a type is complete if and only if it
+/// is defined.
+bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested,
+ bool OnlyNeedComplete) {
+ return hasAcceptableDefinition(D, Suggested, Sema::AcceptableKind::Visible,
+ OnlyNeedComplete);
+}
+
+/// Determine whether there is any declaration of \p D that was ever a
+/// definition (perhaps before module merging) and is currently
+/// reachable.
+/// \param D The definition of the entity.
+/// \param Suggested Filled in with the declaration that should be made
+/// reachable
+/// in order to provide a definition of this entity.
+/// \param OnlyNeedComplete If \c true, we only need the type to be complete,
+/// not defined. This only matters for enums with a fixed underlying
+/// type, since in all other cases, a type is complete if and only if it
+/// is defined.
+bool Sema::hasReachableDefinition(NamedDecl *D, NamedDecl **Suggested,
+ bool OnlyNeedComplete) {
+ return hasAcceptableDefinition(D, Suggested, Sema::AcceptableKind::Reachable,
+ OnlyNeedComplete);
+}
+
/// Locks in the inheritance model for the given class and all of its bases.
static void assignInheritanceModel(Sema &S, CXXRecordDecl *RD) {
RD = RD->getMostRecentNonInjectedDecl();
@@ -8802,20 +8826,19 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
// Check that any necessary explicit specializations are visible. For an
// enum, we just need the declaration, so don't check this.
if (Def && !isa<EnumDecl>(Def))
- checkSpecializationVisibility(Loc, Def);
+ checkSpecializationReachability(Loc, Def);
// If we have a complete type, we're done.
if (!Incomplete) {
- // If we know about the definition but it is not visible, complain.
- NamedDecl *SuggestedDef = nullptr;
+ NamedDecl *Suggested = nullptr;
if (Def &&
- !hasVisibleDefinition(Def, &SuggestedDef, /*OnlyNeedComplete*/true)) {
+ !hasReachableDefinition(Def, &Suggested, /*OnlyNeedComplete=*/true)) {
// If the user is going to see an error here, recover by making the
// definition visible.
bool TreatAsComplete = Diagnoser && !isSFINAEContext();
- if (Diagnoser && SuggestedDef)
- diagnoseMissingImport(Loc, SuggestedDef, MissingImportKind::Definition,
- /*Recover*/TreatAsComplete);
+ if (Diagnoser && Suggested)
+ diagnoseMissingImport(Loc, Suggested, MissingImportKind::Definition,
+ /*Recover*/ TreatAsComplete);
return !TreatAsComplete;
} else if (Def && !TemplateInstCallbacks.empty()) {
CodeSynthesisContext TempInst;
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index ae298557ee49c..b4506da2bb2ba 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -605,15 +605,27 @@ void ASTDeclReader::VisitDecl(Decl *D) {
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
index 3666d5a6daab8..01f692c9611b7 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -311,7 +311,7 @@ void ASTDeclWriter::VisitDecl(Decl *D) {
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
diff erent from its
@@ -1928,7 +1928,7 @@ void ASTWriter::WriteDeclAbbrevs() {
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 @@ void ASTWriter::WriteDeclAbbrevs() {
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 @@ void ASTWriter::WriteDeclAbbrevs() {
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 @@ void ASTWriter::WriteDeclAbbrevs() {
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 @@ void ASTWriter::WriteDeclAbbrevs() {
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 @@ void ASTWriter::WriteDeclAbbrevs() {
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 @@ void ASTWriter::WriteDeclAbbrevs() {
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 @@ void ASTWriter::WriteDeclAbbrevs() {
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
index 0000000000000..638057cbd681f
--- /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
index 79529c6c0fc0a..d4b425b22fd6a 100644
--- 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 @@ module;
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 at p2.cpp:16 {{not visible}}
+ // expected-note at p2.cpp:16 {{not visible}}
exported = 1; // expected-error {{must be imported from module 'A'}}
- // expected-note at p2.cpp:18 {{not visible}}
+ // expected-note at 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 at 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 at p2.cpp:20 {{declaration here is not visible}}
not_exported_private = 1; // expected-error {{undeclared identifier}}
@@ -55,7 +58,7 @@ import A;
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 at p2.cpp:16 {{not visible}}
@@ -64,14 +67,14 @@ void test_late() {
not_exported = 1;
#ifndef IMPLEMENTATION
- // expected-error at -2 {{undeclared identifier 'not_exported'; did you mean 'exported'}}
- // expected-note at p2.cpp:18 {{declared here}}
+ // expected-error at -2 {{declaration of 'not_exported' must be imported from module 'A' before it is required}}
+ // expected-note at p2.cpp:19 {{declaration here is not visible}}
#endif
internal = 1;
#ifndef IMPLEMENTATION
- // FIXME: should not be visible here
- // expected-error at -3 {{undeclared identifier}}
+ // expected-error at -2 {{declaration of 'internal' must be imported from module 'A' before it is required}}
+ // expected-note at 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
index 0000000000000..6a63e3e15aac4
--- /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 <typename T, typename U> void foo(T, U u) { auto v = u; }
+export template <typename T, typename U> 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 <typename T> 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 <typename T> 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<int, X *>'}}
+ // 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
index 7aa54e38f8c3f..0c02d253f3a89 100644
--- a/clang/test/CXX/module/module.import/p2.cpp
+++ b/clang/test/CXX/module/module.import/p2.cpp
@@ -23,7 +23,10 @@ export A f();
//--- 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 at -1 {{definition of 'A' must be imported from module 'M:impl'}} expected-error at -1 {{}}
+ // expected-note at impl.cppm:2 {{declaration here is not visible}}
+ // expected-note at impl.cppm:2 {{definition here is not reachable}} expected-note at impl.cppm:2 {{}}
}
//--- UseInPartA.cppm
@@ -38,7 +41,10 @@ void test() {
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 at -1 {{definition of 'A' must be imported from module 'M:impl'}} expected-error at -1 {{}}
+ // expected-note at impl.cppm:2 {{declaration here is not visible}}
+ // expected-note at impl.cppm:2 {{definition here is not reachable}} expected-note at 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
index 0a6f8c2aad567..804b0363ffa31 100644
--- a/clang/test/CXX/module/module.interface/p2.cpp
+++ b/clang/test/CXX/module/module.interface/p2.cpp
@@ -69,22 +69,29 @@ import "header.h";
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
index 0000000000000..1572390f0d289
--- /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 <typename T>
+ static bool templ_get(T t) { return false; }
+ typedef int typedef_type;
+ using using_type = int;
+ template <typename T>
+ 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<int> 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
index 0000000000000..00f87607dc1d8
--- /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
index 0000000000000..6ccf6ee6d0566
--- /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
index 0000000000000..96f80a209d266
--- /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
index 0000000000000..9c498a260530f
--- /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
index 8bd05c298d95d..bcc29d9ba25e0 100644
--- 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 at -1 {{'CPP' must be defined before it is used}}
- // expected-error at -2 {{no member named 'print' in 'CPP'}}
- // expected-note at Inputs/CPP.cppm:5 {{definition here is not reachable}}
- // expected-note at 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
index 8370777e7ed47..efac3f892ab79 100644
--- 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 @@ import M;
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 at module.cppm:9 {{here}}
+ // expected-note at module.cppm:10 {{declaration here is not visible}}
+ // expected-note at 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
index 0000000000000..fdaf5c349f8ae
--- /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.<private>' before it is required}}
+ // expected-error at -1 {{definition of 'X' must be imported from module 'Private.<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
index 0000000000000..0d6d8655d5329
--- /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
index 0000000000000..ca5bbc68d759f
--- /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
index 0000000000000..6fb109e41fcf0
--- /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 <typename T = t>
+struct A {
+ T a;
+};
+
+//--- Use.cpp
+import template_default_arg;
+void bar() {
+ A<> a0;
+ A<t> 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
index 0000000000000..2170c7b92a370
--- /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 T>
+class Wrapper {
+public:
+ T value;
+};
+#endif
+
+//--- Templ.cppm
+export module Templ;
+export template <class T>
+class Wrapper2 {
+public:
+ T value;
+};
+
+//--- Use.cppm
+module;
+#include "Templ.h"
+export module Use;
+import Templ;
+
+export template <class T>
+class Use {
+public:
+ Wrapper<T> value;
+ Wrapper2<T> value2;
+};
+
+export template <class T>
+Wrapper<T> wrapper;
+
+//--- Use.cpp
+// expected-no-diagnostics
+module;
+#include "Templ.h"
+export module User;
+
+export template <class T>
+class User {
+public:
+ Wrapper<T> value;
+};
diff --git a/clang/test/Modules/Reachability-using-templates.cpp b/clang/test/Modules/Reachability-using-templates.cpp
new file mode 100644
index 0000000000000..f530e15bd4d2b
--- /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 <class> struct t {};
+export template <class T> using u = t<T>;
+
+//--- Use.cpp
+// expected-no-diagnostics
+import mod.templates;
+void foo() {
+ u<int> v{};
+}
diff --git a/clang/test/Modules/Reachability-using.cpp b/clang/test/Modules/Reachability-using.cpp
new file mode 100644
index 0000000000000..642b97dd8432c
--- /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
index 88ec945d6a848..03e2e26339005 100644
--- a/clang/test/Modules/cxx20-10-1-ex2.cpp
+++ b/clang/test/Modules/cxx20-10-1-ex2.cpp
@@ -53,7 +53,8 @@ int &c = n; // OK
//--- 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
index 0000000000000..ee9e0ae4637ec
--- /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 <typename T>
+struct bar : public bar_base {
+};
+
+//--- foo.cppm
+module;
+#include "bar.h"
+export module foo;
+export template <typename T>
+int foo() {
+ bool a = bar<T>::value;
+ bar<T>::get();
+ bar<T> b;
+ b.member_value = a;
+ bool c = b.get_func();
+ return bar<T>::a;
+}
+
+//--- Use.cpp
+// expected-no-diagnostics
+import foo;
+void test() {
+ foo<int>();
+}
diff --git a/clang/test/Modules/explicitly-specialized-template.cpp b/clang/test/Modules/explicitly-specialized-template.cpp
new file mode 100644
index 0000000000000..89677254ea739
--- /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 <typename T>
+struct base {};
+
+template <typename T>
+struct foo;
+
+template <typename T>
+struct foo {};
+
+template <>
+struct foo<int> : base<int> {
+ int getInt();
+};
+#endif // FOO_H
+
+//--- X.cppm
+module;
+#include "foo.h"
+export module X;
+export template <class T>
+class X {
+ foo<int> x;
+
+public:
+ int print() {
+ return x.getInt();
+ }
+};
+
+//--- Use.cpp
+import X;
+foo<int> f; // expected-error {{'foo' must be declared before it is used}}
+ // expected-note@* {{declaration here is not visible}}
+int bar() {
+ X<int> x;
+ return x.print();
+}
diff --git a/clang/test/Modules/module-private.cpp b/clang/test/Modules/module-private.cpp
index a4b3b0fd21d39..2848226ba067c 100644
--- a/clang/test/Modules/module-private.cpp
+++ b/clang/test/Modules/module-private.cpp
@@ -21,8 +21,8 @@ int test_broken() {
vector<int> 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
index 0000000000000..3eac92e7edb94
--- /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 <typename T>
+void foo() {
+}
+
+template <>
+void foo<int>() {
+}
+
+template <typename T>
+void foo2() {
+}
+
+template <>
+void foo2<int>() {
+}
+
+template <typename T>
+void foo3() {
+}
+
+template <>
+void foo3<int>();
+
+export module foo;
+export using ::foo;
+export using ::foo3;
+
+export template <typename T>
+void foo4() {
+}
+
+export template <>
+void foo4<int>() {
+}
+
+//--- Use.cpp
+import foo;
+void use() {
+ foo<short>();
+ foo<int>();
+ foo2<short>(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}}
+ // expected-note@* {{declaration here is not visible}}
+ foo2<int>(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}}
+ // expected-note@* {{declaration here is not visible}}
+ foo3<short>();
+ foo3<int>();
+
+ foo4<short>();
+ foo4<int>();
+}
diff --git a/clang/test/Modules/template_default_argument.cpp b/clang/test/Modules/template_default_argument.cpp
new file mode 100644
index 0000000000000..5a7d1c04cf181
--- /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 <typename T, typename U = T>
+class templ {};
+template <typename T, typename U = void>
+void templ_func() {}
+
+//--- B.cppm
+module;
+#include "templ.h"
+export module B;
+export template <typename G>
+templ<G> bar() {
+ templ_func<G>();
+ return {};
+}
+
+//--- Use.cpp
+// expected-no-diagnostics
+import B;
+auto foo() {
+ return bar<int>();
+}
More information about the cfe-commits
mailing list