Skip to content

Implement experimental@execution(concurrent | caller) attribute #78704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 21, 2025
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
10 changes: 10 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,16 @@ BridgedABIAttr BridgedABIAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc atLoc,
BridgedSourceRange range, BridgedNullableDecl abiDecl);

enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExecutionKind {
BridgedExecutionKindConcurrent,
BridgedExecutionKindCaller,
};

SWIFT_NAME("BridgedExecutionAttr.createParsed(_:atLoc:range:behavior:)")
BridgedExecutionAttr BridgedExecutionAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc atLoc,
BridgedSourceRange range, BridgedExecutionKind behavior);

enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedAccessLevel {
BridgedAccessLevelPrivate,
BridgedAccessLevelFilePrivate,
Expand Down
28 changes: 28 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ class DeclAttribute : public AttributeBase {

NumFeatures : 31
);

SWIFT_INLINE_BITFIELD(ExecutionAttr, DeclAttribute, NumExecutionKindBits,
Behavior : NumExecutionKindBits
);
} Bits;
// clang-format on

Expand Down Expand Up @@ -2922,6 +2926,30 @@ class ABIAttr : public DeclAttribute {
UNIMPLEMENTED_CLONE(ABIAttr)
};

class ExecutionAttr : public DeclAttribute {
public:
ExecutionAttr(SourceLoc AtLoc, SourceRange Range,
ExecutionKind behavior,
bool Implicit)
: DeclAttribute(DeclAttrKind::Execution, AtLoc, Range, Implicit) {
Bits.ExecutionAttr.Behavior = static_cast<uint8_t>(behavior);
}

ExecutionAttr(ExecutionKind behavior, bool Implicit)
: ExecutionAttr(/*AtLoc=*/SourceLoc(), /*Range=*/SourceRange(), behavior,
Implicit) {}

ExecutionKind getBehavior() const {
return static_cast<ExecutionKind>(Bits.ExecutionAttr.Behavior);
}

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DeclAttrKind::Execution;
}

UNIMPLEMENTED_CLONE(ExecutionAttr)
};

