Skip to content

Commit 66e7a78

Browse files
authored
Merge pull request #85370 from DougGregor/extern-global-variables
2 parents a302d4e + c8a60c8 commit 66e7a78

File tree

14 files changed

+182
-87
lines changed

14 files changed

+182
-87
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6071,9 +6071,7 @@ class AbstractStorageDecl : public ValueDecl {
60716071

60726072
/// Return true if this is a property that either has storage
60736073
/// or init accessor associated with it.
6074-
bool supportsInitialization() const {
6075-
return hasStorage() || hasInitAccessor();
6076-
}
6074+
bool supportsInitialization() const;
60776075

60786076
/// Return true if this storage has the basic accessors/capability
60796077
/// to be mutated. This is generally constant after the accessors are

include/swift/AST/DeclAttr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ DECL_ATTR(_rawLayout, RawLayout,
774774
146)
775775

776776
DECL_ATTR(_extern, Extern,
777-
OnFunc,
777+
OnFunc | OnVar,
778778
AllowMultipleAttributes | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
779779
147)
780780

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,8 +2123,8 @@ WARNING(reserved_runtime_symbol_name,none,
21232123
// @_extern
21242124
ERROR(attr_extern_experimental,none,
21252125
"'@_extern' requires '-enable-experimental-feature Extern'", ())
2126-
ERROR(extern_not_at_top_level_func,none,
2127-
"'@_extern' can only be applied to global functions", ())
2126+
ERROR(extern_not_at_top_level,none,
2127+
"'@_extern' can only be applied to globals", ())
21282128
ERROR(extern_empty_c_name,none,
21292129
"expected non-empty C name in '@_extern'", ())
21302130
ERROR(extern_only_non_other_attr,none,
@@ -2134,6 +2134,8 @@ WARNING(extern_c_maybe_invalid_name, none,
21342134
"C name '%0' may be invalid; explicitly specify the name in "
21352135
"'@_extern(c)' to suppress this warning",
21362136
(StringRef))
2137+
ERROR(extern_variable_initializer, none,
2138+
"'@_extern' variable cannot have an initializer", ())
21372139

21382140
// @_staticExclusiveOnly
21392141
ERROR(attr_static_exclusive_only_disabled,none,
@@ -2170,13 +2172,14 @@ ERROR(c_func_variadic, none,
21702172
ERROR(c_func_unsupported_specifier, none,
21712173
"cannot declare '%0' argument %1 in %kind2",
21722174
(StringRef, DeclName, const ValueDecl *))
2173-
ERROR(c_func_unsupported_type, none, "%0 cannot be represented in C",
2175+
ERROR(c_unsupported_type, none, "%0 cannot be represented in C",
21742176
(Type))
21752177
ERROR(c_func_async,none,
21762178
"async functions cannot be represented in C", ())
21772179
ERROR(c_func_throws,none,
21782180
"raising errors from C functions is not supported", ())
2179-
2181+
ERROR(c_var_computed,none,
2182+
"computed variable cannot be represented in C", ())
21802183
ERROR(expose_wasm_not_at_top_level_func,none,
21812184
"'@_expose' with 'wasm' can only be applied to global "
21822185
"functions", ())

include/swift/AST/TypeCheckRequests.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ class IsObjCRequest :
276276

277277
/// Determine whether the given declaration has
278278
/// a C-compatible interface.
279-
class IsCCompatibleFuncDeclRequest :
280-
public SimpleRequest<IsCCompatibleFuncDeclRequest,
281-
bool(FuncDecl *),
279+
class IsCCompatibleDeclRequest :
280+
public SimpleRequest<IsCCompatibleDeclRequest,
281+
bool(ValueDecl *),
282282
RequestFlags::Cached> {
283283
public:
284284
using SimpleRequest::SimpleRequest;
@@ -287,7 +287,7 @@ class IsCCompatibleFuncDeclRequest :
287287
friend SimpleRequest;
288288

289289
// Evaluation.
290-
bool evaluate(Evaluator &evaluator, FuncDecl *decl) const;
290+
bool evaluate(Evaluator &evaluator, ValueDecl *decl) const;
291291

292292
public:
293293
// Caching.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,8 @@ SWIFT_REQUEST(TypeChecker, SerializeAttrGenericSignatureRequest,
581581
SWIFT_REQUEST(TypeChecker, IsFunctionBodySkippedRequest,
582582
bool(const AbstractFunctionDecl *),
583583
SeparatelyCached, NoLocationInfo)
584-
SWIFT_REQUEST(TypeChecker, IsCCompatibleFuncDeclRequest,
585-
bool(const FuncDecl *),
584+
SWIFT_REQUEST(TypeChecker, IsCCompatibleDeclRequest,
585+
bool(const ValueDecl *),
586586
Cached, NoLocationInfo)
587587
SWIFT_REQUEST(TypeChecker, SemanticDeclAttrsRequest,
588588
DeclAttributes(const Decl *),

lib/AST/Decl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7990,6 +7990,13 @@ bool AbstractStorageDecl::hasInitAccessor() const {
79907990
HasInitAccessorRequest{const_cast<AbstractStorageDecl *>(this)}, false);
79917991
}
79927992

7993+
bool AbstractStorageDecl::supportsInitialization() const {
7994+
if (getAttrs().hasAttribute<ExternAttr>())
7995+
return false;
7996+
7997+
return hasStorage() || hasInitAccessor();
7998+
}
7999+
79938000
VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer,
79948001
SourceLoc nameLoc, Identifier name,
79958002
DeclContext *dc, StorageIsMutable_t supportsMutation)
@@ -8158,6 +8165,10 @@ bool VarDecl::isLazilyInitializedGlobal() const {
81588165
if (getAttrs().hasAttribute<SILGenNameAttr>())
81598166
return false;
81608167

8168+
// @_extern declarations are not initialized.
8169+
if (getAttrs().hasAttribute<ExternAttr>())
8170+
return false;
8171+
81618172
// Top-level global variables in the main source file and in the REPL are not
81628173
// lazily initialized.
81638174
return !isTopLevelGlobal();

lib/ASTGen/Sources/ASTGen/Decls.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,10 @@ extension ASTGenVisitor {
612612
}
613613

614614
// @const/@section globals are not top-level, per SE-0492.
615-
let isConst = attrs.attributes.hasAttribute(.Section) || attrs.attributes.hasAttribute(.ConstVal)
615+
// We follow the same rule for @_extern.
616+
let isConst = attrs.attributes.hasAttribute(.Section)
617+
|| attrs.attributes.hasAttribute(.ConstVal)
618+
|| attrs.attributes.hasAttribute(.Extern)
616619

617620
let topLevelDecl: BridgedTopLevelCodeDecl?
618621
if self.declContext.isModuleScopeContext, self.declContext.parentSourceFile.isScriptMode, !isConst {

lib/Parse/ParseDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8747,12 +8747,14 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
87478747
SourceLoc VarLoc = newBindingContext.getIntroducer() ? consumeToken() : Tok.getLoc();
87488748

87498749
bool IsConst = Attributes.hasAttribute<SectionAttr>() ||
8750-
Attributes.hasAttribute<ConstValAttr>();
8750+
Attributes.hasAttribute<ConstValAttr>() ||
8751+
Attributes.hasAttribute<ExternAttr>();
87518752

87528753
// If this is a var in the top-level of script/repl source file, wrap the
87538754
// PatternBindingDecl in a TopLevelCodeDecl, since it represents executable
87548755
// code. The VarDecl and any accessor decls (for computed properties) go in
87558756
// CurDeclContext. @const/@section globals are not top-level, per SE-0492.
8757+
// We follow the same rule for @_extern.
87568758
TopLevelCodeDecl *topLevelDecl = nullptr;
87578759
if (allowTopLevelCode() && CurDeclContext->isModuleScopeContext() &&
87588760
!IsConst) {

lib/SILGen/SILGenGlobalVariable.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ SILGlobalVariable *SILGenModule::getSILGlobalVariable(VarDecl *gDecl,
5050
formalLinkage = getDeclLinkage(gDecl);
5151
auto silLinkage = getSILLinkage(formalLinkage, forDef);
5252

53-
if (gDecl->getAttrs().hasAttribute<SILGenNameAttr>()) {
53+
auto cExternAttr = ExternAttr::find(gDecl->getAttrs(), ExternKind::C);
54+
if (gDecl->getAttrs().hasAttribute<SILGenNameAttr>() || cExternAttr) {
5455
silLinkage = SILLinkage::DefaultForDeclaration;
5556
if (! gDecl->hasInitialValue()) {
5657
forDef = NotForDefinition;
@@ -77,6 +78,10 @@ SILGlobalVariable *SILGenModule::getSILGlobalVariable(VarDecl *gDecl,
7778
if (auto sectionAttr = gDecl->getAttrs().getAttribute<SectionAttr>())
7879
silGlobal->setSection(sectionAttr->Name);
7980

81+
if (cExternAttr) {
82+
silGlobal->setAsmName(cExternAttr->getCName(gDecl));
83+
}
84+
8085
return silGlobal;
8186
}
8287

lib/Sema/TypeCheckAttr.cpp

Lines changed: 81 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2538,58 +2538,78 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {
25382538
}
25392539
}
25402540

2541-
bool IsCCompatibleFuncDeclRequest::evaluate(Evaluator &evaluator,
2542-
FuncDecl *FD) const {
2543-
if (FD->isInvalid())
2541+
bool IsCCompatibleDeclRequest::evaluate(Evaluator &evaluator,
2542+
ValueDecl *VD) const {
2543+
if (VD->isInvalid())
25442544
return false;
25452545

2546-
bool foundError = false;
2547-
2548-
if (FD->hasAsync()) {
2549-
FD->diagnose(diag::c_func_async);
2550-
foundError = true;
2551-
}
2552-
2553-
if (FD->hasThrows()) {
2554-
FD->diagnose(diag::c_func_throws);
2555-
foundError = true;
2556-
}
2557-
2558-
// --- Check for unsupported result types.
2559-
Type resultTy = FD->getResultInterfaceType();
2560-
if (!resultTy->isVoid() && !resultTy->isRepresentableIn(ForeignLanguage::C, FD)) {
2561-
FD->diagnose(diag::c_func_unsupported_type, resultTy);
2562-
foundError = true;
2563-
}
2546+
if (auto FD = dyn_cast<FuncDecl>(VD)) {
2547+
bool foundError = false;
25642548

2565-
for (auto *param : *FD->getParameters()) {
2566-
// --- Check for unsupported specifiers.
2567-
if (param->isVariadic()) {
2568-
FD->diagnose(diag::c_func_variadic, param->getName(), FD);
2549+
if (FD->hasAsync()) {
2550+
FD->diagnose(diag::c_func_async);
25692551
foundError = true;
25702552
}
2571-
if (param->getSpecifier() != ParamSpecifier::Default) {
2572-
param
2573-
->diagnose(diag::c_func_unsupported_specifier,
2574-
ParamDecl::getSpecifierSpelling(param->getSpecifier()),
2575-
param->getName(), FD)
2576-
.fixItRemove(param->getSpecifierLoc());
2553+
2554+
if (FD->hasThrows()) {
2555+
FD->diagnose(diag::c_func_throws);
25772556
foundError = true;
25782557
}
25792558

2580-
// --- Check for unsupported parameter types.
2581-
auto paramTy = param->getTypeInContext();
2582-
if (!paramTy->isRepresentableIn(ForeignLanguage::C, FD)) {
2583-
param->diagnose(diag::c_func_unsupported_type, paramTy);
2559+
// --- Check for unsupported result types.
2560+
Type resultTy = FD->getResultInterfaceType();
2561+
if (!resultTy->isVoid() && !resultTy->isRepresentableIn(ForeignLanguage::C, FD)) {
2562+
FD->diagnose(diag::c_unsupported_type, resultTy);
25842563
foundError = true;
25852564
}
2565+
2566+
for (auto *param : *FD->getParameters()) {
2567+
// --- Check for unsupported specifiers.
2568+
if (param->isVariadic()) {
2569+
FD->diagnose(diag::c_func_variadic, param->getName(), FD);
2570+
foundError = true;
2571+
}
2572+
if (param->getSpecifier() != ParamSpecifier::Default) {
2573+
param
2574+
->diagnose(diag::c_func_unsupported_specifier,
2575+
ParamDecl::getSpecifierSpelling(param->getSpecifier()),
2576+
param->getName(), FD)
2577+
.fixItRemove(param->getSpecifierLoc());
2578+
foundError = true;
2579+
}
2580+
2581+
// --- Check for unsupported parameter types.
2582+
auto paramTy = param->getTypeInContext();
2583+
if (!paramTy->isRepresentableIn(ForeignLanguage::C, FD)) {
2584+
param->diagnose(diag::c_unsupported_type, paramTy);
2585+
foundError = true;
2586+
}
2587+
}
2588+
return !foundError;
25862589
}
2587-
return !foundError;
2590+
2591+
if (auto var = dyn_cast<VarDecl>(VD)) {
2592+
Type varType = var->getInterfaceType();
2593+
if (!varType->isRepresentableIn(ForeignLanguage::C,
2594+
var->getDeclContext())) {
2595+
var->diagnose(diag::c_unsupported_type, varType);
2596+
return true;
2597+
}
2598+
2599+
if (!var->hasStorage()) {
2600+
var->diagnose(diag::c_var_computed);
2601+
return false;
2602+
}
2603+
2604+
return true;
2605+
}
2606+
2607+
return false;
25882608
}
25892609

2590-
static bool isCCompatibleFuncDecl(FuncDecl *FD) {
2591-
return evaluateOrDefault(FD->getASTContext().evaluator,
2592-
IsCCompatibleFuncDeclRequest{FD}, {});
2610+
static bool isCCompatibleDecl(ValueDecl *VD) {
2611+
return evaluateOrDefault(VD->getASTContext().evaluator,
2612+
IsCCompatibleDeclRequest{VD}, {});
25932613
}
25942614

25952615
void AttributeChecker::visitExternAttr(ExternAttr *attr) {
@@ -2598,17 +2618,27 @@ void AttributeChecker::visitExternAttr(ExternAttr *attr) {
25982618
diagnoseAndRemoveAttr(attr, diag::attr_extern_experimental);
25992619
return;
26002620
}
2601-
2602-
// Only top-level func or static func decls are currently supported.
2603-
auto *FD = dyn_cast<FuncDecl>(D);
2604-
if (!FD || (FD->getDeclContext()->isTypeContext() && !FD->isStatic())) {
2605-
diagnose(attr->getLocation(), diag::extern_not_at_top_level_func);
2606-
}
26072621

2622+
auto VD = dyn_cast<ValueDecl>(D);
2623+
if (!VD)
2624+
return;
2625+
2626+
// Check the declaration context.
2627+
// FIXME: Unify this with @c / @_cdecl checking.
2628+
if (VD->getDeclContext()->isModuleScopeContext()) {
2629+
// Top-level declarations are okay.
2630+
} else if (VD->getDeclContext()->isTypeContext() &&
2631+
!VD->getDeclContext()->getGenericSignatureOfContext() &&
2632+
((isa<FuncDecl>(VD) && cast<FuncDecl>(VD)->isStatic()) ||
2633+
(isa<VarDecl>(VD) && cast<VarDecl>(VD)->isStatic()))) {
2634+
// Static members of non-generic types are okay.
2635+
} else {
2636+
diagnose(attr->getLocation(), diag::extern_not_at_top_level);
2637+
}
26082638

26092639
// C name must not be empty.
26102640
if (attr->getExternKind() == ExternKind::C) {
2611-
StringRef cName = attr->getCName(FD);
2641+
StringRef cName = attr->getCName(VD);
26122642
if (cName.empty()) {
26132643
diagnose(attr->getLocation(), diag::extern_empty_c_name);
26142644
} else if (!attr->Name.has_value() && !clang::isValidAsciiIdentifier(cName)) {
@@ -2630,10 +2660,10 @@ void AttributeChecker::visitExternAttr(ExternAttr *attr) {
26302660
}
26312661

26322662
// Ensure the decl has C compatible interface. Otherwise it produces diagnostics.
2633-
if (!isCCompatibleFuncDecl(FD)) {
2663+
if (!isCCompatibleDecl(VD)) {
26342664
attr->setInvalid();
26352665
// Mark the decl itself invalid not to require body even with invalid ExternAttr.
2636-
FD->setInvalid();
2666+
VD->setInvalid();
26372667
}
26382668
}
26392669

@@ -5715,7 +5745,9 @@ TypeChecker::diagnosticIfDeclCannotBeUnavailable(const Decl *D,
57155745
static bool shouldBlockImplicitDynamic(Decl *D) {
57165746
if (D->getAttrs().hasAttribute<SILGenNameAttr>() ||
57175747
D->getAttrs().hasAttribute<TransparentAttr>() ||
5718-
D->getAttrs().hasAttribute<InlinableAttr>())
5748+
D->getAttrs().hasAttribute<InlinableAttr>() ||
5749+
D->getAttrs().hasAttribute<CDeclAttr>() ||
5750+
D->getAttrs().hasAttribute<ExternAttr>())
57195751
return true;
57205752
return false;
57215753
}

0 commit comments

Comments
 (0)