Skip to content

Commit 1c522b0

Browse files
committed
[TaskLocal] TL macro must handle force unwrapped optional types
Resolves rdar://128225191
1 parent ffba38d commit 1c522b0

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

lib/Macros/Sources/SwiftMacros/SyntaxExtensions.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,17 @@ extension VariableDeclSyntax {
3030
modifier.isAccessControl
3131
}
3232
}
33+
}
34+
35+
extension ImplicitlyUnwrappedOptionalTypeSyntax {
36+
internal var asOptionalTypeSyntax: some TypeSyntaxProtocol {
37+
OptionalTypeSyntax(
38+
leadingTrivia: leadingTrivia,
39+
unexpectedBeforeWrappedType,
40+
wrappedType: wrappedType,
41+
self.unexpectedBetweenWrappedTypeAndExclamationMark,
42+
self.unexpectedAfterExclamationMark,
43+
trailingTrivia: self.trailingTrivia
44+
)
45+
}
3346
}

lib/Macros/Sources/SwiftMacros/TaskLocalMacro.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,24 @@ extension TaskLocalMacro: PeerMacro {
5050
message: "'@TaskLocal' property must have name", id: .incompatibleDecl)
5151
}
5252

53-
let type = firstBinding.typeAnnotation?.type
53+
let originalType = firstBinding.typeAnnotation?.type
5454
let explicitTypeAnnotation: TypeAnnotationSyntax?
55-
if let type {
56-
explicitTypeAnnotation = TypeAnnotationSyntax(type: TypeSyntax("TaskLocal<\(type.trimmed)>"))
55+
let taskLocalWrappedType: TypeSyntax?
56+
if let forceUnwrappedOptionalType = firstBinding.typeAnnotation?.type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
57+
taskLocalWrappedType = TypeSyntax("\(forceUnwrappedOptionalType.wrappedType.trimmed)?")
58+
} else {
59+
taskLocalWrappedType = originalType?.trimmed
60+
}
61+
if let taskLocalWrappedType {
62+
explicitTypeAnnotation = TypeAnnotationSyntax(type: TypeSyntax("TaskLocal<\(taskLocalWrappedType)>"))
5763
} else {
5864
explicitTypeAnnotation = nil
5965
}
6066

6167
let initialValue: ExprSyntax
6268
if let initializerValue = firstBinding.initializer?.value {
6369
initialValue = ExprSyntax(initializerValue)
64-
} else if let type, type.isOptional {
70+
} else if let originalType, originalType.isOptional {
6571
initialValue = ExprSyntax(NilLiteralExprSyntax())
6672
} else {
6773
throw DiagnosticsError(
@@ -162,7 +168,7 @@ extension TypeSyntax {
162168
// and T? we can detect the optional.
163169
fileprivate var isOptional: Bool {
164170
switch self.as(TypeSyntaxEnum.self) {
165-
case .optionalType:
171+
case .optionalType, .implicitlyUnwrappedOptionalType:
166172
return true
167173
case .identifierType(let identifierType):
168174
return identifierType.name.text == "Optional"
@@ -172,7 +178,8 @@ extension TypeSyntax {
172178
return false
173179
}
174180
return memberType.name.text == "Optional"
175-
default: return false
181+
default:
182+
return false
176183
}
177184
}
178185
}

test/Concurrency/Runtime/async_task_locals_basic.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ enum TL {
3434

3535
@TaskLocal
3636
static var clazz: ClassTaskLocal?
37+
38+
@TaskLocal
39+
static var force: ForceUnwrapMe!
3740
}
3841

3942
@TaskLocal
@@ -43,6 +46,8 @@ var globalTaskLocal: StringLike = StringLike("<not-set>")
4346
@available(SwiftStdlib 5.10, *)
4447
struct LessAvailable {}
4548

49+
struct ForceUnwrapMe {}
50+
4651
@TaskLocal
4752
@available(SwiftStdlib 5.10, *)
4853
var globalLessAvailable: LessAvailable?

0 commit comments

Comments
 (0)