[clang] [SystemZ][z/OS] Implement _Export keyword (PR #140944)
Sean Perry via cfe-commits
cfe-commits at lists.llvm.org
Wed May 21 13:10:19 PDT 2025
https://github.com/perry-ca updated https://github.com/llvm/llvm-project/pull/140944
>From 7e3ff9fbe877ab54fd16bd41e26f32798f0ab430 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Wed, 21 May 2025 18:02:26 +0000
Subject: [PATCH 1/2] 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}}
+ };
+}
>From 83482ce7ca6f4680247a2d60b0dd32bc743fc9b5 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Wed, 21 May 2025 20:11:51 +0000
Subject: [PATCH 2/2] Add some parsing tests
---
clang/test/Parser/attr-export.c | 15 +++++++++++++++
clang/test/Parser/attr-export.cpp | 25 +++++++++++++++++++++++++
2 files changed, 40 insertions(+)
create mode 100644 clang/test/Parser/attr-export.c
create mode 100644 clang/test/Parser/attr-export.cpp
diff --git a/clang/test/Parser/attr-export.c b/clang/test/Parser/attr-export.c
new file mode 100644
index 0000000000000..5742127f411b3
--- /dev/null
+++ b/clang/test/Parser/attr-export.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos -fsyntax-only -verify %s
+
+int _Export *p; // expected-error {{expected identifier or '('}}
+int * _Export *p2; // expected-error {{expected identifier or '('}}
+int *pr3 _Export; // expected-error {{expected ';' after top level declarator}}
+
+int f _Export (); // expected-error {{expected ';' after top level declarator}}
+int f2() _Export; // expected-error {{expected function body after function declarator}}
+
+
+int * _Export pg;
+
+int (*_Export fp)();
+
+int _Export fg();
diff --git a/clang/test/Parser/attr-export.cpp b/clang/test/Parser/attr-export.cpp
new file mode 100644
index 0000000000000..1fd3965edb1ef
--- /dev/null
+++ b/clang/test/Parser/attr-export.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos -fsyntax-only -verify %s
+
+int _Export *p; // expected-error {{expected unqualified-id}}
+int * _Export *p2; // expected-error {{expected unqualified-id}}
+int *pr3 _Export; // expected-error {{expected ';' after top level declarator}}
+
+int f _Export (); // expected-error {{expected ';' after top level declarator}}
+int f2() _Export; // expected-error {{expected function body after function declarator}}
+
+
+int * _Export pg;
+
+int (*_Export fp)();
+
+int _Export fg();
+
+struct S {};
+
+int S::* _Export mp1;
+int _Export S::* mp2; // expected-error {{expected unqualified-id}}
+
+struct _Export S1 {};
+struct S2 _Export {}; // expected-error {{expected unqualified-id}}
+_Export struct S3 {}; // expected-error {{expected unqualified-id}}
+
More information about the cfe-commits
mailing list