[c++2a] Implement semantic restrictions for 'export' declarations.

llvm-svn: 358932
This commit is contained in:
Richard Smith 2019-04-22 22:50:11 +00:00
parent 68602ab2f3
commit e181de7f4f
19 changed files with 444 additions and 59 deletions

View File

@ -4239,8 +4239,10 @@ public:
SourceLocation getRBraceLoc() const { return RBraceLoc; }
void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
bool hasBraces() const { return RBraceLoc.isValid(); }
SourceLocation getEndLoc() const LLVM_READONLY {
if (RBraceLoc.isValid())
if (hasBraces())
return RBraceLoc;
// No braces: get the end location of the (only) declaration in context
// (if present).

View File

@ -600,10 +600,6 @@ public:
return getModuleOwnershipKind() == ModuleOwnershipKind::ModulePrivate;
}
/// Whether this declaration is exported (by virtue of being lexically
/// within an ExportDecl or by being a NamespaceDecl).
bool isExported() const;
/// Return true if this declaration has an attribute which acts as
/// definition of the entity, such as 'alias' or 'ifunc'.
bool hasDefiningAttr() const;

View File

@ -168,6 +168,7 @@ def ExtraTokens : DiagGroup<"extra-tokens">;
def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">;
def CXX11ExtraSemi : DiagGroup<"c++11-extra-semi">;
def EmptyInitStatement : DiagGroup<"empty-init-stmt">;
def ExportUnnamed : DiagGroup<"export-unnamed">;
def ExtraSemiStmt : DiagGroup<"extra-semi-stmt", [EmptyInitStatement]>;
def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi,
CXX11ExtraSemi]>;

View File

@ -9273,8 +9273,27 @@ def err_module_decl_not_at_start : Error<
def note_global_module_introducer_missing : Note<
"add 'module;' to the start of the file to introduce a "
"global module fragment">;
def err_export_within_anonymous_namespace : Error<
"export declaration appears within anonymous namespace">;
def note_anonymous_namespace : Note<"anonymous namespace begins here">;
def ext_export_no_name_block : ExtWarn<
"ISO C++20 does not permit %select{an empty|a static_assert}0 declaration "
"to appear in an export block">, InGroup<ExportUnnamed>;
def ext_export_no_names : ExtWarn<
"ISO C++20 does not permit a declaration that does not introduce any names "
"to be exported">, InGroup<ExportUnnamed>;
def note_export : Note<"export block begins here">;
def err_export_no_name : Error<
"%select{empty|static_assert|asm}0 declaration cannot be exported">;
def ext_export_using_directive : ExtWarn<
"ISO C++20 does not permit using directive to be exported">,
InGroup<DiagGroup<"export-using-directive">>;
def err_export_within_export : Error<
"export declaration appears within another export declaration">;
def err_export_internal : Error<
"declaration of %0 with internal linkage cannot be exported">;
def err_export_using_internal : Error<
"using declaration referring to %0 with internal linkage cannot be exported">;
def err_export_not_in_module_interface : Error<
"export declaration can only be used within a module interface unit"
"%select{ after the module declaration|}0">;

View File

@ -1651,6 +1651,9 @@ private:
/// The modules we're currently parsing.
llvm::SmallVector<ModuleScope, 16> ModuleScopes;
/// Namespace definitions that we will export when they finish.
llvm::SmallPtrSet<const NamespaceDecl*, 8> DeferredExportedNamespaces;
/// Get the module whose scope we are currently within.
Module *getCurrentModule() const {
return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;

View File

@ -430,22 +430,6 @@ bool Decl::isReferenced() const {
return false;
}
bool Decl::isExported() const {
if (isModulePrivate())
return false;
// Namespaces are always exported.
if (isa<TranslationUnitDecl>(this) || isa<NamespaceDecl>(this))
return true;
// Otherwise, this is a strictly lexical check.
for (auto *DC = getLexicalDeclContext(); DC; DC = DC->getLexicalParent()) {
if (cast<Decl>(DC)->isModulePrivate())
return false;
if (isa<ExportDecl>(DC))
return true;
}
return false;
}
ExternalSourceSymbolAttr *Decl::getExternalSourceSymbolAttr() const {
const Decl *Definition = nullptr;
if (auto *ID = dyn_cast<ObjCInterfaceDecl>(this)) {

View File

@ -437,9 +437,10 @@ Decl *Parser::ParseExportDeclaration() {
// The Modules TS draft says "An export-declaration shall declare at least one
// entity", but the intent is that it shall contain at least one declaration.
if (Tok.is(tok::r_brace))
if (Tok.is(tok::r_brace) && getLangOpts().ModulesTS) {
Diag(ExportLoc, diag::err_export_empty)
<< SourceRange(ExportLoc, Tok.getLocation());
}
while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
Tok.isNot(tok::eof)) {

View File

@ -9037,6 +9037,9 @@ void Sema::ActOnFinishNamespaceDef(Decl *Dcl, SourceLocation RBrace) {
PopDeclContext();
if (Namespc->hasAttr<VisibilityAttr>())
PopPragmaVisibility(true, RBrace);
// If this namespace contains an export-declaration, export it now.
if (DeferredExportedNamespaces.erase(Namespc))
Dcl->setModuleOwnershipKind(Decl::ModuleOwnershipKind::VisibleWhenImported);
}
CXXRecordDecl *Sema::getStdBadAlloc() const {

View File

@ -330,6 +330,14 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path);
}
/// Determine whether \p D is lexically within an export-declaration.
static const ExportDecl *getEnclosingExportDecl(const Decl *D) {
for (auto *DC = D->getLexicalDeclContext(); DC; DC = DC->getLexicalParent())
if (auto *ED = dyn_cast<ExportDecl>(DC))
return ED;
return nullptr;
}
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
SourceLocation ImportLoc,
@ -384,7 +392,7 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
// Re-export the module if needed.
if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) {
if (ExportLoc.isValid() || Import->isExported())
if (ExportLoc.isValid() || getEnclosingExportDecl(Import))
getCurrentModule()->Exports.emplace_back(Mod, false);
} else if (ExportLoc.isValid()) {
Diag(ExportLoc, diag::err_export_not_in_module_interface);
@ -516,11 +524,13 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc,
SourceLocation LBraceLoc) {
ExportDecl *D = ExportDecl::Create(Context, CurContext, ExportLoc);
// C++20 [module.interface]p1:
// Set this temporarily so we know the export-declaration was braced.
D->setRBraceLoc(LBraceLoc);
// C++2a [module.interface]p1:
// An export-declaration shall appear only [...] in the purview of a module
// interface unit. An export-declaration shall not appear directly or
// indirectly within an unnamed namespace or a private-module-fragment.
// FIXME: Check for the unnamed namespace case.
// indirectly within [...] a private-module-fragment.
if (ModuleScopes.empty() || !ModuleScopes.back().Module->isModulePurview()) {
Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0;
} else if (!ModuleScopes.back().ModuleInterface) {
@ -534,10 +544,35 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc,
Diag(ModuleScopes.back().BeginLoc, diag::note_private_module_fragment);
}
for (const DeclContext *DC = CurContext; DC; DC = DC->getLexicalParent()) {
if (const auto *ND = dyn_cast<NamespaceDecl>(DC)) {
// An export-declaration shall not appear directly or indirectly within
// an unnamed namespace [...]
if (ND->isAnonymousNamespace()) {
Diag(ExportLoc, diag::err_export_within_anonymous_namespace);
Diag(ND->getLocation(), diag::note_anonymous_namespace);
// Don't diagnose internal-linkage declarations in this region.
D->setInvalidDecl();
break;
}
// A declaration is exported if it is [...] a namespace-definition
// that contains an exported declaration.
//
// Defer exporting the namespace until after we leave it, in order to
// avoid marking all subsequent declarations in the namespace as exported.
if (!DeferredExportedNamespaces.insert(ND).second)
break;
}
}
// [...] its declaration or declaration-seq shall not contain an
// export-declaration.
if (D->isExported())
if (auto *ED = getEnclosingExportDecl(D)) {
Diag(ExportLoc, diag::err_export_within_export);
if (ED->hasBraces())
Diag(ED->getLocation(), diag::note_export);
}
CurContext->addDecl(D);
PushDeclContext(S, D);
@ -545,15 +580,131 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc,
return D;
}
static bool checkExportedDeclContext(Sema &S, DeclContext *DC,
SourceLocation BlockStart);
namespace {
enum class UnnamedDeclKind {
Empty,
StaticAssert,
Asm,
UsingDirective,
Context
};
}
static llvm::Optional<UnnamedDeclKind> getUnnamedDeclKind(Decl *D) {
if (isa<EmptyDecl>(D))
return UnnamedDeclKind::Empty;
if (isa<StaticAssertDecl>(D))
return UnnamedDeclKind::StaticAssert;
if (isa<FileScopeAsmDecl>(D))
return UnnamedDeclKind::Asm;
if (isa<UsingDirectiveDecl>(D))
return UnnamedDeclKind::UsingDirective;
// Everything else either introduces one or more names or is ill-formed.
return llvm::None;
}
unsigned getUnnamedDeclDiag(UnnamedDeclKind UDK, bool InBlock) {
switch (UDK) {
case UnnamedDeclKind::Empty:
case UnnamedDeclKind::StaticAssert:
// Allow empty-declarations and static_asserts in an export block as an
// extension.
return InBlock ? diag::ext_export_no_name_block : diag::err_export_no_name;
case UnnamedDeclKind::UsingDirective:
// Allow exporting using-directives as an extension.
return diag::ext_export_using_directive;
case UnnamedDeclKind::Context:
// Allow exporting DeclContexts that transitively contain no declarations
// as an extension.
return diag::ext_export_no_names;
case UnnamedDeclKind::Asm:
return diag::err_export_no_name;
}
llvm_unreachable("unknown kind");
}
static void diagExportedUnnamedDecl(Sema &S, UnnamedDeclKind UDK, Decl *D,
SourceLocation BlockStart) {
S.Diag(D->getLocation(), getUnnamedDeclDiag(UDK, BlockStart.isValid()))
<< (unsigned)UDK;
if (BlockStart.isValid())
S.Diag(BlockStart, diag::note_export);
}
/// Check that it's valid to export \p D.
static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) {
// C++2a [module.interface]p3:
// An exported declaration shall declare at least one name
if (auto UDK = getUnnamedDeclKind(D))
diagExportedUnnamedDecl(S, *UDK, D, BlockStart);
// [...] shall not declare a name with internal linkage.
if (auto *ND = dyn_cast<NamedDecl>(D)) {
// Don't diagnose anonymous union objects; we'll diagnose their members
// instead.
if (ND->getDeclName() && ND->getFormalLinkage() == InternalLinkage) {
S.Diag(ND->getLocation(), diag::err_export_internal) << ND;
if (BlockStart.isValid())
S.Diag(BlockStart, diag::note_export);
}
}
// C++2a [module.interface]p5:
// all entities to which all of the using-declarators ultimately refer
// shall have been introduced with a name having external linkage
if (auto *USD = dyn_cast<UsingShadowDecl>(D)) {
NamedDecl *Target = USD->getUnderlyingDecl();
if (Target->getFormalLinkage() == InternalLinkage) {
S.Diag(USD->getLocation(), diag::err_export_using_internal) << Target;
S.Diag(Target->getLocation(), diag::note_using_decl_target);
if (BlockStart.isValid())
S.Diag(BlockStart, diag::note_export);
}
}
// Recurse into namespace-scope DeclContexts. (Only namespace-scope
// declarations are exported.)
if (auto *DC = dyn_cast<DeclContext>(D))
if (DC->getRedeclContext()->isFileContext() && !isa<EnumDecl>(D))
return checkExportedDeclContext(S, DC, BlockStart);
return false;
}
/// Check that it's valid to export all the declarations in \p DC.
static bool checkExportedDeclContext(Sema &S, DeclContext *DC,
SourceLocation BlockStart) {
bool AllUnnamed = true;
for (auto *D : DC->decls())
AllUnnamed &= checkExportedDecl(S, D, BlockStart);
return AllUnnamed;
}
/// Complete the definition of an export declaration.
Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) {
auto *ED = cast<ExportDecl>(D);
if (RBraceLoc.isValid())
ED->setRBraceLoc(RBraceLoc);
// FIXME: Diagnose export of internal-linkage declaration (including
// anonymous namespace).
PopDeclContext();
if (!D->isInvalidDecl()) {
SourceLocation BlockStart =
ED->hasBraces() ? ED->getBeginLoc() : SourceLocation();
for (auto *Child : ED->decls()) {
if (checkExportedDecl(*this, Child, BlockStart)) {
// If a top-level child is a linkage-spec declaration, it might contain
// no declarations (transitively), in which case it's ill-formed.
diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child,
BlockStart);
}
}
}
return D;
}

