diff --git a/lib/Macros/Sources/ObservationMacros/ObservableMacro.swift b/lib/Macros/Sources/ObservationMacros/ObservableMacro.swift index be1bf85a327e0..60708957d43f7 100644 --- a/lib/Macros/Sources/ObservationMacros/ObservableMacro.swift +++ b/lib/Macros/Sources/ObservationMacros/ObservableMacro.swift @@ -202,17 +202,15 @@ extension ObservableMacro: MemberMacro { declaration.addIfNeeded(ObservableMacro.registrarVariable(observableType), to: &declarations) declaration.addIfNeeded(ObservableMacro.accessFunction(observableType), to: &declarations) declaration.addIfNeeded(ObservableMacro.withMutationFunction(observableType), to: &declarations) - + +#if !OBSERVATION_SUPPORTS_PEER_MACROS let storedInstanceVariables = declaration.definedVariables.filter { $0.isValidForObservation } for property in storedInstanceVariables { - if property.hasMacroApplication(ObservableMacro.ignoredMacroName) { continue } - if property.initializer == nil { - context.addDiagnostics(from: DiagnosticsError(syntax: property, message: "@Observable requires property '\(property.identifier?.text ?? "")' to have an initial value", id: .missingInitializer), node: property) - } - let storage = DeclSyntax(property.privatePrefixed("_", addingAttribute: ObservableMacro.ignoredAttribute)) - declaration.addIfNeeded(storage, to: &declarations) - + if property.hasMacroApplication(ObservableMacro.ignoredMacroName) { continue } + let storage = DeclSyntax(property.privatePrefixed("_", addingAttribute: ObservableMacro.ignoredAttribute)) + declaration.addIfNeeded(storage, to: &declarations) } +#endif return declarations } @@ -293,6 +291,13 @@ public struct ObservationTrackedMacro: AccessorMacro { return [] } + let initAccessor: AccessorDeclSyntax = + """ + init(initialValue) initializes(_\(identifier)) { + _\(identifier) = initialValue + } + """ + let getAccessor: AccessorDeclSyntax = """ get { @@ -310,7 +315,7 @@ public struct ObservationTrackedMacro: AccessorMacro { } """ - return [getAccessor, setAccessor] + return [initAccessor, getAccessor, setAccessor] } } @@ -327,8 +332,9 @@ extension ObservationTrackedMacro: PeerMacro { property.isValidForObservation else { return [] } - - if property.hasMacroApplication(ObservableMacro.ignoredMacroName) { + + if property.hasMacroApplication(ObservableMacro.ignoredMacroName) || + property.hasMacroApplication(ObservableMacro.trackedMacroName) { return [] } diff --git a/stdlib/public/Observation/Sources/Observation/Observable.swift b/stdlib/public/Observation/Sources/Observation/Observable.swift index 19a4012f93a1d..a2f837af85db3 100644 --- a/stdlib/public/Observation/Sources/Observation/Observable.swift +++ b/stdlib/public/Observation/Sources/Observation/Observable.swift @@ -16,7 +16,11 @@ #if $Macros && hasAttribute(attached) @available(SwiftStdlib 5.9, *) +#if OBSERVATION_SUPPORTS_PEER_MACROS +@attached(member, names: named(_$observationRegistrar), named(access), named(withMutation)) +#else @attached(member, names: named(_$observationRegistrar), named(access), named(withMutation), arbitrary) +#endif @attached(memberAttribute) @attached(conformance) public macro Observable() = @@ -24,7 +28,9 @@ public macro Observable() = @available(SwiftStdlib 5.9, *) @attached(accessor) -// @attached(peer, names: prefixed(_)) +#if OBSERVATION_SUPPORTS_PEER_MACROS +@attached(peer, names: prefixed(_)) +#endif public macro ObservationTracked() = #externalMacro(module: "ObservationMacros", type: "ObservationTrackedMacro") diff --git a/test/stdlib/Observation/Observable.swift b/test/stdlib/Observation/Observable.swift index 886bbf15a19de..1fef61087f849 100644 --- a/test/stdlib/Observation/Observable.swift +++ b/test/stdlib/Observation/Observable.swift @@ -1,6 +1,6 @@ // REQUIRES: swift_swift_parser, executable_test -// 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) +// 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) // REQUIRES: observation // REQUIRES: concurrency @@ -23,6 +23,24 @@ struct Structure { var field: Int = 0 } +@Observable +struct MemberwiseInitializers { + var field: Int +} + +func validateMemberwiseInitializers() { + _ = MemberwiseInitializers(field: 3) +} + +@Observable +struct DefiniteInitialization { + var field: Int + + init(field: Int) { + self.field = field + } +} + @Observable class ContainsWeak { weak var obj: AnyObject? = nil