Skip to content

[TaskLocal] Carry access control modifiers into synthesized property #73475

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 3 commits into from
May 7, 2024
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
1 change: 1 addition & 0 deletions lib/Macros/Sources/SwiftMacros/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_swift_macro_library(SwiftMacros
OptionSetMacro.swift
DebugDescriptionMacro.swift
DistributedResolvableMacro.swift
SyntaxExtensions.swift
TaskLocalMacro.swift
SWIFT_DEPENDENCIES
SwiftDiagnostics
Expand Down
29 changes: 7 additions & 22 deletions lib/Macros/Sources/SwiftMacros/DistributedResolvableMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension DistributedResolvableMacro {
return []
}

let accessModifiers: String = proto.accessModifiersString
let accessModifiers = proto.accessControlModifiers

let requirementStubs =
proto.memberBlock.members // requirements
Expand All @@ -71,7 +71,7 @@ extension DistributedResolvableMacro {
return [extensionDecl.cast(ExtensionDeclSyntax.self)]
}

static func stubMethodDecl(access: String, _ requirement: MemberBlockItemListSyntax.Element) -> String {
static func stubMethodDecl(access: DeclModifierListSyntax, _ requirement: MemberBlockItemListSyntax.Element) -> String {
// do we need to stub a computed variable?
if let variable = requirement.decl.as(VariableDeclSyntax.self) {
var accessorStubs: [String] = []
Expand Down Expand Up @@ -142,7 +142,7 @@ extension DistributedResolvableMacro {
""", id: .invalidApplication)
}

let accessModifiers = proto.accessModifiersString
let accessModifiers = proto.accessControlModifiers

for req in proto.genericWhereClause?.requirements ?? [] {
switch req.requirement {
Expand Down Expand Up @@ -198,31 +198,16 @@ extension DistributedResolvableMacro {
}
}

private static func typealiasActorSystem(access: String, _ proto: ProtocolDeclSyntax, _ type: TypeSyntax) -> DeclSyntax {
"\(raw: access)typealias ActorSystem = \(type)"
private static func typealiasActorSystem(access: DeclModifierListSyntax,
_ proto: ProtocolDeclSyntax,
_ type: TypeSyntax) -> DeclSyntax {
"\(access)typealias ActorSystem = \(type)"
}
}

// ===== -----------------------------------------------------------------------
// MARK: Convenience Extensions

extension ProtocolDeclSyntax {
var accessModifiersString: String {
let modifiers = modifiers.filter { modifier in
modifier.isAccessControl
}

guard !modifiers.isEmpty else {
return ""
}

let string = modifiers
.map { "\($0.trimmed)" }
.joined(separator: " ")
return "\(string) "
}
}

extension TypeSyntax {
fileprivate var isActorSystem: Bool {
self.trimmedDescription == "ActorSystem"
Expand Down
33 changes: 33 additions & 0 deletions lib/Macros/Sources/SwiftMacros/SyntaxExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022-2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// Common Syntax extensions used by standard library macros. //
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxMacros
import SwiftDiagnostics

extension DeclGroupSyntax {
internal var accessControlModifiers: DeclModifierListSyntax {
modifiers.filter { modifier in
modifier.isAccessControl
}
}
}

extension VariableDeclSyntax {
internal var accessControlModifiers: DeclModifierListSyntax {
modifiers.filter { modifier in
modifier.isAccessControl
}
}
}
5 changes: 4 additions & 1 deletion lib/Macros/Sources/SwiftMacros/TaskLocalMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ extension TaskLocalMacro: PeerMacro {
message: "'@TaskLocal' property must have default value, or be optional", id: .mustBeVar)
}

// Copy access modifiers
let access = varDecl.accessControlModifiers

// If the property is global, do not prefix the synthesised decl with 'static'
let isGlobal = context.lexicalContext.isEmpty
let staticKeyword: TokenSyntax?
Expand All @@ -80,7 +83,7 @@ extension TaskLocalMacro: PeerMacro {

return [
"""
\(staticKeyword)let $\(name)\(explicitTypeAnnotation) = TaskLocal(wrappedValue: \(initialValue))
\(access)\(staticKeyword)let $\(name)\(explicitTypeAnnotation) = TaskLocal(wrappedValue: \(initialValue))
"""
]
}
Expand Down
39 changes: 39 additions & 0 deletions test/Concurrency/Macros/task_local_macro_expansion.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// REQUIRES: swift_swift_parser, asserts
//
// UNSUPPORTED: back_deploy_concurrency
// REQUIRES: concurrency
//
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t-scratch)

// RUN: %target-swift-frontend -disable-availability-checking -typecheck -verify -plugin-path %swift-plugin-dir -I %t -dump-macro-expansions %s -dump-macro-expansions 2>&1 | %FileCheck %s

struct Nein {
@TaskLocal
public static var example: String = "hello"
}

// CHECK: public static let $example: TaskLocal<String> = TaskLocal(wrappedValue: "hello")
//
// CHECK: {
// CHECK: get {
// CHECK: $example.get()
// CHECK: }
// CHECK: }

struct Available {
@available(OSX 10.9, *)
struct AvailableValue {}

@TaskLocal
@available(OSX 10.9, *)
private static var example: AvailableValue?
}

// CHECK: private static let $example: TaskLocal<AvailableValue?> = TaskLocal(wrappedValue: nil)
//
// CHECK: {
// CHECK: get {
// CHECK: $example.get()
// CHECK: }
// CHECK: }
10 changes: 9 additions & 1 deletion test/Concurrency/Runtime/async_task_locals_basic.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-run-simple-swift( -plugin-path %swift-plugin-dir -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s
// RUN: %target-run-simple-swift( -plugin-path %swift-plugin-dir -parse-as-library %import-libdispatch) | %FileCheck %s

// REQUIRES: executable_test
// REQUIRES: concurrency
Expand Down Expand Up @@ -37,8 +37,16 @@ enum TL {
}

@TaskLocal
@available(SwiftStdlib 5.1, *)
var globalTaskLocal: StringLike = StringLike("<not-set>")

@available(SwiftStdlib 5.10, *)
struct LessAvailable {}

@TaskLocal
@available(SwiftStdlib 5.10, *)
var globalLessAvailable: LessAvailable?

@available(SwiftStdlib 5.1, *)
final class ClassTaskLocal: Sendable {
init() {
Expand Down