View File

@ -0,0 +1,3 @@
extern int foo;
namespace bar { extern int baz(); }
static int baz;

View File

@ -23,15 +23,15 @@ namespace N {
}
#ifdef ERRORS
namespace {
export int d1; // FIXME: invalid
namespace { // expected-note 2{{anonymous namespace begins here}}
export int d1; // expected-error {{export declaration appears within anonymous namespace}}
namespace X {
export int d2; // FIXME: invalid
export int d2; // expected-error {{export declaration appears within anonymous namespace}}
}
}
export export int e; // expected-error {{within another export declaration}}
export { export int f; } // expected-error {{within another export declaration}}
export { export int f; } // expected-error {{within another export declaration}} expected-note {{export block begins here}}
module :private; // expected-note {{private module fragment begins here}}
export int priv; // expected-error {{export declaration cannot be used in a private module fragment}}

View File

@ -0,0 +1,94 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: %clang_cc1 -std=c++2a -x c++-header %S/Inputs/header.h -emit-header-module -fmodule-name=FIXME -o %t/h.pcm
// RUN: %clang_cc1 -std=c++2a %s -DX_INTERFACE -emit-module-interface -o %t/x.pcm
// RUN: %clang_cc1 -std=c++2a %s -DY_INTERFACE -emit-module-interface -o %t/y.pcm
// RUN: %clang_cc1 -std=c++2a %s -DINTERFACE -fmodule-file=%t/x.pcm -fmodule-file=%t/y.pcm -emit-module-interface -o %t/m.pcm
// RUN: %clang_cc1 -std=c++2a %s -DIMPLEMENTATION -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=%t/m.pcm -verify
// RUN: %clang_cc1 -std=c++2a %s -DUSER -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=%t/m.pcm -verify
#if defined(X_INTERFACE)
export module X;
export int x;
#elif defined(Y_INTERFACE)
export module Y;
export int y;
#elif defined(INTERFACE)
export module p2;
export import X;
import Y; // not exported
namespace A {
int f();
export int g();
int h();
namespace inner {}
}
export namespace B {
namespace inner {}
}
namespace B {
int f();
}
namespace C {}
namespace D { int f(); }
export namespace D {}
#elif defined(IMPLEMENTATION)
module p2;
import "header.h";
// Per [basic.scope.namespace]/2.3, exportedness has no impact on visibility
// within the same module.
//
// expected-no-diagnostics
void use() {
A::f();
A::g();
A::h();
using namespace A::inner;
using namespace B;
using namespace B::inner;
B::f();
f();
using namespace C;
D::f();
}
int use_header() { return foo + bar::baz(); }
#elif defined(USER)
import p2;
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::g();
A::h(); // expected-error {{no member named 'h' in namespace 'A'}}
using namespace A::inner; // expected-error {{expected namespace name}}
// 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'}}
// namespace C is not exported
using namespace C; // expected-error {{expected namespace name}}
// namespace D is exported, but D::f is not
D::f(); // expected-error {{no member named 'f' in namespace 'D'}}
}
int use_header() { return foo + bar::baz(); }
#else
#error unknown mode
#endif

