https://github.com/perry-ca created https://github.com/llvm/llvm-project/pull/140944
Implement the _Export keyword that is used on z/OS to indicate that a symbol with external linkage is to be exported from the shared library. In the XL C/C++ compiler this keyword is used only in C++ source code. That is being extended to include C source as well in this PR. This code was originally in PR https://github.com/llvm/llvm-project/pull/111035. I have split it out into a separate PR so the code for `#pragma export` is in one PR and the code for `_Export` keyword is in another. See that original PR for earlier comments. For reference, the XL documentation for _Export: https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-export-function-specifier-c-only >From 7e3ff9fbe877ab54fd16bd41e26f32798f0ab430 Mon Sep 17 00:00:00 2001 From: Sean Perry <pe...@ca.ibm.com> Date: Wed, 21 May 2025 18:02:26 +0000 Subject: [PATCH] Implement _Export keyword --- clang/docs/ReleaseNotes.rst | 5 ++ clang/include/clang/Basic/Attr.td | 6 ++ clang/include/clang/Basic/AttrDocs.td | 33 +++++++++++ .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Basic/TokenKinds.def | 3 + clang/include/clang/Sema/DeclSpec.h | 38 ++++++++++-- clang/include/clang/Sema/Sema.h | 2 + clang/lib/Driver/ToolChains/ZOS.cpp | 4 ++ clang/lib/Parse/ParseDecl.cpp | 15 +++++ clang/lib/Parse/ParseDeclCXX.cpp | 6 ++ clang/lib/Sema/DeclSpec.cpp | 6 ++ clang/lib/Sema/SemaDecl.cpp | 21 +++++++ clang/lib/Sema/SemaDeclAttr.cpp | 9 +++ clang/test/CodeGen/attr-export.c | 23 ++++++++ clang/test/CodeGen/attr-export.cpp | 59 +++++++++++++++++++ clang/test/Sema/attr-export-failing.cpp | 4 ++ clang/test/Sema/zos-export.c | 25 ++++++++ clang/test/Sema/zos-export.cpp | 44 ++++++++++++++ 18 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 clang/test/CodeGen/attr-export.c create mode 100644 clang/test/CodeGen/attr-export.cpp create mode 100644 clang/test/Sema/attr-export-failing.cpp create mode 100644 clang/test/Sema/zos-export.c create mode 100644 clang/test/Sema/zos-export.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ad8409397ff8a..8abfd60caedbc 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -879,6 +879,11 @@ WebAssembly Support AVR Support ^^^^^^^^^^^ +SystemZ Support +^^^^^^^^^^^^^^^ + +- Add support for `_Export` keyword for z/OS + DWARF Support in Clang ---------------------- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a6a7482a94a29..7bbaaa53e275b 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4267,6 +4267,12 @@ def Thread : Attr { let Subjects = SubjectList<[Var]>; } +def zOSExport : InheritableAttr { + let Spellings = [CustomKeyword<"_Export">]; + let Subjects = SubjectList<[Function, Var, CXXRecord]>; + let Documentation = [zOSExportDocs]; +} + def Win64 : IgnoredAttr { let Spellings = [CustomKeyword<"__w64">]; let LangOpts = [MicrosoftExt]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 65d66dd398ad1..3beb38552df05 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -242,6 +242,39 @@ members, and static locals. }]; } +def zOSExportDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Use the ``_Export`` keyword on a function or external variable to declare +that it is to be exported (made available to other modules). A symbol needs to be +declared exported on or before the definition of the symbol. The ``_Export`` +keyword must immediately precede the declaration name in the declarator. +For example: + +.. code-block:: c + + int _Export func(float); + int (*_Export funcPtr)(float); + +The first statement exports the function ``func``, if ``func`` is defined in the +translation unit after this declaration. + +All of the static data members and member functions of a class or struct and its vague +linkage objects (vtable, typeinfo, typeinfo name) can be exported +by including ``_Export`` in tag of the class during the class definition or forward +declaration of the class. + +.. code-block:: c++ + + class _Export C { + int func(); + }; + +Select members of a class can be exported by using the ``_Export`` keyword on +declaration within the class or definition of the member. + }]; +} + def NoEscapeDocs : Documentation { let Category = DocCatVariable; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 66f9480d99380..8d84d0bb698b3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9655,6 +9655,8 @@ def warn_redefine_extname_not_applied : Warning< "#pragma redefine_extname is applicable to external C declarations only; " "not applied to %select{function|variable}0 %1">, InGroup<Pragmas>; +def err_cannot_be_exported : Error< + "needs to have external linkage to be '_Export` qualified">; } // End of general sema category. // inline asm. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 94e72fea56a68..d0cb1aa2d7844 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -353,6 +353,9 @@ KEYWORD(__ptrauth , KEYALL) // C2y UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX) +// z/OS specific keywords +KEYWORD(_Export , KEYZOS) + // C++ 2.11p1: Keywords. KEYWORD(asm , KEYCXX|KEYGNU) KEYWORD(bool , BOOLSUPPORT|KEYC23) diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 6c4a32c4ac2f0..58a69d274a02e 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -397,6 +397,8 @@ class DeclSpec { unsigned FS_virtual_specified : 1; LLVM_PREFERRED_TYPE(bool) unsigned FS_noreturn_specified : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned ExportSpecified : 1; // z/OS extension // friend-specifier LLVM_PREFERRED_TYPE(bool) @@ -443,6 +445,7 @@ class DeclSpec { SourceLocation FS_forceinlineLoc; SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; SourceLocation TQ_pipeLoc; + SourceLocation ExportLoc; WrittenBuiltinSpecs writtenBS; void SaveWrittenBuiltinSpecs(); @@ -491,9 +494,9 @@ class DeclSpec { TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false), TypeQualifiers(TQ_unspecified), FS_inline_specified(false), FS_forceinline_specified(false), FS_virtual_specified(false), - FS_noreturn_specified(false), FriendSpecifiedFirst(false), - ConstexprSpecifier( - static_cast<unsigned>(ConstexprSpecKind::Unspecified)), + FS_noreturn_specified(false), ExportSpecified(false), + FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>( + ConstexprSpecKind::Unspecified)), Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} // storage-class-specifier @@ -660,6 +663,9 @@ class DeclSpec { bool isNoreturnSpecified() const { return FS_noreturn_specified; } SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; } + bool isExportSpecified() const { return ExportSpecified; } + SourceLocation getExportSpecLoc() const { return ExportLoc; } + void ClearFunctionSpecs() { FS_inline_specified = false; FS_inlineLoc = SourceLocation(); @@ -810,6 +816,8 @@ class DeclSpec { bool setFunctionSpecNoreturn(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); + bool setExportSpec(SourceLocation Loc); + bool SetFriendSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); bool setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec, @@ -1955,6 +1963,10 @@ class Declarator { LLVM_PREFERRED_TYPE(bool) unsigned InlineStorageUsed : 1; + /// Indicates whether this is set as _Export. + LLVM_PREFERRED_TYPE(bool) + unsigned ExportSpecified : 1; // z/OS extension + /// Indicates whether this declarator has an initializer. LLVM_PREFERRED_TYPE(bool) unsigned HasInitializer : 1; @@ -2001,6 +2013,9 @@ class Declarator { /// this declarator as a parameter pack. SourceLocation EllipsisLoc; + /// The source location of the _Export keyword on this declarator. + SourceLocation ExportLoc; + Expr *PackIndexingExpr; friend struct DeclaratorChunk; @@ -2030,7 +2045,8 @@ class Declarator { FunctionDefinitionKind::Declaration)), Redeclaration(false), Extension(false), ObjCIvar(false), ObjCWeakProperty(false), InlineStorageUsed(false), - HasInitializer(false), Attrs(DS.getAttributePool().getFactory()), + ExportSpecified(false), HasInitializer(false), + Attrs(DS.getAttributePool().getFactory()), DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr), TrailingRequiresClause(nullptr), InventedTemplateParameterList(nullptr) { @@ -2109,6 +2125,18 @@ class Declarator { Range.setEnd(SR.getEnd()); } + /// Set this declarator as _Export. + void SetExport(SourceLocation Loc) { + ExportSpecified = true; + ExportLoc = Loc; + } + + /// Whether this declarator is marked as _Export. + bool IsExport() const { return ExportSpecified; } + + /// Get the location of the _Export keyword. + SourceLocation getExportLoc() const { return ExportLoc; } + /// Reset the contents of this Declarator. void clear() { SS.clear(); @@ -2125,8 +2153,10 @@ class Declarator { HasInitializer = false; ObjCIvar = false; ObjCWeakProperty = false; + ExportSpecified = false; CommaLoc = SourceLocation(); EllipsisLoc = SourceLocation(); + ExportLoc = SourceLocation(); PackIndexingExpr = nullptr; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a994b845e11fc..a973f116440ff 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4871,6 +4871,8 @@ class Sema final : public SemaBase { TypeVisibilityAttr::VisibilityType Vis); VisibilityAttr *mergeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI, VisibilityAttr::VisibilityType Vis); + void mergeVisibilityType(Decl *D, SourceLocation Loc, + VisibilityAttr::VisibilityType Type); SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI, StringRef Name); diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp index c5ad3ef1b00f1..371623b83abd3 100644 --- a/clang/lib/Driver/ToolChains/ZOS.cpp +++ b/clang/lib/Driver/ToolChains/ZOS.cpp @@ -37,6 +37,10 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs, options::OPT_fno_aligned_allocation)) CC1Args.push_back("-faligned-alloc-unavailable"); + if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ, + options::OPT_fvisibility_ms_compat)) + CC1Args.push_back("-fvisibility=hidden"); + if (DriverArgs.hasFlag(options::OPT_fxl_pragma_pack, options::OPT_fno_xl_pragma_pack, true)) CC1Args.push_back("-fxl-pragma-pack"); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 7a87cd2e340cc..3928062079cfc 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4217,6 +4217,11 @@ void Parser::ParseDeclarationSpecifiers( isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID); break; + case tok::kw__Export: + // _Export keyword is part of the declarator id + goto DoneWithDeclSpec; + break; + // friend case tok::kw_friend: if (DSContext == DeclSpecContext::DSC_class) { @@ -6418,6 +6423,16 @@ void Parser::ParseDeclaratorInternal(Declarator &D, tok::TokenKind Kind = Tok.getKind(); + if (Kind == tok::kw__Export) { + SourceLocation loc = ConsumeToken(); + D.SetExport(loc); + D.SetRangeEnd(loc); + + if (DirectDeclParser) + (this->*DirectDeclParser)(D); + return; + } + if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) { DeclSpec DS(AttrFactory); ParseTypeQualifierListOpt(DS); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 316bc30edf1f0..8d991ddc85e93 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1588,6 +1588,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // If attributes exist after tag, parse them. for (;;) { MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs); + if (Tok.is(tok::kw__Export)) { + SourceLocation loc = ConsumeToken(); + DS.setExportSpec(loc); + continue; + } + // Parse inheritance specifiers. if (Tok.isOneOf(tok::kw___single_inheritance, tok::kw___multiple_inheritance, diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index ee5a862c32509..e1cafc3d0f977 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1104,6 +1104,12 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc, return false; } +bool DeclSpec::setExportSpec(SourceLocation Loc) { + ExportSpecified = true; + ExportLoc = Loc; + return false; +} + bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { if (isFriendSpecified()) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index cb81ac889e480..2cbb7507795a0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5175,6 +5175,9 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, assert(EllipsisLoc.isInvalid() && "Friend ellipsis but not friend-specified?"); + if (DS.isExportSpecified()) + mergeVisibilityType(Tag, DS.getExportSpecLoc(), VisibilityAttr::Default); + // Track whether this decl-specifier declares anything. bool DeclaresAnything = true; @@ -6515,6 +6518,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, if (!New) return nullptr; + if (D.IsExport()) + mergeVisibilityType(New, D.getExportLoc(), VisibilityAttr::Default); + warnOnCTypeHiddenInCPlusPlus(New); // If this has an identifier and is not a function template specialization, @@ -6754,6 +6760,9 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, return nullptr; } + if (D.IsExport()) + Diag(D.getName().StartLocation, diag::err_cannot_be_exported); + TypedefDecl *NewTD = ParseTypedefDecl(S, D, TInfo->getType(), TInfo); if (!NewTD) return nullptr; @@ -8213,6 +8222,9 @@ NamedDecl *Sema::ActOnVariableDeclarator( ProcessPragmaWeak(S, NewVD); + if (D.IsExport() && !NewVD->hasExternalFormalLinkage()) + Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported); + // If this is the first declaration of an extern C variable, update // the map of such variables. if (NewVD->isFirstDecl() && !NewVD->isInvalidDecl() && @@ -10864,6 +10876,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, ProcessPragmaWeak(S, NewFD); checkAttributesAfterMerging(*this, *NewFD); + if (D.IsExport() && !NewFD->hasExternalFormalLinkage()) + Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported); + AddKnownFunctionAttributes(NewFD); if (NewFD->hasAttr<OverloadableAttr>() && @@ -15423,6 +15438,9 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D, if (getLangOpts().OpenCL) deduceOpenCLAddressSpace(New); + if (D.IsExport()) + Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported); + return New; } @@ -19005,6 +19023,9 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, PPC().CheckPPCMMAType(T, NewFD->getLocation())) NewFD->setInvalidDecl(); + if (D && D->IsExport()) + Diag(D->getIdentifierLoc(), diag::err_cannot_be_exported); + NewFD->setAccess(AS); return NewFD; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 4d7f0455444f1..c1bbd2127ea03 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2632,6 +2632,15 @@ static void handleExternalSourceSymbolAttr(Sema &S, Decl *D, S.Context, AL, Language, DefinedIn, IsGeneratedDeclaration, USR)); } +void Sema::mergeVisibilityType(Decl *D, SourceLocation Loc, + VisibilityAttr::VisibilityType Value) { + if (VisibilityAttr *Attr = D->getAttr<VisibilityAttr>()) { + if (Attr->getVisibility() != Value) + Diag(Loc, diag::err_mismatched_visibility); + } else + D->addAttr(VisibilityAttr::CreateImplicit(Context, Value)); +} + template <class T> static T *mergeVisibilityAttr(Sema &S, Decl *D, const AttributeCommonInfo &CI, typename T::VisibilityType value) { diff --git a/clang/test/CodeGen/attr-export.c b/clang/test/CodeGen/attr-export.c new file mode 100644 index 0000000000000..aae0562c1890c --- /dev/null +++ b/clang/test/CodeGen/attr-export.c @@ -0,0 +1,23 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang --target=s390x-none-zos -S -emit-llvm %s -o - | FileCheck %s + +// Check the variables +// CHECK: @func_ptr = global ptr null, align 8 +// CHECK: @var1 = global i32 0, align 4 +// CHECK: @var2 = hidden global i32 0, align 4 +// CHECK: @var3 = global i32 0, align 4 +// CHECK: @var4 = hidden global i32 0, align 4 +// CHECK: @var5 = global i32 0, align 4 + +// Check the functions +// CHECK: define void @foo1 +// CHECK: define hidden void @foo2 + +int _Export var1; +int var2; +int _Export var3, var4, _Export var5; + +void _Export foo1(){}; +void foo2(){}; + +int (*_Export func_ptr)(void) = 0; diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp new file mode 100644 index 0000000000000..b5da80356ffda --- /dev/null +++ b/clang/test/CodeGen/attr-export.cpp @@ -0,0 +1,59 @@ +// REQUIRES: systemz-registered-target +// RUN: %clangxx --target=s390x-none-zos -S -emit-llvm %s -o - | FileCheck %s + +// Check the variables +// CHECK: @var1 = global i32 0, align 4 +// CHECK: @var2 = hidden global i32 0, align 4 +// CHECK: @var3 = global i32 0, align 4 +// CHECK: @var4 = hidden global i32 0, align 4 +// CHECK: @var5 = global i32 0, align 4 +// CHECK: @obj1 = global %class.class1 zeroinitializer, align 2 +// CHECK: @obj2 = hidden global %class.class1 zeroinitializer, align 2 +// CHECK: @func_ptr = global ptr null, align 8 +// CHECK: @p2m = global i64 -1, align 8 + +// Check the functions +// CHECK: define void @_Z4foo1v +// CHECK: define hidden void @_Z4foo2v +// CHECK: define void @_ZN6class13fooEv +// CHECK: define hidden void @_ZN6class23fooEv +// CHECK: define hidden void @_ZN6class33fooEv +// CHECK: define void @_ZN6class33barEv + +int _Export var1; +int var2; +int _Export var3, var4, _Export var5; + +void _Export foo1(){}; +void foo2(){}; + +class _Export class1 { +public: + void foo(); +}; + +class class2 { +public: + void foo(); +}; + +void class1::foo(){}; + +void class2::foo(){}; + +class1 _Export obj1; +class1 obj2; + +class class3 { +public: + int mbr; + void foo(); + void _Export bar(); +}; + +void class3::foo() {}; +void class3::bar() {}; + +int (*_Export func_ptr)(void) = 0; + +int class3::* _Export p2m = 0; diff --git a/clang/test/Sema/attr-export-failing.cpp b/clang/test/Sema/attr-export-failing.cpp new file mode 100644 index 0000000000000..14c22c96f2f1f --- /dev/null +++ b/clang/test/Sema/attr-export-failing.cpp @@ -0,0 +1,4 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only -verify +__attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}} +class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}} diff --git a/clang/test/Sema/zos-export.c b/clang/test/Sema/zos-export.c new file mode 100644 index 0000000000000..64aeefd063411 --- /dev/null +++ b/clang/test/Sema/zos-export.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos %s -fsyntax-only -verify + +typedef int _Export ty; //expected-error {{needs to have external linkage to be '_Export` qualified}} +ty x; +int f(int _Export argument); //expected-error {{needs to have external linkage to be '_Export` qualified}} +static int _Export file_scope_static; //expected-error {{needs to have external linkage to be '_Export` qualified}} +struct S { + int _Export nonstaticdatamember; //expected-error {{needs to have external linkage to be '_Export` qualified}} +}; +void g() { + int _Export automatic; //expected-error {{needs to have external linkage to be '_Export` qualified}} +} + +static void _Export static_func() { //expected-error {{needs to have external linkage to be '_Export` qualified}} +} + +void _Export h() { + static_func(); +} + +void j() { + static int _Export sl = 0; //expected-error {{needs to have external linkage to be '_Export` qualified}} +} + +int _Export file_scope; diff --git a/clang/test/Sema/zos-export.cpp b/clang/test/Sema/zos-export.cpp new file mode 100644 index 0000000000000..42be5327e28a5 --- /dev/null +++ b/clang/test/Sema/zos-export.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos %s -fsyntax-only -verify + +typedef int _Export ty; //expected-error {{needs to have external linkage to be '_Export` qualified}} +ty typedef_var; +int f(int _Export argument); //expected-error {{needs to have external linkage to be '_Export` qualified}} +static int _Export file_scope_static; //expected-error {{needs to have external linkage to be '_Export` qualified}} +struct S { + int _Export nonstaticdatamember; //expected-error {{needs to have external linkage to be '_Export` qualified}} +}; +void g() { + int _Export automatic; //expected-error {{needs to have external linkage to be '_Export` qualified}} +} + +static void _Export static_func() { //expected-error {{needs to have external linkage to be '_Export` qualified}} +} + +void _Export h() { + static_func(); +} + +void j() { + static int _Export sl = 0; //expected-error {{needs to have external linkage to be '_Export` qualified}} +} + +int _Export file_scope; + +struct _Export SE { +}; + +struct ST { + void _Export f(); + virtual void _Export v_(); + static int _Export i; +}; + +namespace { + int _Export anon_var; //expected-error {{needs to have external linkage to be '_Export` qualified}} + extern "C" int _Export anon_C_var; + void _Export anon_f() {} //expected-error {{needs to have external linkage to be '_Export` qualified}} + extern "C" void _Export anon_C_f() {} + struct anon_S { + static int _Export anon_static_data_member; //expected-error {{needs to have external linkage to be '_Export` qualified}} + }; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits