Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e8d355c
initial work for pragma export & _Export keyword
perry-ca Oct 2, 2024
6cf4355
Merge remote-tracking branch 'origin/main' into perry/pragma-export
perry-ca Oct 2, 2024
e1cbba0
Add pragma export & _Export
perry-ca Oct 3, 2024
b23cfff
Merge remote-tracking branch 'origin/main' into perry/pragma-export
perry-ca Oct 3, 2024
39a1411
restore to main
perry-ca Oct 3, 2024
9cfc3cc
Reset pragma handler was missing
perry-ca Oct 3, 2024
e0cb769
formating
perry-ca Oct 3, 2024
82e9962
more formating
perry-ca Oct 3, 2024
e7e560a
missing requires & s390x-none-zos targets in some
perry-ca Oct 3, 2024
b5c7f01
merge conflict
perry-ca Oct 7, 2024
b99182f
Merge branch 'llvm:main' into perry/pragma-export
perry-ca Nov 26, 2024
5d40056
wip
perry-ca Oct 28, 2024
266840a
change func names
perry-ca Nov 26, 2024
1deb06a
Merge branch 'llvm:main' into perry/pragma-export
perry-ca Dec 12, 2024
5fb85ea
Merge branch 'llvm:main' into perry/pragma-export
perry-ca Mar 7, 2025
0fcfcfe
address comments
perry-ca Mar 28, 2025
b54bd17
handle arrays & func ptrs better
perry-ca Mar 31, 2025
6ba783c
Add error checking
perry-ca Apr 4, 2025
b407888
resolve merge conflict
perry-ca Apr 4, 2025
a1faeca
remove dead code
perry-ca Apr 4, 2025
7871a6d
formatting
perry-ca Apr 4, 2025
9672490
add release note
perry-ca Apr 4, 2025
a156ebd
add parsing lit tests
perry-ca Apr 8, 2025
c6eb30f
Merge remote-tracking branch 'origin/main' into perry/pragma-export
perry-ca Apr 8, 2025
5f0a685
Improve locations in messages
perry-ca Apr 8, 2025
a5187aa
more error handling & better reuse
perry-ca May 2, 2025
507960b
update tests
perry-ca May 2, 2025
aa8ffc7
test case updates
perry-ca May 2, 2025
34b2109
merge latest
perry-ca May 2, 2025
8432125
remove unused function and improve interface to other
perry-ca May 5, 2025
1198786
Merge remote-tracking branch 'origin/main' into perry/pragma-export
perry-ca May 5, 2025
9e23c5d
handle externC properly for C++
perry-ca May 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ASTConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ class ASTConsumer {
/// completed.
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}

/// CompletePragmaExport - complete #pragma export statements.
virtual void CompletePragmaExport(Decl *D) {}

/// Callback invoked when an MSInheritanceAttr has been attached to a
/// CXXRecordDecl.
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -4520,6 +4520,12 @@ def ReleaseHandle : InheritableParamAttr {
let Documentation = [ReleaseHandleDocs];
}

def zOSExport : InheritableAttr {
let Spellings = [CustomKeyword<"_Export">];
let Subjects = SubjectList<[Function, Var, CXXRecord]>;
let Documentation = [zOSExportDocs];
}

def UnsafeBufferUsage : InheritableAttr {
let Spellings = [Clang<"unsafe_buffer_usage">];
let Subjects = SubjectList<[Function, Field]>;
Expand Down
21 changes: 21 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -6863,6 +6863,27 @@ attribute requires a string literal argument to identify the handle being releas
}];
}

def zOSExportDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Use the _Export keyword with a function name or external variable to declare
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Use the _Export keyword with a function name or external variable to declare
Use the ``_Export`` keyword on a function or external variable to declare

that it is to be exported (made available to other modules). You must define
the object name in the same translation unit in which you use the _Export
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the object name in the same translation unit in which you use the _Export
the object name in the same translation unit in which you use the ``_Export``

keyword. For example:

.. code-block:: c

int _Export anthony(float);

This statement exports the function anthony, if you define the function in the
translation unit. The _Export keyword must immediately precede the object name.
If you apply the _Export keyword to a class, the compiler automatically exports
all static data members and member functions of the class. However, if you want
it to apply to individual class members, then you must apply it to each member
that can be referenced.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This statement exports the function anthony, if you define the function in the
translation unit. The _Export keyword must immediately precede the object name.
If you apply the _Export keyword to a class, the compiler automatically exports
all static data members and member functions of the class. However, if you want
it to apply to individual class members, then you must apply it to each member
that can be referenced.
This statement exports the function ``anthony``, if you define the function in the
translation unit. The ``_Export`` keyword must immediately precede the declaration name.
If you apply the ``_Export`` keyword to a class, the compiler automatically exports
all static data members and member functions of the class. However, if you want
it to apply to individual class members, then you must apply it to each member
that can be referenced.

}];
}

def UnsafeBufferUsageDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,13 @@ def err_pragma_pop_visibility_mismatch : Error<
"#pragma visibility pop with no matching #pragma visibility push">;
def note_surrounding_namespace_starts_here : Note<
"surrounding namespace with visibility attribute starts here">;
def warn_failed_to_resolve_pragma : Warning<
"failed to resolve '#pragma %0' to a declaration">,
InGroup<IgnoredPragmas>;
def warn_pragma_not_applied : Warning<
"#pragma %0 is applicable to symbols with external linkage only; "
"not applied to %1">,
InGroup<IgnoredPragmas>;
def err_pragma_loop_invalid_argument_type : Error<
"invalid argument of type %0; expected an integer type">;
def err_pragma_loop_compatibility : Error<
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ KEYWORD(__func__ , KEYALL)
KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)

// z/OS specific keywords
KEYWORD(_Export , KEYZOS)

// C++ 2.11p1: Keywords.
KEYWORD(asm , KEYCXX|KEYGNU)
Expand Down Expand Up @@ -1003,6 +1005,9 @@ PRAGMA_ANNOTATION(pragma_fp)
// Annotation for the attribute pragma directives - #pragma clang attribute ...
PRAGMA_ANNOTATION(pragma_attribute)

// Annotation for C/C++ #pragma export(ident)
PRAGMA_ANNOTATION(pragma_export)

// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
PRAGMA_ANNOTATION(pragma_riscv)

Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
std::unique_ptr<PragmaHandler> ExportHandler;
std::unique_ptr<PragmaHandler> RISCVPragmaHandler;

std::unique_ptr<CommentHandler> CommentSemaHandler;
Expand Down Expand Up @@ -854,6 +855,18 @@ class Parser : public CodeCompletionHandler {

void HandlePragmaAttribute();

/// Helper functions for handling zOS pragmas.
NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
IdentifierInfo *IdentName);
bool zOSParseParameterList(StringRef PragmaName,
std::optional<SmallVector<QualType, 4>> &TypeList,
Qualifiers &CVQual);
bool zOSHandlePragmaHelper(tok::TokenKind);

/// Handle the annotation token produced for
/// #pragma export ...
void HandlePragmaExport();

/// GetLookAheadToken - This peeks ahead N tokens and returns that token
/// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1)
/// returns the token after Tok, etc.
Expand Down
37 changes: 33 additions & 4 deletions clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ class DeclSpec {
unsigned FS_virtual_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned FS_noreturn_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned export_specified : 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
unsigned export_specified : 1;
unsigned ExportSpecified : 1; // zOS extension


// friend-specifier
LLVM_PREFERRED_TYPE(bool)
Expand Down Expand Up @@ -444,6 +446,7 @@ class DeclSpec {
SourceLocation FS_forceinlineLoc;
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
SourceLocation TQ_pipeLoc;
SourceLocation exportLoc;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SourceLocation exportLoc;
SourceLocation ExportLoc;


WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
Expand Down Expand Up @@ -492,9 +495,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), export_specified(false),
FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}

// storage-class-specifier
Expand Down Expand Up @@ -661,6 +664,9 @@ class DeclSpec {
bool isNoreturnSpecified() const { return FS_noreturn_specified; }
SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }

bool isExportSpecified() const { return export_specified; }
SourceLocation getExportSpecLoc() const { return exportLoc; }

