[clang] 21e16ab - [clang][ABI] New C++20 module mangling scheme
Nathan Sidwell via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 8 06:22:03 PST 2022
Author: Nathan Sidwell
Date: 2022-03-08T06:21:50-08:00
New Revision: 21e16ab6b8ddaccb70d2344bb35419e214a32ec9
URL: https://github.com/llvm/llvm-project/commit/21e16ab6b8ddaccb70d2344bb35419e214a32ec9
DIFF: https://github.com/llvm/llvm-project/commit/21e16ab6b8ddaccb70d2344bb35419e214a32ec9.diff
LOG: [clang][ABI] New C++20 module mangling scheme
The existing module symbol mangling scheme turns out to be
undemangleable. It is also desirable to switch to the
strong-ownership model as the hoped-for C++17 compatibility turns out
to be fragile, and we also now have a better way of controlling that.
The issue is captured on the ABI list at:
https://github.com/itanium-cxx-abi/cxx-abi/issues/134
A document describing the issues and new mangling is at:
https://drive.google.com/file/d/1qQjqptzOFT_lfXH8L6-iD9nCRi34wjft/view
This patch is the code-generation part. I have a demangler too, but
that patch is based on some to-be-landed refactoring of the demangler.
The old mangling is unceremoniously dropped. No backwards
compatibility, no deprectated old-mangling flag. It was always
labelled experimental. (Old and new manglings cannot be confused.)
Reviewed By: ChuanqiXu
Differential Revision: https://reviews.llvm.org/D118352
Added:
clang/test/CodeGenCXX/Inputs/cxx20-module-impl-1a.cpp
clang/test/CodeGenCXX/Inputs/cxx20-module-std-subst-2a.cpp
clang/test/CodeGenCXX/cxx20-module-decomp-1.cpp
clang/test/CodeGenCXX/cxx20-module-extern-1.cppm
clang/test/CodeGenCXX/cxx20-module-impl-1a.cpp
clang/test/CodeGenCXX/cxx20-module-nested-1.cppm
clang/test/CodeGenCXX/cxx20-module-nested-2.cppm
clang/test/CodeGenCXX/cxx20-module-part-1a.cpp
clang/test/CodeGenCXX/cxx20-module-part-1b.cpp
clang/test/CodeGenCXX/cxx20-module-part-1c.cpp
clang/test/CodeGenCXX/cxx20-module-std-subst-1.cppm
clang/test/CodeGenCXX/cxx20-module-std-subst-2b.cpp
clang/test/CodeGenCXX/cxx20-module-std-subst-2c.cpp
clang/test/CodeGenCXX/cxx20-module-sub-1a.cppm
clang/test/CodeGenCXX/cxx20-module-sub-1b.cppm
clang/test/CodeGenCXX/cxx20-module-tmpl-1.cppm
Modified:
clang/include/clang/AST/Mangle.h
clang/lib/AST/Decl.cpp
clang/lib/AST/ItaniumMangle.cpp
clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp
clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm
clang/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp
clang/test/CXX/modules-ts/basic/basic.link/p3.cppm
clang/test/CXX/modules-ts/codegen-basics.cppm
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h
index 7d02f08e0120c..c594c9809fef6 100644
--- a/clang/include/clang/AST/Mangle.h
+++ b/clang/include/clang/AST/Mangle.h
@@ -194,6 +194,8 @@ class ItaniumMangleContext : public MangleContext {
virtual void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &) = 0;
+ virtual void mangleModuleInitializer(const Module *Module, raw_ostream &) = 0;
+
// This has to live here, otherwise the CXXNameMangler won't have access to
// it.
virtual DiscriminatorOverrideTy getDiscriminatorOverride() const = 0;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 82c4412296dbc..9b8d0f6e288e7 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1540,6 +1540,11 @@ LinkageInfo LinkageComputer::getDeclLinkageAndVisibility(const NamedDecl *D) {
}
Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const {
+ if (isa<NamespaceDecl>(this))
+ // Namespaces never have module linkage. It is the entities within them
+ // that [may] do.
+ return nullptr;
+
Module *M = getOwningModule();
if (!M)
return nullptr;
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index d1d7a7c40ceb9..1f78142044c8f 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -130,6 +130,8 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) override;
+ void mangleModuleInitializer(const Module *Module, raw_ostream &) override;
+
bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
// Lambda closure types are already numbered.
if (isLambda(ND))
@@ -438,6 +440,7 @@ class CXXNameMangler {
void mangleType(QualType T);
void mangleNameOrStandardSubstitution(const NamedDecl *ND);
void mangleLambdaSig(const CXXRecordDecl *Lambda);
+ void mangleModuleNamePrefix(StringRef Name, bool IsPartition = false);
private:
@@ -473,22 +476,21 @@ class CXXNameMangler {
void mangleNameWithAbiTags(GlobalDecl GD,
const AbiTagList *AdditionalAbiTags);
- void mangleModuleName(const Module *M);
- void mangleModuleNamePrefix(StringRef Name);
+ void mangleModuleName(const NamedDecl *ND);
void mangleTemplateName(const TemplateDecl *TD,
const TemplateArgument *TemplateArgs,
unsigned NumTemplateArgs);
- void mangleUnqualifiedName(GlobalDecl GD,
+ void mangleUnqualifiedName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags) {
- mangleUnqualifiedName(GD, cast<NamedDecl>(GD.getDecl())->getDeclName(), UnknownArity,
- AdditionalAbiTags);
+ mangleUnqualifiedName(GD, cast<NamedDecl>(GD.getDecl())->getDeclName(), DC,
+ UnknownArity, AdditionalAbiTags);
}
void mangleUnqualifiedName(GlobalDecl GD, DeclarationName Name,
- unsigned KnownArity,
+ const DeclContext *DC, unsigned KnownArity,
const AbiTagList *AdditionalAbiTags);
- void mangleUnscopedName(GlobalDecl GD,
+ void mangleUnscopedName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags);
- void mangleUnscopedTemplateName(GlobalDecl GD,
+ void mangleUnscopedTemplateName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags);
void mangleSourceName(const IdentifierInfo *II);
void mangleRegCallName(const IdentifierInfo *II);
@@ -733,7 +735,8 @@ bool ItaniumMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) {
if (VD->isExternC())
return false;
- // Variables at global scope with non-internal linkage are not mangled.
+ // Variables at global scope are not mangled unless they have internal
+ // linkage or are specializations or are attached to a named module.
const DeclContext *DC = getEffectiveDeclContext(D);
// Check for extern variable declared locally.
if (DC->isFunctionOrMethod() && D->hasLinkage())
@@ -741,7 +744,8 @@ bool ItaniumMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) {
DC = getEffectiveParentContext(DC);
if (DC->isTranslationUnit() && D->getFormalLinkage() != InternalLinkage &&
!CXXNameMangler::shouldHaveAbiTags(*this, VD) &&
- !isa<VarTemplateSpecializationDecl>(VD))
+ !isa<VarTemplateSpecializationDecl>(VD) &&
+ !VD->getOwningModuleForLinkage())
return false;
}
@@ -1016,14 +1020,6 @@ void CXXNameMangler::mangleNameWithAbiTags(GlobalDecl GD,
return;
}
- // Do not mangle the owning module for an external linkage declaration.
- // This enables backwards-compatibility with non-modular code, and is
- // a valid choice since conflicts are not permitted by C++ Modules TS
- // [basic.def.odr]/6.2.
- if (!ND->hasExternalFormalLinkage())
- if (Module *M = ND->getOwningModuleForLinkage())
- mangleModuleName(M);
-
// Closures can require a nested-name mangling even if they're semantically
// in the global namespace.
if (const NamedDecl *PrefixND = getClosurePrefix(ND)) {
@@ -1035,38 +1031,35 @@ void CXXNameMangler::mangleNameWithAbiTags(GlobalDecl GD,
// Check if we have a template.
const TemplateArgumentList *TemplateArgs = nullptr;
if (GlobalDecl TD = isTemplate(GD, TemplateArgs)) {
- mangleUnscopedTemplateName(TD, AdditionalAbiTags);
+ mangleUnscopedTemplateName(TD, DC, AdditionalAbiTags);
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
return;
}
- mangleUnscopedName(GD, AdditionalAbiTags);
+ mangleUnscopedName(GD, DC, AdditionalAbiTags);
return;
}
mangleNestedName(GD, DC, AdditionalAbiTags);
}
-void CXXNameMangler::mangleModuleName(const Module *M) {
- // Implement the C++ Modules TS name mangling proposal; see
- // https://gcc.gnu.org/wiki/cxx-modules?action=AttachFile
- //
- // <module-name> ::= W <unscoped-name>+ E
- // ::= W <module-subst> <unscoped-name>* E
- Out << 'W';
- mangleModuleNamePrefix(M->Name);
- Out << 'E';
+void CXXNameMangler::mangleModuleName(const NamedDecl *ND) {
+ if (ND->isExternallyVisible())
+ if (Module *M = ND->getOwningModuleForLinkage())
+ mangleModuleNamePrefix(M->getPrimaryModuleInterfaceName());
}
-void CXXNameMangler::mangleModuleNamePrefix(StringRef Name) {
- // <module-subst> ::= _ <seq-id> # 0 < seq-id < 10
- // ::= W <seq-id - 10> _ # otherwise
+// <module-name> ::= <module-subname>
+// ::= <module-name> <module-subname>
+// ::= <substitution>
+// <module-subname> ::= W <source-name>
+// ::= W P <source-name>
+void CXXNameMangler::mangleModuleNamePrefix(StringRef Name, bool IsPartition) {
+ // <substitution> ::= S <seq-id> _
auto It = ModuleSubstitutions.find(Name);
if (It != ModuleSubstitutions.end()) {
- if (It->second < 10)
- Out << '_' << static_cast<char>('0' + It->second);
- else
- Out << 'W' << (It->second - 10) << '_';
+ Out << 'S';
+ mangleSeqID(It->second);
return;
}
@@ -1075,11 +1068,16 @@ void CXXNameMangler::mangleModuleNamePrefix(StringRef Name) {
auto Parts = Name.rsplit('.');
if (Parts.second.empty())
Parts.second = Parts.first;
- else
- mangleModuleNamePrefix(Parts.first);
+ else {
+ mangleModuleNamePrefix(Parts.first, IsPartition);
+ IsPartition = false;
+ }
+ Out << 'W';
+ if (IsPartition)
+ Out << 'P';
Out << Parts.second.size() << Parts.second;
- ModuleSubstitutions.insert({Name, ModuleSubstitutions.size()});
+ ModuleSubstitutions.insert({Name, SeqID++});
}
void CXXNameMangler::mangleTemplateName(const TemplateDecl *TD,
@@ -1088,27 +1086,27 @@ void CXXNameMangler::mangleTemplateName(const TemplateDecl *TD,
const DeclContext *DC = Context.getEffectiveDeclContext(TD);
if (DC->isTranslationUnit() || isStdNamespace(DC)) {
- mangleUnscopedTemplateName(TD, nullptr);
+ mangleUnscopedTemplateName(TD, DC, nullptr);
mangleTemplateArgs(asTemplateName(TD), TemplateArgs, NumTemplateArgs);
} else {
mangleNestedName(TD, TemplateArgs, NumTemplateArgs);
}
}
-void CXXNameMangler::mangleUnscopedName(GlobalDecl GD,
+void CXXNameMangler::mangleUnscopedName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags) {
- const NamedDecl *ND = cast<NamedDecl>(GD.getDecl());
// <unscoped-name> ::= <unqualified-name>
// ::= St <unqualified-name> # ::std::
- if (isStdNamespace(Context.getEffectiveDeclContext(ND)))
+ assert(!isa<LinkageSpecDecl>(DC) && "unskipped LinkageSpecDecl");
+ if (isStdNamespace(DC))
Out << "St";
- mangleUnqualifiedName(GD, AdditionalAbiTags);
+ mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
}
void CXXNameMangler::mangleUnscopedTemplateName(
- GlobalDecl GD, const AbiTagList *AdditionalAbiTags) {
+ GlobalDecl GD, const DeclContext *DC, const AbiTagList *AdditionalAbiTags) {
const TemplateDecl *ND = cast<TemplateDecl>(GD.getDecl());
// <unscoped-template-name> ::= <unscoped-name>
// ::= <substitution>
@@ -1121,9 +1119,10 @@ void CXXNameMangler::mangleUnscopedTemplateName(
"template template param cannot have abi tags");
mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
} else if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND)) {
- mangleUnscopedName(GD, AdditionalAbiTags);
+ mangleUnscopedName(GD, DC, AdditionalAbiTags);
} else {
- mangleUnscopedName(GD.getWithDecl(ND->getTemplatedDecl()), AdditionalAbiTags);
+ mangleUnscopedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
+ AdditionalAbiTags);
}
addSubstitution(ND);
@@ -1399,15 +1398,19 @@ void CXXNameMangler::mangleUnresolvedName(
mangleTemplateArgs(TemplateName(), TemplateArgs, NumTemplateArgs);
}
-void CXXNameMangler::mangleUnqualifiedName(GlobalDecl GD,
- DeclarationName Name,
- unsigned KnownArity,
- const AbiTagList *AdditionalAbiTags) {
+void CXXNameMangler::mangleUnqualifiedName(
+ GlobalDecl GD, DeclarationName Name, const DeclContext *DC,
+ unsigned KnownArity, const AbiTagList *AdditionalAbiTags) {
const NamedDecl *ND = cast_or_null<NamedDecl>(GD.getDecl());
- unsigned Arity = KnownArity;
- // <unqualified-name> ::= <operator-name>
+ // <unqualified-name> ::= [<module-name>] <operator-name>
// ::= <ctor-dtor-name>
- // ::= <source-name>
+ // ::= [<module-name>] <source-name>
+ // ::= [<module-name>] DC <source-name>* E
+
+ if (ND && DC && DC->isFileContext())
+ mangleModuleName(ND);
+
+ unsigned Arity = KnownArity;
switch (Name.getNameKind()) {
case DeclarationName::Identifier: {
const IdentifierInfo *II = Name.getAsIdentifierInfo();
@@ -1418,8 +1421,6 @@ void CXXNameMangler::mangleUnqualifiedName(GlobalDecl GD,
//
// <unqualified-name> ::= DC <source-name>* E
//
- // These can never be referenced across translation units, so we do
- // not need a cross-vendor mangling for anything other than demanglers.
// Proposed on cxx-abi-dev on 2016-08-12
Out << "DC";
for (auto *BD : DD->bindings())
@@ -1716,7 +1717,7 @@ void CXXNameMangler::mangleNestedName(GlobalDecl GD,
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
} else {
manglePrefix(DC, NoFunction);
- mangleUnqualifiedName(GD, AdditionalAbiTags);
+ mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
}
Out << 'E';
@@ -1746,7 +1747,7 @@ void CXXNameMangler::mangleNestedNameWithClosurePrefix(
Out << 'N';
mangleClosurePrefix(PrefixND);
- mangleUnqualifiedName(GD, AdditionalAbiTags);
+ mangleUnqualifiedName(GD, nullptr, AdditionalAbiTags);
Out << 'E';
}
@@ -1824,7 +1825,7 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD,
// Mangle the name relative to the closest enclosing function.
// equality ok because RD derived from ND above
if (D == RD) {
- mangleUnqualifiedName(RD, AdditionalAbiTags);
+ mangleUnqualifiedName(RD, DC, AdditionalAbiTags);
} else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
if (const NamedDecl *PrefixND = getClosurePrefix(BD))
mangleClosurePrefix(PrefixND, true /*NoFunction*/);
@@ -1855,7 +1856,7 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD,
assert(!AdditionalAbiTags && "Block cannot have additional abi tags");
mangleUnqualifiedBlock(BD);
} else {
- mangleUnqualifiedName(GD, AdditionalAbiTags);
+ mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
}
if (const NamedDecl *ND = dyn_cast<NamedDecl>(RD ? RD : D)) {
@@ -2082,10 +2083,11 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) {
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
} else if (const NamedDecl *PrefixND = getClosurePrefix(ND)) {
mangleClosurePrefix(PrefixND, NoFunction);
- mangleUnqualifiedName(ND, nullptr);
+ mangleUnqualifiedName(ND, nullptr, nullptr);
} else {
- manglePrefix(Context.getEffectiveDeclContext(ND), NoFunction);
- mangleUnqualifiedName(ND, nullptr);
+ const DeclContext *DC = Context.getEffectiveDeclContext(ND);
+ manglePrefix(DC, NoFunction);
+ mangleUnqualifiedName(ND, DC, nullptr);
}
addSubstitution(ND);
@@ -2138,11 +2140,13 @@ void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) {
mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
} else {
- manglePrefix(Context.getEffectiveDeclContext(ND), NoFunction);
+ const DeclContext *DC = Context.getEffectiveDeclContext(ND);
+ manglePrefix(DC, NoFunction);
if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
- mangleUnqualifiedName(GD, nullptr);
+ mangleUnqualifiedName(GD, DC, nullptr);
else
- mangleUnqualifiedName(GD.getWithDecl(ND->getTemplatedDecl()), nullptr);
+ mangleUnqualifiedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
+ nullptr);
}
addSubstitution(ND);
@@ -2183,8 +2187,9 @@ void CXXNameMangler::mangleClosurePrefix(const NamedDecl *ND, bool NoFunction) {
mangleTemplatePrefix(TD, NoFunction);
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
} else {
- manglePrefix(Context.getEffectiveDeclContext(ND), NoFunction);
- mangleUnqualifiedName(ND, nullptr);
+ const auto *DC = Context.getEffectiveDeclContext(ND);
+ manglePrefix(DC, NoFunction);
+ mangleUnqualifiedName(ND, DC, nullptr);
}
Out << 'M';
@@ -6026,6 +6031,9 @@ bool CXXNameMangler::isSpecializedAs(QualType S, llvm::StringRef Name,
if (TemplateArgs[0].getAsType() != A)
return false;
+ if (SD->getSpecializedTemplate()->getOwningModuleForLinkage())
+ return false;
+
return true;
}
@@ -6057,6 +6065,9 @@ bool CXXNameMangler::isStdCharSpecialization(
!isSpecializedAs(TemplateArgs[2].getAsType(), "allocator", A))
return false;
+ if (SD->getSpecializedTemplate()->getOwningModuleForLinkage())
+ return false;
+
return true;
}
@@ -6074,6 +6085,9 @@ bool CXXNameMangler::mangleStandardSubstitution(const NamedDecl *ND) {
if (!isStdNamespace(Context.getEffectiveDeclContext(TD)))
return false;
+ if (TD->getOwningModuleForLinkage())
+ return false;
+
// <substitution> ::= Sa # ::std::allocator
if (TD->getIdentifier()->isStr("allocator")) {
Out << "Sa";
@@ -6093,6 +6107,9 @@ bool CXXNameMangler::mangleStandardSubstitution(const NamedDecl *ND) {
if (!isStdNamespace(Context.getEffectiveDeclContext(SD)))
return false;
+ if (SD->getSpecializedTemplate()->getOwningModuleForLinkage())
+ return false;
+
// <substitution> ::= Ss # ::std::basic_string<char,
// ::std::char_traits<char>,
// ::std::allocator<char> >
@@ -6447,6 +6464,19 @@ void ItaniumMangleContextImpl::mangleLambdaSig(const CXXRecordDecl *Lambda,
Mangler.mangleLambdaSig(Lambda);
}
+void ItaniumMangleContextImpl::mangleModuleInitializer(const Module *M,
+ raw_ostream &Out) {
+ CXXNameMangler Mangler(*this, Out);
+ Mangler.getStream() << "_ZGI";
+ Mangler.mangleModuleNamePrefix(M->getPrimaryModuleInterfaceName());
+ if (M->isModulePartition()) {
+ auto Partition = M->Name.find(':');
+ Mangler.mangleModuleNamePrefix(
+ StringRef(&M->Name[Partition + 1], M->Name.size() - Partition - 1),
+ /*IsPartition*/ true);
+ }
+}
+
ItaniumMangleContext *ItaniumMangleContext::create(ASTContext &Context,
DiagnosticsEngine &Diags) {
return new ItaniumMangleContextImpl(
diff --git a/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp b/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp
index 6eb5639d3024e..0e300a1260326 100644
--- a/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp
+++ b/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp
@@ -1,21 +1,21 @@
// RUN: %clang_cc1 -fmodules-ts %S/module.cppm -triple %itanium_abi_triple -emit-module-interface -o %t
// RUN: %clang_cc1 -fmodules-ts %s -triple %itanium_abi_triple -fmodule-file=%t -emit-llvm -o - | FileCheck %s --implicit-check-not=unused --implicit-check-not=global_module
-// CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
-// CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @const_var_exported = available_externally {{(dso_local )?}}constant i32 3,
+// CHECK-DAG: @_ZW6Module19extern_var_exported = external {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module19inline_var_exported = linkonce_odr {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module18const_var_exported = available_externally {{(dso_local )?}}constant i32 3,
//
-// CHECK-DAG: @_ZW6ModuleE25extern_var_module_linkage = external {{(dso_local )?}}global
-// CHECK-DAG: @_ZW6ModuleE25inline_var_module_linkage = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @_ZW6ModuleE25static_var_module_linkage = available_externally {{(dso_local )?}}global i32 0,
-// CHECK-DAG: @_ZW6ModuleE24const_var_module_linkage = available_externally {{(dso_local )?}}constant i32 3,
+// CHECK-DAG: @_ZW6Module25extern_var_module_linkage = external {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module25inline_var_module_linkage = linkonce_odr {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module25static_var_module_linkage = available_externally {{(dso_local )?}}global i32 0,
+// CHECK-DAG: @_ZW6Module24const_var_module_linkage = available_externally {{(dso_local )?}}constant i32 3,
module Module;
void use() {
- // CHECK: define linkonce_odr {{.*}}@_Z20used_inline_exportedv
+ // CHECK: define linkonce_odr {{.*}}@_ZW6Module20used_inline_exportedv
used_inline_exported();
- // CHECK: declare {{.*}}@_Z18noninline_exportedv
+ // CHECK: declare {{.*}}@_ZW6Module18noninline_exportedv
noninline_exported();
(void)&extern_var_exported;
@@ -23,13 +23,13 @@ void use() {
(void)&const_var_exported;
// FIXME: This symbol should not be visible here.
- // CHECK: declare {{.*}}@_ZW6ModuleE26used_static_module_linkagev
+ // CHECK: declare {{.*}}@_ZW6Module26used_static_module_linkagev
used_static_module_linkage();
- // CHECK: define linkonce_odr {{.*}}@_ZW6ModuleE26used_inline_module_linkagev
+ // CHECK: define linkonce_odr {{.*}}@_ZW6Module26used_inline_module_linkagev
used_inline_module_linkage();
- // CHECK: declare {{.*}}@_ZW6ModuleE24noninline_module_linkagev
+ // CHECK: declare {{.*}}@_ZW6Module24noninline_module_linkagev
noninline_module_linkage();
(void)&extern_var_module_linkage;
diff --git a/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm b/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm
index 65861f84bae6a..6be0611d779ce 100644
--- a/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm
+++ b/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm
@@ -6,24 +6,24 @@
// CHECK-DAG: @_ZL23const_var_global_module = internal constant
//
// For ABI compatibility, these symbols do not include the module name.
-// CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module19extern_var_exported = external {{(dso_local )?}}global
// FIXME: Should this be 'weak_odr global'? Presumably it must be, since we
// can discard this global and its initializer (if any), and other TUs are not
// permitted to run the initializer for this variable.
-// CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @const_var_exported = {{(dso_local )?}}constant
+// CHECK-DAG: @_ZW6Module19inline_var_exported = linkonce_odr {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module18const_var_exported = {{(dso_local )?}}constant
//
-// CHECK-DAG: @_ZW6ModuleE25extern_var_module_linkage = external {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module25extern_var_module_linkage = external {{(dso_local )?}}global
// FIXME: Should this be 'weak_odr global'? Presumably it must be, since we
// can discard this global and its initializer (if any), and other TUs are not
// permitted to run the initializer for this variable.
-// CHECK-DAG: @_ZW6ModuleE25inline_var_module_linkage = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @_ZW6ModuleE25static_var_module_linkage = {{(dso_local )?}}global
-// CHECK-DAG: @_ZW6ModuleE24const_var_module_linkage = {{(dso_local )?}}constant
+// CHECK-DAG: @_ZW6Module25inline_var_module_linkage = linkonce_odr {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module25static_var_module_linkage = {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module24const_var_module_linkage = {{(dso_local )?}}constant
//
-// CHECK-DAG: @_ZW6ModuleE25unused_var_module_linkage = {{(dso_local )?}}global i32 4
-// CHECK-DAG: @_ZW6ModuleE32unused_static_var_module_linkage = {{(dso_local )?}}global i32 5
-// CHECK-DAG: @_ZW6ModuleE31unused_const_var_module_linkage = {{(dso_local )?}}constant i32 7
+// CHECK-DAG: @_ZW6Module25unused_var_module_linkage = {{(dso_local )?}}global i32 4
+// CHECK-DAG: @_ZW6Module32unused_static_var_module_linkage = {{(dso_local )?}}global i32 5
+// CHECK-DAG: @_ZW6Module31unused_const_var_module_linkage = {{(dso_local )?}}constant i32 7
static void unused_static_global_module() {}
static void used_static_global_module() {}
@@ -64,7 +64,7 @@ export {
inline int inline_var_exported;
const int const_var_exported = 3;
- // CHECK: define {{(dso_local )?}}void {{.*}}@_Z18noninline_exportedv
+ // CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6Module18noninline_exportedv
void noninline_exported() {
(void)&extern_var_exported;
(void)&inline_var_exported;
@@ -75,9 +75,9 @@ export {
// FIXME: Ideally we wouldn't emit this as its name is not visible outside this
// TU, but this module interface might contain a template that can use this
// function so we conservatively emit it for now.
-// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6ModuleE28unused_static_module_linkagev
+// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6Module28unused_static_module_linkagev
static void unused_static_module_linkage() {}
-// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6ModuleE26used_static_module_linkagev
+// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6Module26used_static_module_linkagev
static void used_static_module_linkage() {}
inline void unused_inline_module_linkage() {}
@@ -88,10 +88,10 @@ inline int inline_var_module_linkage;
static int static_var_module_linkage;
const int const_var_module_linkage = 3;
-// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6ModuleE24noninline_module_linkagev
+// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6Module24noninline_module_linkagev
void noninline_module_linkage() {
used_static_module_linkage();
- // CHECK: define linkonce_odr {{.*}}@_ZW6ModuleE26used_inline_module_linkagev
+ // CHECK: define linkonce_odr {{.*}}@_ZW6Module26used_inline_module_linkagev
used_inline_module_linkage();
(void)&extern_var_module_linkage;
@@ -109,5 +109,5 @@ struct a {
struct b {};
struct c {};
};
-// CHECK: define {{(dso_local )?}}void @_ZW6ModuleE1fW_0EN1a1bEW_0ENS_1cE(
+// CHECK: define {{(dso_local )?}}void @_ZW6Module1fNS_1a1bENS0_1cE(
void f(a::b, a::c) {}
diff --git a/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp b/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp
index d55e063797118..6c795b99694a2 100644
--- a/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp
+++ b/clang/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp
@@ -1,16 +1,16 @@
// RUN: %clang_cc1 -fmodules-ts %S/module.cppm -triple %itanium_abi_triple -emit-module-interface -o %t
// RUN: %clang_cc1 -fmodules-ts %s -triple %itanium_abi_triple -fmodule-file=%t -emit-llvm -o - | FileCheck %s --implicit-check-not=unused --implicit-check-not=global_module
-// CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
-// CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @const_var_exported = available_externally {{(dso_local )?}}constant i32 3
+// CHECK-DAG: @_ZW6Module19extern_var_exported = external {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module19inline_var_exported = linkonce_odr {{(dso_local )?}}global
+// CHECK-DAG: @_ZW6Module18const_var_exported = available_externally {{(dso_local )?}}constant i32 3
import Module;
void use() {
- // CHECK: define linkonce_odr {{.*}}@_Z20used_inline_exportedv
+ // CHECK: define linkonce_odr {{.*}}@_ZW6Module20used_inline_exportedv
used_inline_exported();
- // CHECK: declare {{.*}}@_Z18noninline_exportedv
+ // CHECK: declare {{.*}}@_ZW6Module18noninline_exportedv
noninline_exported();
(void)&extern_var_exported;
diff --git a/clang/test/CXX/modules-ts/basic/basic.link/p3.cppm b/clang/test/CXX/modules-ts/basic/basic.link/p3.cppm
index 4fca204b39267..7e809d766d3aa 100644
--- a/clang/test/CXX/modules-ts/basic/basic.link/p3.cppm
+++ b/clang/test/CXX/modules-ts/basic/basic.link/p3.cppm
@@ -3,9 +3,9 @@
export module M;
-// CHECK-DAG: @_ZW1ME1a ={{.*}} constant i32 1
+// CHECK-DAG: @_ZW1M1a ={{.*}} constant i32 1
const int a = 1;
-// CHECK-DAG: @b ={{.*}} constant i32 2
+// CHECK-DAG: @_ZW1M1b ={{.*}} constant i32 2
export const int b = 2;
export int f() { return a + b; }
diff --git a/clang/test/CXX/modules-ts/codegen-basics.cppm b/clang/test/CXX/modules-ts/codegen-basics.cppm
index 7ccf6de1f3761..cfade9a494f0d 100644
--- a/clang/test/CXX/modules-ts/codegen-basics.cppm
+++ b/clang/test/CXX/modules-ts/codegen-basics.cppm
@@ -4,14 +4,14 @@
export module FooBar;
export {
- // CHECK-DAG: define{{.*}} i32 @_Z1fv(
+ // CHECK-DAG: define{{.*}} i32 @_ZW6FooBar1fv(
int f() { return 0; }
}
-// CHECK-DAG: define weak_odr void @_ZW6FooBarE2f2v(
+// CHECK-DAG: define weak_odr void @_ZW6FooBar2f2v(
inline void f2() {}
-// CHECK-DAG: define{{.*}} void @_ZW6FooBarE2f3v(
+// CHECK-DAG: define{{.*}} void @_ZW6FooBar2f3v(
static void f3() {}
export void use_f3() { f3(); }
diff --git a/clang/test/CodeGenCXX/Inputs/cxx20-module-impl-1a.cpp b/clang/test/CodeGenCXX/Inputs/cxx20-module-impl-1a.cpp
new file mode 100644
index 0000000000000..ff3ce97d0d312
--- /dev/null
+++ b/clang/test/CodeGenCXX/Inputs/cxx20-module-impl-1a.cpp
@@ -0,0 +1,3 @@
+export module Foo;
+export void Exported();
+void Module();
diff --git a/clang/test/CodeGenCXX/Inputs/cxx20-module-std-subst-2a.cpp b/clang/test/CodeGenCXX/Inputs/cxx20-module-std-subst-2a.cpp
new file mode 100644
index 0000000000000..53f0ec4cf29bd
--- /dev/null
+++ b/clang/test/CodeGenCXX/Inputs/cxx20-module-std-subst-2a.cpp
@@ -0,0 +1,11 @@
+module;
+# 5 __FILE__ 1
+namespace std {
+template <typename A> struct allocator {};
+template <typename C, typename T, typename A>
+class basic_string;
+} // namespace std
+# 12 "" 2
+export module RenameString;
+export template <typename C, typename T>
+using str = std::basic_string<C, T, std::allocator<C>>;
diff --git a/clang/test/CodeGenCXX/cxx20-module-decomp-1.cpp b/clang/test/CodeGenCXX/cxx20-module-decomp-1.cpp
new file mode 100644
index 0000000000000..e14f02a9e78ff
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-decomp-1.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+
+export module Foo;
+
+// CHECK-DAG: @_ZW3Foo3ary =
+int ary[2];
+
+// CHECK-DAG: @_ZW3FooDC1a1bE =
+export auto &[a, b] = ary;
+
+namespace N {
+// CHECK-DAG: @_ZN1NW3FooDC1a1bEE =
+export auto &[a, b] = ary;
+// CHECK-DAG: @_ZN1NW3FooDC1c1dEE =
+auto &[c, d] = ary;
+// FIXME: We mangle the module name here, as we give this ModuleInternalLinkage
+// That's no longer needed.
+// CHECK DAG: @_ZN1MDC1e1fEE =
+static auto &__attribute__((used))[e, f] = ary;
+} // namespace N
diff --git a/clang/test/CodeGenCXX/cxx20-module-extern-1.cppm b/clang/test/CodeGenCXX/cxx20-module-extern-1.cppm
new file mode 100644
index 0000000000000..1d81ff5f1d6fa
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-extern-1.cppm
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+
+// module-purview extern "C++" semantics not implemented
+// XFAIL: *
+
+export module FOO;
+extern "C++" export class A;
+export class B;
+
+// CHECK-DAG: void @_ZW3FOO3FooP1APNS_1B(
+export void Foo (A *, B*) {
+}
+
+extern "C++" {
+// CHECK-DAG: void @_Z3BarP1APW3FOO1B(
+export void Bar (A *, B*) {
+}
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-impl-1a.cpp b/clang/test/CodeGenCXX/cxx20-module-impl-1a.cpp
new file mode 100644
index 0000000000000..5c0fac9bf2b5c
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-impl-1a.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++20 %S/Inputs/cxx20-module-impl-1a.cpp -triple %itanium_abi_triple -emit-module-interface -o %t
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -fmodule-file=%t -emit-llvm -o - | FileCheck %s
+
+module Foo;
+
+// CHECK-DAG: @_ZW3Foo8Exportedv(
+void Exported() {
+}
+
+// CHECK-DAG: @_ZW3Foo6Modulev(
+void Module() {
+}
+
+// CHECK-DAG: @_ZW3Foo7Module2v(
+void Module2() {
+}
+
diff --git a/clang/test/CodeGenCXX/cxx20-module-nested-1.cppm b/clang/test/CodeGenCXX/cxx20-module-nested-1.cppm
new file mode 100644
index 0000000000000..e2fa760d043f6
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-nested-1.cppm
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++20 -fmodules-ts %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+module;
+# 4 __FILE__ 1
+namespace Outer::Inner {
+class X;
+// CHECK-DAG: define dso_local void @_ZN5Outer5Inner3BarERNS0_1XE(
+void Bar (X &) {}
+} // namespace Outer::Inner
+# 10 "" 2
+export module FOO;
+namespace Outer {
+class Y;
+namespace Inner {
+// CHECK-DAG: define dso_local void @_ZN5Outer5InnerW3FOO2FnERNS0_1XERNS_S1_1YE(
+void Fn (X &, Y &){} // #1
+} // namespace Inner
+} // namespace Outer
diff --git a/clang/test/CodeGenCXX/cxx20-module-nested-2.cppm b/clang/test/CodeGenCXX/cxx20-module-nested-2.cppm
new file mode 100644
index 0000000000000..7fbf2c0239dcf
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-nested-2.cppm
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fmodules-ts %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+export module FOO;
+namespace Outer {
+class Y;
+class Inner {
+ class X;
+ void Fn (X &, Y &); // #2
+};
+// CHECK-DAG: define dso_local void @_ZN5OuterW3FOO5Inner2FnERNS1_1XERNS_S0_1YE(
+void Inner::Fn (X &, Y &) {}
+}
+
diff --git a/clang/test/CodeGenCXX/cxx20-module-part-1a.cpp b/clang/test/CodeGenCXX/cxx20-module-part-1a.cpp
new file mode 100644
index 0000000000000..62a71c30dac66
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-part-1a.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+export module Foo:inter;
+
+// CHECK-DAG: @_ZW3Foo4Frobv(
+export void Frob() {
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-part-1b.cpp b/clang/test/CodeGenCXX/cxx20-module-part-1b.cpp
new file mode 100644
index 0000000000000..50073c2b2db08
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-part-1b.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+export module Foo:impl;
+
+// CHECK-DAG: @_ZW3Foo4Quuxv(
+export void Quux() {
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-part-1c.cpp b/clang/test/CodeGenCXX/cxx20-module-part-1c.cpp
new file mode 100644
index 0000000000000..4649df0e684a8
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-part-1c.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++20 %S/cxx20-module-part-1a.cpp -triple %itanium_abi_triple -emit-module-interface -o %t-inter
+// RUN: %clang_cc1 -std=c++20 %S/cxx20-module-part-1b.cpp -triple %itanium_abi_triple -emit-module-interface -o %t-impl
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -fmodule-file=Foo:inter=%t-inter -fmodule-file=Foo:impl=%t-impl -emit-llvm -o - | FileCheck %s
+export module Foo;
+export import :inter;
+import :impl;
+
+void Wrap() {
+ // CHECK: call void @_ZW3Foo4Frobv()
+ Frob();
+ // CHECK: call void @_ZW3Foo4Quuxv()
+ Quux();
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-std-subst-1.cppm b/clang/test/CodeGenCXX/cxx20-module-std-subst-1.cppm
new file mode 100644
index 0000000000000..7d4992a2adce8
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-std-subst-1.cppm
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+
+module;
+# 5 __FILE__ 1
+class Pooh;
+class Piglet;
+# 8 "" 2
+
+export module std; // might happen, you can't say it won't!
+
+namespace std {
+export template<typename T> class allocator {
+// just for testing, not real!
+void M (T *);
+template <typename U> U *N (T *);
+};
+
+template<typename T> void allocator<T>::M (T *) {}
+template<typename T> template<typename U> U *allocator<T>::N (T *) {
+return nullptr;
+}
+
+// CHECK-DAG: void @_ZNStW3std9allocatorIiE1MEPi(
+template void allocator<int>::M (int *);
+// CHECK-DAG: @_ZNStW3std9allocatorIiE1NIfEEPT_Pi(
+template float *allocator<int>::N<float> (int *);
+}
+
+// CHECK-DAG: @_ZNStW3std9allocatorI4PoohE1MEPS1_(
+template void std::allocator<Pooh>::M (Pooh *);
+// CHECK-DAG: @_ZNStW3std9allocatorI4PoohE1NI6PigletEEPT_PS1_(
+template Piglet *std::allocator<Pooh>::N<Piglet> (Pooh *);
diff --git a/clang/test/CodeGenCXX/cxx20-module-std-subst-2b.cpp b/clang/test/CodeGenCXX/cxx20-module-std-subst-2b.cpp
new file mode 100644
index 0000000000000..04711970edda2
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-std-subst-2b.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -std=c++20 %S/Inputs/cxx20-module-std-subst-2a.cpp -triple %itanium_abi_triple -emit-module-interface -o %t
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -fmodule-file=%t -emit-llvm -o - | FileCheck %s
+
+export module Foo;
+import RenameString;
+
+namespace std {
+template <typename T> struct char_traits {};
+} // namespace std
+
+// use Sb mangling, not Ss as this is not global-module std::char_traits
+// std::char_traits.
+// CHECK-DAG: void @_ZW3Foo1fRSbIcStS_11char_traitsIcESaIcEE(
+void f(str<char, std::char_traits<char>> &s) {
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-std-subst-2c.cpp b/clang/test/CodeGenCXX/cxx20-module-std-subst-2c.cpp
new file mode 100644
index 0000000000000..36e2e67e5c29a
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-std-subst-2c.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -std=c++20 %S/Inputs/cxx20-module-std-subst-2a.cpp -triple %itanium_abi_triple -emit-module-interface -o %t
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -fmodule-file=%t -emit-llvm -o - | FileCheck %s
+module;
+# 5 __FILE__ 1
+namespace std {
+template <typename A> struct char_traits {};
+} // namespace std
+# 9 "" 2
+export module Bar;
+import RenameString;
+
+// Use Ss as this is global-module std::char_traits
+// CHECK-DAG: void @_ZW3Bar1gRSs(
+void g(str<char, std::char_traits<char>> &s) {
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-sub-1a.cppm b/clang/test/CodeGenCXX/cxx20-module-sub-1a.cppm
new file mode 100644
index 0000000000000..8267c38c7f5c0
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-sub-1a.cppm
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+
+export module FOO.BAR;
+export class A;
+namespace Bob {
+export class B;
+
+// CHECK-DAG: void @_ZN3BobW3FOOW3BAR3BarEPS1_1APNS_S1_1BE(
+export void Bar (A *, B*) {
+}
+}
+
+// CHECK-DAG: void @_ZW3FOOW3BAR3FooPS0_1APN3BobS0_1BE(
+export void Foo (A *, Bob::B*) {
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-sub-1b.cppm b/clang/test/CodeGenCXX/cxx20-module-sub-1b.cppm
new file mode 100644
index 0000000000000..324554c61128e
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-sub-1b.cppm
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++20 %S/cxx20-module-sub-1a.cppm -triple %itanium_abi_triple -emit-module-interface -o %t
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -fmodule-file=%t -emit-llvm -o - | FileCheck %s
+
+export module FOO.BAZ;
+import FOO.BAR;
+
+namespace Bob {
+
+// CHECK-DAG: void @_ZN3BobW3FOOW3BAZ3FooEPS0_W3BAR1APNS_S2_1BE(
+void Foo (A *, B*) {
+}
+}
+
+// CHECK-DAG: void @_ZW3FOOW3BAZ3BarPS_W3BAR1APN3BobS1_1BE(
+void Bar (A *, Bob::B*) {
+}
diff --git a/clang/test/CodeGenCXX/cxx20-module-tmpl-1.cppm b/clang/test/CodeGenCXX/cxx20-module-tmpl-1.cppm
new file mode 100644
index 0000000000000..85a613ad75623
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx20-module-tmpl-1.cppm
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s
+
+export module FOO;
+
+class One;
+class Two;
+
+export template<typename T> struct TPL
+{
+void M (T *);
+template<typename U> void N (T *, U*);
+};
+
+template<typename T>
+void TPL<T>::M (T *) {}
+
+template<typename T> template<typename U> void TPL<T>::N (T *, U*) {}
+
+// CHECK-DAG: void @_ZNW3FOO3TPLIS_3OneE1MEPS1_(
+template void TPL<One>::M (One *);
+// CHECK-DAG: void @_ZNW3FOO3TPLIS_3OneE1NIS_3TwoEEvPS1_PT_(
+template void TPL<One>::N<Two> (One *, Two *);
+
+namespace NMS {
+class One;
+class Two;
+
+export template<typename T> struct TPL
+{
+void M (T *);
+template<typename U> void N (T *, U*);
+};
+
+template<typename T>
+void TPL<T>::M (T *) {}
+
+template<typename T> template<typename U> void TPL<T>::N (T *, U*) {}
+
+// CHECK-DAG: void @_ZN3NMSW3FOO3TPLINS_S0_3OneEE1MEPS2_(
+template void TPL<One>::M (One *);
+// CHECK-DAG: void @_ZN3NMSW3FOO3TPLINS_S0_3OneEE1NINS_S0_3TwoEEEvPS2_PT_
+template void TPL<One>::N<Two> (One *, Two *);
+}
More information about the cfe-commits
mailing list