/// Attributes that may be applied to declarations.
class DeclAttributes {
/// Linked list of declaration attributes.
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/AttrKind.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ enum class ExternKind: uint8_t {
enum : unsigned { NumExternKindBits =
countBitsUsed(static_cast<unsigned>(ExternKind::Last_ExternKind)) };

enum class ExecutionKind : uint8_t {
Concurrent = 0,
Caller,
Last_ExecutionKind = Caller
};

enum : unsigned { NumExecutionKindBits =
countBitsUsed(static_cast<unsigned>(ExecutionKind::Last_ExecutionKind)) };

enum class DeclAttrKind : unsigned {
#define DECL_ATTR(_, CLASS, ...) CLASS,
#define LAST_DECL_ATTR(CLASS) Last_DeclAttr = CLASS,
Expand Down
7 changes: 6 additions & 1 deletion include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,12 @@ DECL_ATTR(abi, ABI,
165)
DECL_ATTR_FEATURE_REQUIREMENT(ABI, ABIAttribute)

LAST_DECL_ATTR(ABI)
DECL_ATTR(execution, Execution,
OnFunc | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
166)
DECL_ATTR_FEATURE_REQUIREMENT(Execution, NonIsolatedAsyncInheritsIsolationFromContext)

LAST_DECL_ATTR(Execution)

#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
27 changes: 27 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8211,5 +8211,32 @@ ERROR(attr_abi_incompatible_with_silgen_name,none,
"the same purpose",
(DescriptiveDeclKind))

//===----------------------------------------------------------------------===//
// MARK: @execution Attribute
//===----------------------------------------------------------------------===//
ERROR(attr_execution_concurrent_only_on_async,none,
"cannot use '@execution(concurrent)' on non-async %kind0",
(ValueDecl *))

ERROR(attr_execution_concurrent_incompatible_with_global_actor,none,
"cannot use '@execution(concurrent)' on %kind0 isolated to global actor %1",
(ValueDecl *, Type))

ERROR(attr_execution_concurrent_incompatible_isolated_parameter,none,
"cannot use '@execution(concurrent)' on %kind0 because it has "
"an isolated parameter: %1",
(ValueDecl *, ValueDecl *))

ERROR(attr_execution_concurrent_incompatible_dynamically_isolated_parameter,none,
"cannot use '@execution(concurrent)' on %kind0 because it has "
"a dynamically isolated parameter: %1",
(ValueDecl *, ValueDecl *))

ERROR(attr_execution_concurrent_incompatible_with_nonisolated,none,
"cannot use '@execution(concurrent)' and 'nonisolated' on the same %0 "
"because they serve the same purpose",
(ValueDecl *))


#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
2 changes: 2 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ IDENTIFIER(size)
IDENTIFIER(speed)
IDENTIFIER(unchecked)
IDENTIFIER(unsafe)
IDENTIFIER(concurrent)
IDENTIFIER(caller)

// The singleton instance of TupleTypeDecl in the Builtin module
IDENTIFIER(TheTupleType)
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,14 @@ static StringRef getDumpString(NonSendableKind kind) {
return "Specific";
}
}
static StringRef getDumpString(ExecutionKind kind) {
switch (kind) {
case ExecutionKind::Concurrent:
return "concurrent";
case ExecutionKind::Caller:
return "caller";
}
}
static StringRef getDumpString(StringRef s) {
return s;
}
Expand Down Expand Up @@ -3871,6 +3879,11 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, StringRef>,

#undef TRIVIAL_ATTR_PRINTER

void visitExecutionAttr(ExecutionAttr *Attr, StringRef label) {
printCommon(Attr, "execution_attr", label);
printField(Attr->getBehavior(), "behavior");
printFoot();
}
void visitABIAttr(ABIAttr *Attr, StringRef label) {
printCommon(Attr, "abi_attr", label);
printRec(Attr->abiDecl, "decl");
Expand Down
22 changes: 22 additions & 0 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,19 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
break;
}

case DeclAttrKind::Execution: {
auto *attr = cast<ExecutionAttr>(this);
switch (attr->getBehavior()) {
case ExecutionKind::Concurrent:
Printer << "@execution(concurrent)";
break;
case ExecutionKind::Caller:
Printer << "@execution(caller)";
break;
}
break;
}

#define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS:
#include "swift/AST/DeclAttr.def"
llvm_unreachable("handled above");
Expand Down Expand Up @@ -1861,6 +1874,15 @@ StringRef DeclAttribute::getAttrName() const {
}
case DeclAttrKind::Lifetime:
return "lifetime";
case DeclAttrKind::Execution: {
switch (cast<ExecutionAttr>(this)->getBehavior()) {
case ExecutionKind::Concurrent:
return "execution(concurrent)";
case ExecutionKind::Caller:
return "execution(caller)";
}
llvm_unreachable("Invalid execution kind");
}
}
llvm_unreachable("bad DeclAttrKind");
}
Expand Down
18 changes: 18 additions & 0 deletions lib/AST/Bridging/DeclAttributeBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,21 @@ BridgedUnavailableFromAsyncAttr BridgedUnavailableFromAsyncAttr_createParsed(
UnavailableFromAsyncAttr(cMessage.unbridged(), cAtLoc.unbridged(),
cRange.unbridged(), /*implicit=*/false);
}

static ExecutionKind unbridged(BridgedExecutionKind kind) {
switch (kind) {
case BridgedExecutionKindConcurrent:
return ExecutionKind::Concurrent;
case BridgedExecutionKindCaller:
return ExecutionKind::Caller;
}
llvm_unreachable("unhandled enum value");
}

BridgedExecutionAttr BridgedExecutionAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc atLoc,
BridgedSourceRange range, BridgedExecutionKind behavior) {
return new (cContext.unbridged())
ExecutionAttr(atLoc.unbridged(), range.unbridged(),
unbridged(behavior), /*implicit=*/false);
}
29 changes: 29 additions & 0 deletions lib/ASTGen/Sources/ASTGen/DeclAttrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ extension ASTGenVisitor {
let attrName = identTy.name.rawText
let attrKind = BridgedDeclAttrKind(from: attrName.bridged)
switch attrKind {
case .execution:
return handle(self.generateExecutionAttr(attribute: node)?.asDeclAttribute)
case .ABI:
return handle(self.generateABIAttr(attribute: node)?.asDeclAttribute)
case .alignment:
Expand Down Expand Up @@ -329,6 +331,33 @@ extension ASTGenVisitor {
return handle(self.generateCustomAttr(attribute: node)?.asDeclAttribute)
}

/// E.g.:
/// ```
/// @execution(concurrent)
/// @execution(caller)
/// ```
func generateExecutionAttr(attribute node: AttributeSyntax) -> BridgedExecutionAttr? {
let behavior: BridgedExecutionKind? = self.generateSingleAttrOption(
attribute: node,
{
switch $0.rawText {
case "concurrent": return .concurrent
case "caller": return .caller
default: return nil
}
}
)
guard let behavior else {
return nil
}
return .createParsed(
self.ctx,
atLoc: self.generateSourceLoc(node.atSign),
range: self.generateAttrSourceRange(node),
behavior: behavior
)
}

/// E.g.:
/// ```
/// @abi(func fn())
Expand Down
19 changes: 18 additions & 1 deletion lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2706,6 +2706,8 @@ ParserResult<LifetimeAttr> Parser::parseLifetimeAttribute(SourceLoc atLoc,
/* implicit */ false, lifetimeEntry.get()));
}



