Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 1 addition & 3 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6071,9 +6071,7 @@ class AbstractStorageDecl : public ValueDecl {

/// Return true if this is a property that either has storage
/// or init accessor associated with it.
bool supportsInitialization() const {
return hasStorage() || hasInitAccessor();
}
bool supportsInitialization() const;

/// Return true if this storage has the basic accessors/capability
/// to be mutated. This is generally constant after the accessors are
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ DECL_ATTR(_rawLayout, RawLayout,
146)

DECL_ATTR(_extern, Extern,
OnFunc,
OnFunc | OnVar,
AllowMultipleAttributes | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
147)

Expand Down
11 changes: 7 additions & 4 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2123,8 +2123,8 @@ WARNING(reserved_runtime_symbol_name,none,
// @_extern
ERROR(attr_extern_experimental,none,
"'@_extern' requires '-enable-experimental-feature Extern'", ())
ERROR(extern_not_at_top_level_func,none,
"'@_extern' can only be applied to global functions", ())
ERROR(extern_not_at_top_level,none,
"'@_extern' can only be applied to globals", ())
ERROR(extern_empty_c_name,none,
"expected non-empty C name in '@_extern'", ())
ERROR(extern_only_non_other_attr,none,
Expand All @@ -2134,6 +2134,8 @@ WARNING(extern_c_maybe_invalid_name, none,
"C name '%0' may be invalid; explicitly specify the name in "
"'@_extern(c)' to suppress this warning",
(StringRef))
ERROR(extern_variable_initializer, none,
"'@_extern' variable cannot have an initializer", ())

// @_staticExclusiveOnly
ERROR(attr_static_exclusive_only_disabled,none,
Expand Down Expand Up @@ -2170,13 +2172,14 @@ ERROR(c_func_variadic, none,
ERROR(c_func_unsupported_specifier, none,
"cannot declare '%0' argument %1 in %kind2",
(StringRef, DeclName, const ValueDecl *))
ERROR(c_func_unsupported_type, none, "%0 cannot be represented in C",
ERROR(c_unsupported_type, none, "%0 cannot be represented in C",
(Type))
ERROR(c_func_async,none,
"async functions cannot be represented in C", ())
ERROR(c_func_throws,none,
"raising errors from C functions is not supported", ())

ERROR(c_var_computed,none,
"computed variable cannot be represented in C", ())
ERROR(expose_wasm_not_at_top_level_func,none,
"'@_expose' with 'wasm' can only be applied to global "
"functions", ())
Expand Down
8 changes: 4 additions & 4 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,9 @@ class IsObjCRequest :

/// Determine whether the given declaration has
/// a C-compatible interface.
class IsCCompatibleFuncDeclRequest :
public SimpleRequest<IsCCompatibleFuncDeclRequest,
bool(FuncDecl *),
class IsCCompatibleDeclRequest :
public SimpleRequest<IsCCompatibleDeclRequest,
bool(ValueDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
Expand All @@ -287,7 +287,7 @@ class IsCCompatibleFuncDeclRequest :
friend SimpleRequest;

// Evaluation.
bool evaluate(Evaluator &evaluator, FuncDecl *decl) const;
bool evaluate(Evaluator &evaluator, ValueDecl *decl) const;

public:
// Caching.
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,8 @@ SWIFT_REQUEST(TypeChecker, SerializeAttrGenericSignatureRequest,
SWIFT_REQUEST(TypeChecker, IsFunctionBodySkippedRequest,
bool(const AbstractFunctionDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsCCompatibleFuncDeclRequest,
bool(const FuncDecl *),
SWIFT_REQUEST(TypeChecker, IsCCompatibleDeclRequest,
bool(const ValueDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SemanticDeclAttrsRequest,
DeclAttributes(const Decl *),
Expand Down
11 changes: 11 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7977,6 +7977,13 @@ bool AbstractStorageDecl::hasInitAccessor() const {
HasInitAccessorRequest{const_cast<AbstractStorageDecl *>(this)}, false);
}

bool AbstractStorageDecl::supportsInitialization() const {
if (getAttrs().hasAttribute<ExternAttr>())
return false;

return hasStorage() || hasInitAccessor();
}

VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer,
SourceLoc nameLoc, Identifier name,
DeclContext *dc, StorageIsMutable_t supportsMutation)
Expand Down Expand Up @@ -8145,6 +8152,10 @@ bool VarDecl::isLazilyInitializedGlobal() const {
if (getAttrs().hasAttribute<SILGenNameAttr>())
return false;

// @_extern declarations are not initialized.
if (getAttrs().hasAttribute<ExternAttr>())
return false;

// Top-level global variables in the main source file and in the REPL are not
// lazily initialized.
return !isTopLevelGlobal();
Expand Down
5 changes: 4 additions & 1 deletion lib/ASTGen/Sources/ASTGen/Decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,10 @@ extension ASTGenVisitor {
}

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

let topLevelDecl: BridgedTopLevelCodeDecl?
if self.declContext.isModuleScopeContext, self.declContext.parentSourceFile.isScriptMode, !isConst {
Expand Down
4 changes: 3 additions & 1 deletion lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8747,12 +8747,14 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
SourceLoc VarLoc = newBindingContext.getIntroducer() ? consumeToken() : Tok.getLoc();

bool IsConst = Attributes.hasAttribute<SectionAttr>() ||
Attributes.hasAttribute<ConstValAttr>();
Attributes.hasAttribute<ConstValAttr>() ||
Attributes.hasAttribute<ExternAttr>();

// If this is a var in the top-level of script/repl source file, wrap the
// PatternBindingDecl in a TopLevelCodeDecl, since it represents executable
// code. The VarDecl and any accessor decls (for computed properties) go in
// CurDeclContext. @const/@section globals are not top-level, per SE-0492.
// We follow the same rule for @_extern.
TopLevelCodeDecl *topLevelDecl = nullptr;
if (allowTopLevelCode() && CurDeclContext->isModuleScopeContext() &&
!IsConst) {
Expand Down
7 changes: 6 additions & 1 deletion lib/SILGen/SILGenGlobalVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ SILGlobalVariable *SILGenModule::getSILGlobalVariable(VarDecl *gDecl,
formalLinkage = getDeclLinkage(gDecl);
auto silLinkage = getSILLinkage(formalLinkage, forDef);

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

if (cExternAttr) {
silGlobal->setAsmName(cExternAttr->getCName(gDecl));
}

return silGlobal;
}

Expand Down
130 changes: 81 additions & 49 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2538,58 +2538,78 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {
}
}

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

bool foundError = false;

if (FD->hasAsync()) {
FD->diagnose(diag::c_func_async);
foundError = true;
}

if (FD->hasThrows()) {
FD->diagnose(diag::c_func_throws);
foundError = true;
}

// --- Check for unsupported result types.
Type resultTy = FD->getResultInterfaceType();
if (!resultTy->isVoid() && !resultTy->isRepresentableIn(ForeignLanguage::C, FD)) {
FD->diagnose(diag::c_func_unsupported_type, resultTy);
foundError = true;
}
if (auto FD = dyn_cast<FuncDecl>(VD)) {
bool foundError = false;

for (auto *param : *FD->getParameters()) {
// --- Check for unsupported specifiers.
if (param->isVariadic()) {
FD->diagnose(diag::c_func_variadic, param->getName(), FD);
if (FD->hasAsync()) {
FD->diagnose(diag::c_func_async);
foundError = true;
}
if (param->getSpecifier() != ParamSpecifier::Default) {
param
->diagnose(diag::c_func_unsupported_specifier,
ParamDecl::getSpecifierSpelling(param->getSpecifier()),
param->getName(), FD)
.fixItRemove(param->getSpecifierLoc());

if (FD->hasThrows()) {
FD->diagnose(diag::c_func_throws);
foundError = true;
}

// --- Check for unsupported parameter types.
auto paramTy = param->getTypeInContext();
if (!paramTy->isRepresentableIn(ForeignLanguage::C, FD)) {
param->diagnose(diag::c_func_unsupported_type, paramTy);
// --- Check for unsupported result types.
Type resultTy = FD->getResultInterfaceType();
if (!resultTy->isVoid() && !resultTy->isRepresentableIn(ForeignLanguage::C, FD)) {
FD->diagnose(diag::c_unsupported_type, resultTy);
foundError = true;
}

for (auto *param : *FD->getParameters()) {
// --- Check for unsupported specifiers.
if (param->isVariadic()) {
FD->diagnose(diag::c_func_variadic, param->getName(), FD);
foundError = true;
}
if (param->getSpecifier() != ParamSpecifier::Default) {
param
->diagnose(diag::c_func_unsupported_specifier,
ParamDecl::getSpecifierSpelling(param->getSpecifier()),
param->getName(), FD)
.fixItRemove(param->getSpecifierLoc());
foundError = true;
}

// --- Check for unsupported parameter types.
auto paramTy = param->getTypeInContext();
if (!paramTy->isRepresentableIn(ForeignLanguage::C, FD)) {
param->diagnose(diag::c_unsupported_type, paramTy);
foundError = true;
}
}
return !foundError;
}
return !foundError;

if (auto var = dyn_cast<VarDecl>(VD)) {
Type varType = var->getInterfaceType();
if (!varType->isRepresentableIn(ForeignLanguage::C,
var->getDeclContext())) {
var->diagnose(diag::c_unsupported_type, varType);
return true;
}

if (!var->hasStorage()) {
var->diagnose(diag::c_var_computed);
return false;
}

return true;
}

return false;
}

static bool isCCompatibleFuncDecl(FuncDecl *FD) {
return evaluateOrDefault(FD->getASTContext().evaluator,
IsCCompatibleFuncDeclRequest{FD}, {});
static bool isCCompatibleDecl(ValueDecl *VD) {
return evaluateOrDefault(VD->getASTContext().evaluator,
IsCCompatibleDeclRequest{VD}, {});
}

void AttributeChecker::visitExternAttr(ExternAttr *attr) {
Expand All @@ -2598,17 +2618,27 @@ void AttributeChecker::visitExternAttr(ExternAttr *attr) {
diagnoseAndRemoveAttr(attr, diag::attr_extern_experimental);
return;
}

// Only top-level func or static func decls are currently supported.
auto *FD = dyn_cast<FuncDecl>(D);
if (!FD || (FD->getDeclContext()->isTypeContext() && !FD->isStatic())) {
diagnose(attr->getLocation(), diag::extern_not_at_top_level_func);
}

auto VD = dyn_cast<ValueDecl>(D);
if (!VD)
return;

// Check the declaration context.
// FIXME: Unify this with @c / @_cdecl checking.
if (VD->getDeclContext()->isModuleScopeContext()) {
// Top-level declarations are okay.
} else if (VD->getDeclContext()->isTypeContext() &&
!VD->getDeclContext()->getGenericSignatureOfContext() &&
((isa<FuncDecl>(VD) && cast<FuncDecl>(VD)->isStatic()) ||
(isa<VarDecl>(VD) && cast<VarDecl>(VD)->isStatic()))) {
// Static members of non-generic types are okay.
} else {
diagnose(attr->getLocation(), diag::extern_not_at_top_level);
}

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

// Ensure the decl has C compatible interface. Otherwise it produces diagnostics.
if (!isCCompatibleFuncDecl(FD)) {
if (!isCCompatibleDecl(VD)) {
attr->setInvalid();
// Mark the decl itself invalid not to require body even with invalid ExternAttr.
FD->setInvalid();
VD->setInvalid();
}
}

Expand Down Expand Up @@ -5697,7 +5727,9 @@ TypeChecker::diagnosticIfDeclCannotBeUnavailable(const Decl *D,
static bool shouldBlockImplicitDynamic(Decl *D) {
if (D->getAttrs().hasAttribute<SILGenNameAttr>() ||
D->getAttrs().hasAttribute<TransparentAttr>() ||
D->getAttrs().hasAttribute<InlinableAttr>())
D->getAttrs().hasAttribute<InlinableAttr>() ||
D->getAttrs().hasAttribute<CDeclAttr>() ||
D->getAttrs().hasAttribute<ExternAttr>())
return true;
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2589,6 +2589,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
if (!var->hasStorage())
return;

// If the variable is @_extern, it never needs an initializer.
if (var->getAttrs().hasAttribute<ExternAttr>())
return;

if (var->getAttrs().hasAttribute<SILGenNameAttr>()
|| !ABIRoleInfo(var).providesAPI())
return;
Expand Down
25 changes: 20 additions & 5 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,16 +514,22 @@ const PatternBindingEntry *PatternBindingEntryRequest::evaluate(
}
}

auto isInitAccessorProperty = [](Pattern *pattern) {
auto *var = pattern->getSingleVar();
return var && var->getAccessor(AccessorKind::Init);
auto supportsInitialization = [&] {
bool anySupportsInitialization = false;
pattern->forEachVariable([&](VarDecl *var) {
if (var->supportsInitialization()) {
anySupportsInitialization = true;
}
});

return anySupportsInitialization;
};

// If we have a type but no initializer, check whether the type is
// default-initializable. If so, do it.
if (!pbe.isInitialized() &&
binding->isDefaultInitializable(entryNumber) &&
(pattern->hasStorage() || isInitAccessorProperty(pattern))) {
supportsInitialization()) {
if (auto defaultInit = TypeChecker::buildDefaultInitializer(patternType)) {
// If we got a default initializer, install it and re-type-check it
// to make sure it is properly coerced to the pattern type.
Expand Down Expand Up @@ -598,7 +604,16 @@ const PatternBindingEntry *PatternBindingEntryRequest::evaluate(
Context.Diags.diagnose(binding->getPattern(entryNumber)->getLoc(),
diag::destructuring_let_struct_stored_property_unsupported);
}


// Extern declarations are not permitted to have initializers.
if (auto singleVar = binding->getSingleVar()) {
if (singleVar->getAttrs().hasAttribute<ExternAttr>() &&
pbe.isInitialized()) {
singleVar->diagnose(diag::extern_variable_initializer)
.highlight(pbe.getOriginalInitRange());
}
}

return &pbe;
}

Expand Down
Loading