forked from OSchip/llvm-project
[c++2a] Implement semantic restrictions for 'export' declarations.
llvm-svn: 358932
This commit is contained in:
parent
68602ab2f3
commit
e181de7f4f
|
@ -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).
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]>;
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
extern int foo;
|
||||
namespace bar { extern int baz(); }
|
||||
static int baz;
|
|
@ -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}}
|
||||
|
|
|
@ -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
|
|
@ -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}}
|
|
@ -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?)
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
|
|
Loading…
Reference in New Issue