View File

@ -0,0 +1,54 @@
// RUN: %clang_cc1 -std=c++2a %s -verify -pedantic-errors
export module p3;
namespace A { int ns_mem; }
// An exported declaration shall declare at least one name.
export; // expected-error {{empty declaration cannot be exported}}
export static_assert(true); // expected-error {{static_assert declaration cannot be exported}}
export using namespace A; // expected-error {{ISO C++20 does not permit using directive to be exported}}
export { // expected-note 3{{export block begins here}}
; // expected-error {{ISO C++20 does not permit an empty declaration to appear in an export block}}
static_assert(true); // expected-error {{ISO C++20 does not permit a static_assert declaration to appear in an export block}}
using namespace A; // expected-error {{ISO C++20 does not permit using directive to be exported}}
}
export struct {}; // expected-error {{must be class member}} expected-error {{GNU extension}}
export struct {} struct_;
export union {}; // expected-error {{must be declared 'static'}}
export union {} union_;
export enum {}; // expected-error {{does not declare anything}}
export enum {} enum_;
export enum E : int;
export typedef int; // expected-error {{typedef requires a name}}
export static union {}; // FIXME: this declaration is ill-formed even without the 'export'
export asm(""); // expected-error {{asm declaration cannot be exported}}
export namespace B = A;
export using A::ns_mem;
namespace A {
export using A::ns_mem;
}
export using Int = int;
export extern "C++" {} // expected-error {{ISO C++20 does not permit a declaration that does not introduce any names to be exported}}
export extern "C++" { extern "C" {} } // expected-error {{ISO C++20 does not permit a declaration that does not introduce any names to be exported}}
export extern "C++" { extern "C" int extern_c; }
export { // expected-note {{export block}}
extern "C++" int extern_cxx;
extern "C++" {} // expected-error {{ISO C++20 does not permit a declaration that does not introduce any names to be exported}}
}
export [[]]; // FIXME (bad diagnostic text): expected-error {{empty declaration cannot be exported}}
export [[example::attr]]; // FIXME: expected-error {{empty declaration cannot be exported}} expected-warning {{unknown attribute 'attr'}}
// [...] shall not declare a name with internal linkage
export static int a; // expected-error {{declaration of 'a' with internal linkage cannot be exported}}
export static int b(); // expected-error {{declaration of 'b' with internal linkage cannot be exported}}
export namespace { int c; } // expected-error {{declaration of 'c' with internal linkage cannot be exported}}
namespace { // expected-note {{here}}
export int d; // expected-error {{export declaration appears within anonymous namespace}}
}
export template<typename> static int e; // FIXME
export template<typename> static int f(); // expected-error {{declaration of 'f' with internal linkage cannot be exported}}
export const int k = 5;
export static union { int n; }; // expected-error {{declaration of 'n' with internal linkage cannot be exported}}

