Skip to content

Commit f0446d0

Browse files
authored
Merge pull request #79729 from hborla/concurrency-sugar
[Concurrency] Add an experimental macro for wrapping a function body in a new task.
2 parents c91e295 + 8be41ed commit f0446d0

File tree

8 files changed

+101
-0
lines changed

8 files changed

+101
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7810,6 +7810,10 @@ ERROR(conformance_macro,none,
78107810
"conformance macros are replaced by extension macros",
78117811
())
78127812

7813+
ERROR(experimental_macro,none,
7814+
"macro %0 is experimental",
7815+
(DeclName))
7816+
78137817
ERROR(macro_resolve_circular_reference, none,
78147818
"circular reference resolving %select{freestanding|attached}0 macro %1",
78157819
(bool, DeclName))

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,9 @@ EXPERIMENTAL_FEATURE(ExtensibleEnums, true)
473473
/// Allow isolated conformances.
474474
EXPERIMENTAL_FEATURE(IsolatedConformances, true)
475475

476+
/// Syntax sugar features for concurrency.
477+
EXPERIMENTAL_FEATURE(ConcurrencySyntaxSugar, true)
478+
476479
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
477480
#undef EXPERIMENTAL_FEATURE
478481
#undef UPCOMING_FEATURE

lib/AST/FeatureSet.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ static bool usesFeatureIsolatedConformances(Decl *decl) {
350350
return false;
351351
}
352352

353+
static bool usesFeatureConcurrencySyntaxSugar(Decl *decl) {
354+
return false;
355+
}
356+
353357
static bool usesFeatureMemorySafetyAttributes(Decl *decl) {
354358
if (decl->getAttrs().hasAttribute<SafeAttr>() ||
355359
decl->getAttrs().hasAttribute<UnsafeAttr>())

lib/Macros/Sources/SwiftMacros/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_swift_macro_library(SwiftMacros
1414
OptionSetMacro.swift
1515
DebugDescriptionMacro.swift
1616
DistributedResolvableMacro.swift
17+
StartTaskMacro.swift
1718
SyntaxExtensions.swift
1819
TaskLocalMacro.swift
1920
SwiftifyImportMacro.swift
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftDiagnostics
14+
import SwiftParser
15+
import SwiftSyntax
16+
import SwiftSyntaxBuilder
17+
import SwiftSyntaxMacros
18+
19+
struct TaskMacroDiagnostic: DiagnosticMessage {
20+
static func diagnose(at node: some SyntaxProtocol) -> Diagnostic {
21+
Diagnostic(node: Syntax(node), message: Self.init())
22+
}
23+
24+
var message: String {
25+
"'@StartTask' macro can only be used on functions with an implementation"
26+
}
27+
28+
var severity: DiagnosticSeverity { .error }
29+
30+
var diagnosticID: MessageID {
31+
MessageID(domain: "_Concurrency", id: "StartMacro.\(self)")
32+
}
33+
}
34+
35+
36+
public struct StartTaskMacro: BodyMacro {
37+
public static func expansion(
38+
of node: AttributeSyntax,
39+
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
40+
in context: some MacroExpansionContext
41+
) throws -> [CodeBlockItemSyntax] {
42+
guard let taskBody = declaration.body else {
43+
context.diagnose(TaskMacroDiagnostic.diagnose(at: node))
44+
return []
45+
}
46+
47+
return [
48+
"""
49+
Task \(taskBody)
50+
"""
51+
]
52+
}
53+
}

lib/Sema/TypeCheckMacros.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,19 @@ ExpandBodyMacroRequest::evaluate(Evaluator &evaluator,
17351735
if (bufferID)
17361736
return;
17371737

1738+
// '@StartTask' is gated behind the 'ConcurrencySyntaxSugar'
1739+
// experimental feature.
1740+
auto &ctx = fn->getASTContext();
1741+
if (macro->getParentModule()->getName().is("_Concurrency") &&
1742+
macro->getBaseIdentifier().is("StartTask") &&
1743+
!ctx.LangOpts.hasFeature(Feature::ConcurrencySyntaxSugar)) {
1744+
ctx.Diags.diagnose(
1745+
customAttr->getLocation(),
1746+
diag::experimental_macro,
1747+
macro->getName());
1748+
return;
1749+
}
1750+
17381751
auto macroSourceFile = ::evaluateAttachedMacro(
17391752
macro, fn, customAttr, false, MacroRole::Body);
17401753
if (!macroSourceFile)

stdlib/public/Concurrency/Actor.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ internal func _enqueueOnMain(_ job: UnownedJob)
9898
@available(SwiftStdlib 5.1, *)
9999
@freestanding(expression)
100100
public macro isolation<T>() -> T = Builtin.IsolationMacro
101+
102+
@available(SwiftStdlib 5.1, *)
103+
@attached(body)
104+
public macro StartTask() =
105+
#externalMacro(module: "SwiftMacros", type: "StartTaskMacro")
101106
#endif
102107

103108
#if $IsolatedAny

test/Macros/start_task.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// REQUIRES: swift_swift_parser, swift_feature_ConcurrencySyntaxSugar
2+
3+
// RUN: %target-swift-frontend -typecheck -plugin-path %swift-plugin-dir -enable-experimental-feature ConcurrencySyntaxSugar -language-mode 6 %s -dump-macro-expansions 2>&1 | %FileCheck %s
4+
5+
func f() async {}
6+
7+
// CHECK-LABEL: @__swiftmacro_10start_task4sync9StartTaskfMb_.swift
8+
// CHECK: Task {
9+
// CHECK: await f()
10+
// CHECK: }
11+
12+
@StartTask
13+
func sync() {
14+
await f()
15+
}
16+
17+
18+

0 commit comments

Comments
 (0)