void ClearFunctionSpecs() {
FS_inline_specified = false;
FS_inlineLoc = SourceLocation();
Expand Down Expand Up @@ -811,6 +817,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,
Expand Down Expand Up @@ -1955,6 +1963,10 @@ class Declarator {
LLVM_PREFERRED_TYPE(bool)
unsigned InlineStorageUsed : 1;

/// Indicates whether this is set as _Export
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Indicates whether this is set as _Export
/// Indicates whether this is set as _Export.

LLVM_PREFERRED_TYPE(bool)
unsigned ExportSpecified : 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
unsigned ExportSpecified : 1;
unsigned ExportSpecified : 1; // zOS extension


/// Indicates whether this declarator has an initializer.
LLVM_PREFERRED_TYPE(bool)
unsigned HasInitializer : 1;
Expand Down Expand Up @@ -2001,6 +2013,9 @@ class Declarator {
/// this declarator as a parameter pack.
SourceLocation EllipsisLoc;

/// The source location of the _Export keyword on this declarator
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The source location of the _Export keyword on this declarator
/// The source location of the _Export keyword on this declarator.

SourceLocation ExportLoc;

Expr *PackIndexingExpr;

friend struct DeclaratorChunk;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -2109,6 +2125,18 @@ class Declarator {
Range.setEnd(SR.getEnd());
}

/// Set this declarator as _Export
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Set this declarator as _Export
/// Set this declarator as _Export.

I'll stop commenting on these now, can you take a pass over all the new comments you've added and ensure they have appropriate trailing punctuation?

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();
Expand All @@ -2125,6 +2153,7 @@ class Declarator {
HasInitializer = false;
ObjCIvar = false;
ObjCWeakProperty = false;
ExportSpecified = false;
CommaLoc = SourceLocation();
EllipsisLoc = SourceLocation();
PackIndexingExpr = nullptr;
Expand Down
30 changes: 30 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,36 @@ class Sema final : public SemaBase {
ActOnPragmaMSFunction(SourceLocation Loc,
const llvm::SmallVectorImpl<StringRef> &NoBuiltins);

/// A label from a C++ #pragma export, for a symbol that we
/// haven't seen the declaration for yet. The TypeList is the argument list
/// the function must match if HasTypeList is true.
struct SymbolLabel {
std::optional<SmallVector<QualType, 4>> TypeList;
StringRef MappedName;
SourceLocation NameLoc;
bool HasTypeList;
Qualifiers CVQual;
};

typedef SmallVector<SymbolLabel, 1> PendingSymbolOverloads;
typedef llvm::DenseMap<NestedNameSpecifier *, PendingSymbolOverloads>
SymbolNames;
SymbolNames PendingExportNames;

FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tryFunctionLookUp is dead code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used by another pragma I'm going to upstream next. This isn't used by pragma export but it is used by other pragmas we'll upstream after this one. I missed this when removing all of that code and can remove it for now if you wish.

SourceLocation NameLoc);

/// trySymbolLookUp try to look up a decl matching the nested specifier
/// with optional type list.
NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please name this so it's clear it's specifically the lookup algorithm for pragma export.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I'll pick a name the works with the other pragmas too.

const clang::Sema::SymbolLabel &Label);

/// ActonPragmaExport - called on well-formed '\#pragma export'.
void ActOnPragmaExport(NestedNameSpecifier *NestedId,
SourceLocation ExportNameLoc,
std::optional<SmallVector<QualType, 4>> &&TypeList,
Qualifiers CVQual);

/// Only called on function definitions; if there is a pragma in scope
/// with the effect of a range-based optnone, consider marking the function
/// with attribute optnone.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/BackendConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class BackendConsumer : public ASTConsumer {
void HandleTagDeclRequiredDefinition(const TagDecl *D) override;
void CompleteTentativeDefinition(VarDecl *D) override;
void CompleteExternalDeclaration(DeclaratorDecl *D) override;
void CompletePragmaExport(Decl *D) override;
void AssignInheritanceModel(CXXRecordDecl *RD) override;
void HandleVTable(CXXRecordDecl *RD) override;

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CodeGenAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ void BackendConsumer::CompleteExternalDeclaration(DeclaratorDecl *D) {
Gen->CompleteExternalDeclaration(D);
}

void BackendConsumer::CompletePragmaExport(Decl *D) {
Gen->CompletePragmaExport(D);
}

void BackendConsumer::AssignInheritanceModel(CXXRecordDecl *RD) {
Gen->AssignInheritanceModel(RD);
}
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5275,6 +5275,21 @@ void CodeGenModule::EmitExternalDeclaration(const DeclaratorDecl *D) {
EmitExternalFunctionDeclaration(FD);
}

void CodeGenModule::EmitPragmaExport(const Decl *D) {
StringRef MangledName;
if (auto FD = dyn_cast<FunctionDecl>(D))
MangledName = getMangledName(GlobalDecl(FD));
else if (auto VD = dyn_cast<VarDecl>(D))
MangledName = getMangledName(GlobalDecl(VD));
else
assert(0 && "Unsupported pragma export Decl type");

if (llvm::GlobalValue *GV = GetGlobalValue(MangledName)) {
GV->setVisibility(llvm::GlobalValue::DefaultVisibility);
GV->setDSOLocal(false);
}
}

CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
return Context.toCharUnitsFromBits(
getDataLayout().getTypeStoreSizeInBits(Ty));
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,8 @@ class CodeGenModule : public CodeGenTypeCache {

void EmitExternalDeclaration(const DeclaratorDecl *D);

void EmitPragmaExport(const Decl *D);

void EmitVTable(CXXRecordDecl *Class);

void RefreshTypeCacheForClass(const CXXRecordDecl *Class);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/ModuleBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ namespace {
Builder->EmitExternalDeclaration(D);
}

void CompletePragmaExport(Decl *D) override {
Builder->EmitPragmaExport(D);
}

void HandleVTable(CXXRecordDecl *RD) override {
if (Diags.hasUnrecoverableErrorOccurred())
return;
Expand Down
12 changes: 8 additions & 4 deletions clang/lib/Driver/ToolChains/ZOS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ 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");
}

// Pass "-fno-sized-deallocation" only when the user hasn't manually enabled
// or disabled sized deallocations.
if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
Expand Down Expand Up @@ -149,11 +154,10 @@ void zos::Linker::ConstructJob(Compilation &C, const JobAction &JA,
StringRef OutputName = Output.getFilename();
// Strip away the last file suffix in presence from output name and add
// a new .x suffix.
size_t Suffix = OutputName.find_last_of('.');
const char *SideDeckName =
Args.MakeArgString(OutputName.substr(0, Suffix) + ".x");
SmallString<128> SideDeckName = OutputName;
llvm::sys::path::replace_extension(SideDeckName, "x");
CmdArgs.push_back("-x");
CmdArgs.push_back(SideDeckName);
CmdArgs.push_back(Args.MakeArgString(SideDeckName));
} else {
// We need to direct side file to /dev/null to suppress linker warning when
// the object file contains exported symbols, and -shared or
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4449,6 +4449,12 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;

case tok::kw__Export:
// If we find kw__Export, it is being applied to a var or function
// This will be handled in ParseDeclaratorInternal()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a declaration specifier, shouldn't this work like any other one? e.g. https://godbolt.org/z/s4Tz1vjn4 (it's an abomination, to be sure, but it's odd to claim this as a declaration specifier but then not implement it as one).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _Export keyword is part of the declarator. This code is to stop the declspec parsing. For example, in int _Export x,y; only x is exported. The flag was added to the DeclSpec class to handle the case of class _Export A. That is later passed on to the Decl for the class. If there is a better way to do this, I'd be interested. We do have to match the existing XL compiler for backwards compatibility.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in int _Export x,y; only x is exported.

That is deeply strange behavior IMO. I can't think of anything else in the language that works like that in that syntactic position.

We do have to match the existing XL compiler for backwards compatibility.

That can be handled in the downstream too. For example, in this case, I would expect a diagnostic to let the user know that _Export very likely does not behave the way they expect in your code example.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is deeply strange behavior IMO. I can't think of anything else in the language that works like that in that syntactic position.

In the traditional implementation, _Export is a declarator like * is, except it binds more strongly than (<function parameters>) or [<array bound>].

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thank you for the explanation, that makes more sense to me.

goto DoneWithDeclSpec;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean _Export has to be last? Not a parser guy here, but this one doesn't feel right to me? @AaronBallman can you double check here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_Export is part of the declarator. If you see the keyword, the declspec is done and and you are now in the declarator.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_Export is not really a declaration specifier (at least in the traditional implementation).
It is more like a C++11 attribute except that it lives on the left of a declarator-id instead of on the right.
In some realizations, the (less clean) conceptual model is that it is a declarator like * is, except it binds more strongly than (<function parameters>) or [<array bound>].

@perry-ca, I don't see any test in this PR for _Export in positions like:

int (*_Export x)(void) = 0;

Is the above accepted with this PR? Which conceptual model is being implemented here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test case above is valid and exports x.

@x = global ptr null, align 8

I'll add a test case to cover this.

You are correct. This keyword is expected to appear right before the identifier of the symbol being exported.

break;

// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class) {
Expand Down Expand Up @@ -6174,6 +6180,7 @@ bool Parser::isDeclarationSpecifier(
case tok::kw_virtual:
case tok::kw_explicit:
case tok::kw__Noreturn:
case tok::kw__Export:

// alignment-specifier
case tok::kw__Alignas:
Expand Down Expand Up @@ -6765,6 +6772,17 @@ void Parser::ParseDeclaratorInternal(Declarator &D,

tok::TokenKind Kind = Tok.getKind();

// If this variable or function is marked as _Export, set the bit
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);
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// If attributes exist after tag, parse them.
for (;;) {
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
// If the token is _Export, set the bits
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,
Expand Down
Loading
Loading