View File

@ -0,0 +1,89 @@
// RUN: %clang_cc1 -std=c++2a %s -verify -pedantic-errors
export module p5;
int a;
static int sa; // expected-note {{target}}
void b();
static void sb(); // expected-note {{target}}
struct c {};
enum d {};
using e = int;
using f = c;
static union { int sg1, sg2; }; // expected-note {{target}}
namespace NS {}
template<typename> int ta;
template<typename> static int sta;
template<typename> void tb();
template<typename> static void stb(); // expected-note {{target}}
template<typename> struct tc {};
template<typename> using te = int;
template<typename> using tf = c;
namespace UnnamedNS {
namespace {
int a; // expected-note {{target}}
static int sa; // expected-note {{target}}
void b(); // expected-note {{target}}
static void sb(); // expected-note {{target}}
struct c {}; // expected-note {{target}}
enum d {}; // expected-note {{target}}
using e = int;
using f = c;
static union { int sg1, sg2; }; // expected-note {{target}}
namespace NS {}
template<typename> int ta; // expected-note {{target}}
template<typename> static int sta; // expected-note {{target}}
template<typename> void tb(); // expected-note {{target}}
template<typename> static void stb(); // expected-note {{target}}
template<typename> struct tc {}; // expected-note {{target}}
template<typename> using te = int; // expected-note {{target}}
template<typename> using tf = c; // expected-note {{target}}
}
}
export { // expected-note 18{{here}}
using ::a;
using ::sa; // expected-error {{using declaration referring to 'sa' with internal linkage}}
using ::b;
using ::sb; // expected-error {{using declaration referring to 'sb' with internal linkage}}
using ::c;
using ::d;
using ::e;
using ::f;
using ::sg1; // expected-error {{using declaration referring to 'sg1' with internal linkage}}
using ::ta;
using ::sta; // FIXME {{using declaration referring to 'sta' with internal linkage}}
using ::tb;
using ::stb; // expected-error {{using declaration referring to 'stb' with internal linkage}}
using ::tc;
using ::te;
using ::tf;
namespace NS2 = ::NS;
namespace UnnamedNS {
using UnnamedNS::a; // expected-error {{internal linkage}}
using UnnamedNS::sa; // expected-error {{internal linkage}}
using UnnamedNS::b; // expected-error {{internal linkage}}
using UnnamedNS::sb; // expected-error {{internal linkage}}
using UnnamedNS::c; // expected-error {{internal linkage}}
using UnnamedNS::d; // expected-error {{internal linkage}}
using UnnamedNS::e; // ok
using UnnamedNS::f; // ok? using-declaration refers to alias-declaration,
// which does not have linkage (even though that then
// refers to a type that has internal linkage)
using UnnamedNS::sg1; // expected-error {{internal linkage}}
using UnnamedNS::ta; // expected-error {{internal linkage}}
using UnnamedNS::sta; // expected-error {{internal linkage}}
using UnnamedNS::tb; // expected-error {{internal linkage}}
using UnnamedNS::stb; // expected-error {{internal linkage}}
using UnnamedNS::tc; // expected-error {{internal linkage}}
using UnnamedNS::te; // expected-error {{internal linkage}}
using UnnamedNS::tf; // expected-error {{internal linkage}}
namespace NS2 = UnnamedNS::NS; // ok (wording bug?)
}
}

