Skip to content

Commit 4d1d8a9

Browse files
authored
[Observation] Add property definite initialization support (#65984)
* [Observation] Transition to peer macros instead of arbitrary members * [Observation] Lift the initializer requirement by utilizing init accessors for fully formed definite initialization * [Observation] Gate enabling of peer macros by flag * [Observation] Enable feature for InitAccessors in the observation tests * [Observation] Add tests to validate memberwise and definite initialization
1 parent e3d940e commit 4d1d8a9

File tree

3 files changed

+43
-13
lines changed

3 files changed

+43
-13
lines changed

lib/Macros/Sources/ObservationMacros/ObservableMacro.swift

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,15 @@ extension ObservableMacro: MemberMacro {
202202
declaration.addIfNeeded(ObservableMacro.registrarVariable(observableType), to: &declarations)
203203
declaration.addIfNeeded(ObservableMacro.accessFunction(observableType), to: &declarations)
204204
declaration.addIfNeeded(ObservableMacro.withMutationFunction(observableType), to: &declarations)
205-
205+
206+
#if !OBSERVATION_SUPPORTS_PEER_MACROS
206207
let storedInstanceVariables = declaration.definedVariables.filter { $0.isValidForObservation }
207208
for property in storedInstanceVariables {
208-
if property.hasMacroApplication(ObservableMacro.ignoredMacroName) { continue }
209-
if property.initializer == nil {
210-
context.addDiagnostics(from: DiagnosticsError(syntax: property, message: "@Observable requires property '\(property.identifier?.text ?? "")' to have an initial value", id: .missingInitializer), node: property)
211-
}
212-
let storage = DeclSyntax(property.privatePrefixed("_", addingAttribute: ObservableMacro.ignoredAttribute))
213-
declaration.addIfNeeded(storage, to: &declarations)
214-
209+
if property.hasMacroApplication(ObservableMacro.ignoredMacroName) { continue }
210+
let storage = DeclSyntax(property.privatePrefixed("_", addingAttribute: ObservableMacro.ignoredAttribute))
211+
declaration.addIfNeeded(storage, to: &declarations)
215212
}
213+
#endif
216214

217215
return declarations
218216
}
@@ -293,6 +291,13 @@ public struct ObservationTrackedMacro: AccessorMacro {
293291
return []
294292
}
295293

294+
let initAccessor: AccessorDeclSyntax =
295+
"""
296+
init(initialValue) initializes(_\(identifier)) {
297+
_\(identifier) = initialValue
298+
}
299+
"""
300+
296301
let getAccessor: AccessorDeclSyntax =
297302
"""
298303
get {
@@ -310,7 +315,7 @@ public struct ObservationTrackedMacro: AccessorMacro {
310315
}
311316
"""
312317

313-
return [getAccessor, setAccessor]
318+
return [initAccessor, getAccessor, setAccessor]
314319
}
315320
}
316321

@@ -327,8 +332,9 @@ extension ObservationTrackedMacro: PeerMacro {
327332
property.isValidForObservation else {
328333
return []
329334
}
330-
331-
if property.hasMacroApplication(ObservableMacro.ignoredMacroName) {
335+
336+
if property.hasMacroApplication(ObservableMacro.ignoredMacroName) ||
337+
property.hasMacroApplication(ObservableMacro.trackedMacroName) {
332338
return []
333339
}
334340

stdlib/public/Observation/Sources/Observation/Observable.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@
1616
#if $Macros && hasAttribute(attached)
1717

1818
@available(SwiftStdlib 5.9, *)
19+
#if OBSERVATION_SUPPORTS_PEER_MACROS
20+
@attached(member, names: named(_$observationRegistrar), named(access), named(withMutation))
21+
#else
1922
@attached(member, names: named(_$observationRegistrar), named(access), named(withMutation), arbitrary)
23+
#endif
2024
@attached(memberAttribute)
2125
@attached(conformance)
2226
public macro Observable() =
2327
#externalMacro(module: "ObservationMacros", type: "ObservableMacro")
2428

2529
@available(SwiftStdlib 5.9, *)
2630
@attached(accessor)
27-
// @attached(peer, names: prefixed(_))
31+
#if OBSERVATION_SUPPORTS_PEER_MACROS
32+
@attached(peer, names: prefixed(_))
33+
#endif
2834
public macro ObservationTracked() =
2935
#externalMacro(module: "ObservationMacros", type: "ObservationTrackedMacro")
3036

test/stdlib/Observation/Observable.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// REQUIRES: swift_swift_parser, executable_test
22

3-
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library -enable-experimental-feature Macros -Xfrontend -plugin-path -Xfrontend %swift-host-lib-dir/plugins)
3+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -Xfrontend -plugin-path -Xfrontend %swift-host-lib-dir/plugins)
44

55
// REQUIRES: observation
66
// REQUIRES: concurrency
@@ -23,6 +23,24 @@ struct Structure {
2323
var field: Int = 0
2424
}
2525

26+
@Observable
27+
struct MemberwiseInitializers {
28+
var field: Int
29+
}
30+
31+
func validateMemberwiseInitializers() {
32+
_ = MemberwiseInitializers(field: 3)
33+
}
34+
35+
@Observable
36+
struct DefiniteInitialization {
37+
var field: Int
38+
39+
init(field: Int) {
40+
self.field = field
41+
}
42+
}
43+
2644
@Observable
2745
class ContainsWeak {
2846
weak var obj: AnyObject? = nil

0 commit comments

Comments
 (0)