/// Parses a (possibly optional) argument for an attribute containing a single, arbitrary identifier.
///
/// \param P The parser object.
Expand Down Expand Up @@ -4022,6 +4024,21 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
Attributes.add(Attr.get());
break;
}

case DeclAttrKind::Execution: {
auto behavior = parseSingleAttrOption<ExecutionKind>(
*this, Loc, AttrRange, AttrName, DK,
{{Context.Id_concurrent, ExecutionKind::Concurrent},
{Context.Id_caller, ExecutionKind::Caller}});
if (!behavior)
return makeParserSuccess();

if (!DiscardAttribute)
Attributes.add(new (Context) ExecutionAttr(AtLoc, AttrRange, *behavior,
/*Implicit*/ false));

break;
}
}

if (DuplicateAttribute) {
Expand Down Expand Up @@ -9729,7 +9746,7 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc,
}
}
}

ParserStatus Status;
SourceLoc SubscriptLoc = consumeToken(tok::kw_subscript);

Expand Down
75 changes: 75 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,81 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
}

public:
void visitExecutionAttr(ExecutionAttr *attr) {
auto *F = dyn_cast<FuncDecl>(D);
if (!F)
return;

if (!F->hasAsync()) {
diagnoseAndRemoveAttr(attr, diag::attr_execution_concurrent_only_on_async,
F);
return;
}

switch (attr->getBehavior()) {
case ExecutionKind::Concurrent: {
// 'concurrent' doesn't work with explicit `nonisolated`
if (F->hasExplicitIsolationAttribute()) {
if (F->getAttrs().hasAttribute<NonisolatedAttr>()) {
diagnoseAndRemoveAttr(
attr,
diag::attr_execution_concurrent_incompatible_with_nonisolated, F);
return;
}
}

auto parameters = F->getParameters();
if (!parameters)
return;

for (auto *P : *parameters) {
auto *repr = P->getTypeRepr();
if (!repr)
continue;

// isolated parameters affect isolation of the function itself
if (isa<IsolatedTypeRepr>(repr)) {
diagnoseAndRemoveAttr(
attr,
diag::attr_execution_concurrent_incompatible_isolated_parameter,
F, P);
return;
}

if (auto *attrType = dyn_cast<AttributedTypeRepr>(repr)) {
if (attrType->has(TypeAttrKind::Isolated)) {
diagnoseAndRemoveAttr(
attr,
diag::
attr_execution_concurrent_incompatible_dynamically_isolated_parameter,
F, P);
return;
}
}
}

// We need isolation check here because global actor isolation
// could be inferred.

auto isolation = getActorIsolation(F);
if (isolation.isGlobalActor()) {
diagnoseAndRemoveAttr(
attr,
diag::attr_execution_concurrent_incompatible_with_global_actor, F,
isolation.getGlobalActor());
return;
}

break;
}

case ExecutionKind::Caller: {
// no restrictions for now.
break;
}
}
}

void visitABIAttr(ABIAttr *attr) {
Decl *AD = attr->abiDecl;
if (isa<VarDecl>(D) && isa<PatternBindingDecl>(AD)) {
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,7 @@ namespace {
UNINTERESTING_ATTR(DynamicCallable)
UNINTERESTING_ATTR(DynamicMemberLookup)
UNINTERESTING_ATTR(SILGenName)
UNINTERESTING_ATTR(Execution)
UNINTERESTING_ATTR(Exported)
UNINTERESTING_ATTR(ForbidSerializingReference)
UNINTERESTING_ATTR(GKInspectable)
Expand Down
Loading