View File

@ -3,7 +3,6 @@
// CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
// CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
// CHECK-DAG: @_ZW6ModuleE19static_var_exported = available_externally {{(dso_local )?}}global i32 0,
// CHECK-DAG: @const_var_exported = available_externally {{(dso_local )?}}constant i32 3,
//
// CHECK-DAG: @_ZW6ModuleE25extern_var_module_linkage = external {{(dso_local )?}}global
@ -21,7 +20,6 @@ void use() {
(void)&extern_var_exported;
(void)&inline_var_exported;
(void)&static_var_exported; // FIXME: Should not be exported.
(void)&const_var_exported;
// FIXME: This symbol should not be visible here.

View File

@ -11,7 +11,6 @@
// 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: @_ZW6ModuleE19static_var_exported = {{(dso_local )?}}global
// CHECK-DAG: @const_var_exported = {{(dso_local )?}}constant
//
// CHECK-DAG: @_ZW6ModuleE25extern_var_module_linkage = external {{(dso_local )?}}global
@ -58,32 +57,17 @@ void noninline_global_module() {
export module Module;
export {
// FIXME: These should be ill-formed: you can't export an internal linkage
// symbol, per [dcl.module.interface]p2.
// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6ModuleE22unused_static_exportedv
static void unused_static_exported() {}
// CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6ModuleE20used_static_exportedv
static void used_static_exported() {}
inline void unused_inline_exported() {}
inline void used_inline_exported() {}
extern int extern_var_exported;
inline int inline_var_exported;
// FIXME: This should be ill-formed: you can't export an internal linkage
// symbol.
static int static_var_exported;
const int const_var_exported = 3;
// CHECK: define {{(dso_local )?}}void {{.*}}@_Z18noninline_exportedv
void noninline_exported() {
used_static_exported();
// CHECK: define linkonce_odr {{.*}}@_Z20used_inline_exportedv
used_inline_exported();
(void)&extern_var_exported;
(void)&inline_var_exported;
(void)&static_var_exported;
(void)&const_var_exported;
}
}

View File

@ -3,7 +3,6 @@
// CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
// CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
// CHECK-DAG: @_ZW6ModuleE19static_var_exported = available_externally {{(dso_local )?}}global i32 0
// CHECK-DAG: @const_var_exported = available_externally {{(dso_local )?}}constant i32 3
import Module;
@ -16,7 +15,6 @@ void use() {
(void)&extern_var_exported;
(void)&inline_var_exported;
(void)&static_var_exported;
(void)&const_var_exported;
// Module-linkage declarations are not visible here.

View File

@ -1,6 +1,7 @@
// RUN: %clang_cc1 -std=c++17 -fmodules-ts -emit-obj -verify -o %t.pcm %s
export module M;
export {
union { bool a; }; // expected-error{{anonymous unions at namespace or global scope must be declared 'static'}}
export { // expected-note 2{{export block begins here}}
union { bool a; }; // expected-error {{anonymous unions at namespace or global scope must be declared 'static'}} expected-error {{declaration of 'a' with internal linkage cannot be exported}}
static union { bool a; }; // expected-error {{declaration of 'a' with internal linkage cannot be exported}}
}

View File

@ -49,8 +49,12 @@ int use_a = a; // expected-error {{declaration of 'a' must be imported from modu
import foo;
export {} // expected-error {{export declaration cannot be empty}}
export { ; }
export { static_assert(true); }
export { // expected-note {{begins here}}
; // expected-warning {{ISO C++20 does not permit an empty declaration to appear in an export block}}
}
export { // expected-note {{begins here}}
static_assert(true); // expected-warning {{ISO C++20 does not permit a static_assert declaration to appear in an export block}}
}
int use_b = b;
int use_n = n; // FIXME: this should not be visible, because it is not exported
@ -74,7 +78,7 @@ struct S {
// language rules right now, but (per personal correspondence between zygoloid
// and gdr) is the intent.
#if TEST == 1
export {
export { // expected-note {{export block begins here}}
extern "C++" {
namespace NestedExport {
export { // expected-error {{appears within another export}}