From 093b8d32db265147c53da6d399ce217f0bcb629f Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Mon, 31 Jan 2022 15:57:11 -0800 Subject: [PATCH 1/2] [Sema] Emit an error for attempts to use `@available` on var declarations that have attached property wrappers. Availability is not supported on stored properties, and properties with property wrappers are stored properties with a layer of indirection. Also, add the same missing diagnostic for `lazy` vars. Resolves rdar://82713248 --- include/swift/AST/Decl.h | 4 ++++ lib/AST/Decl.cpp | 14 ++++++++++++ lib/Sema/CodeSynthesis.cpp | 5 +---- lib/Sema/TypeCheckAttr.cpp | 2 +- test/Sema/availability_stored.swift | 26 ++++++++++++++++++++++ test/decl/class/effectful_properties.swift | 1 + 6 files changed, 47 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 9ceb3b32ff057..fe80ea05cf06b 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5390,6 +5390,10 @@ class VarDecl : public AbstractStorageDecl { /// an attached property wrapper. VarDecl *getPropertyWrapperWrappedValueVar() const; + /// Return true if this property either has storage or has an attached property + /// wrapper that has storage. + bool hasStorageOrWrapsStorage() const; + /// Visit all auxiliary declarations to this VarDecl. /// /// An auxiliary declaration is a declaration synthesized by the compiler to support diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4f737af56b54f..47e57cb4d242e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6446,6 +6446,20 @@ VarDecl *VarDecl::getPropertyWrapperWrappedValueVar() const { return getPropertyWrapperAuxiliaryVariables().localWrappedValueVar; } +bool VarDecl::hasStorageOrWrapsStorage() const { + if (hasStorage()) + return true; + + if (getAttrs().hasAttribute()) + return true; + + auto *backing = getPropertyWrapperBackingProperty(); + if (backing && backing->hasStorage()) + return true; + + return false; +} + void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) const { if (getDeclContext()->isTypeContext() || isImplicit()) return; diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 952619472a472..c3d0e93567c5d 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -928,10 +928,7 @@ bool AreAllStoredPropertiesDefaultInitableRequest::evaluate( if (VD->getAttrs().hasAttribute()) CheckDefaultInitializer = false; - if (VD->hasStorage()) - HasStorage = true; - auto *backing = VD->getPropertyWrapperBackingProperty(); - if (backing && backing->hasStorage()) + if (VD->hasStorageOrWrapsStorage()) HasStorage = true; }); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 8d0b9be104471..dc61de7b1f139 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3562,7 +3562,7 @@ TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) { auto *DC = D->getDeclContext(); if (auto *VD = dyn_cast(D)) { - if (!VD->hasStorage()) + if (!VD->hasStorageOrWrapsStorage()) return None; // Do not permit potential availability of script-mode global variables; diff --git a/test/Sema/availability_stored.swift b/test/Sema/availability_stored.swift index 4835470fd2478..da5d0bad06232 100644 --- a/test/Sema/availability_stored.swift +++ b/test/Sema/availability_stored.swift @@ -8,15 +8,25 @@ @available(macOS 50, *) struct NewStruct {} +@available(macOS 50, *) +@propertyWrapper +struct NewPropertyWrapper { + var wrappedValue: Value +} + @available(macOS 50, *) struct GoodReferenceStruct { var x: NewStruct + @NewPropertyWrapper var y: Int + lazy var z: Int = 42 } @available(macOS 50, *) struct GoodNestedReferenceStruct { struct Inner { var x: NewStruct + @NewPropertyWrapper var y: Int + lazy var z: Int = 42 } } @@ -24,6 +34,14 @@ struct BadReferenceStruct1 { // expected-error@+1 {{stored properties cannot be marked potentially unavailable with '@available'}} @available(macOS 50, *) var x: NewStruct + + // expected-error@+1 {{stored properties cannot be marked potentially unavailable with '@available'}} + @available(macOS 50, *) + @NewPropertyWrapper var y: Int + + // expected-error@+1 {{stored properties cannot be marked potentially unavailable with '@available'}} + @available(macOS 50, *) + lazy var z: Int = 42 } @available(macOS 40, *) @@ -31,6 +49,14 @@ struct BadReferenceStruct2 { // expected-error@+1 {{stored properties cannot be marked potentially unavailable with '@available'}} @available(macOS 50, *) var x: NewStruct + + // expected-error@+1 {{stored properties cannot be marked potentially unavailable with '@available'}} + @available(macOS 50, *) + @NewPropertyWrapper var y: Int + + // expected-error@+1 {{stored properties cannot be marked potentially unavailable with '@available'}} + @available(macOS 50, *) + lazy var z: Int = 42 } // The same behavior should hold for enum elements with payloads. diff --git a/test/decl/class/effectful_properties.swift b/test/decl/class/effectful_properties.swift index 3ecaeebacbea7..8e40e9b6193e1 100644 --- a/test/decl/class/effectful_properties.swift +++ b/test/decl/class/effectful_properties.swift @@ -86,6 +86,7 @@ class AcceptableDynamic { } // mainly just some sanity checks +// expected-error@+1 {{class 'Misc' has no initializers}} class Misc { // expected-error@+2 {{'lazy' cannot be used on a computed property}} // expected-error@+1 {{lazy properties must have an initializer}} From e34196df0b3c35fbd5688ab3ef86e1beed928f7a Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 4 Feb 2022 10:28:11 -0800 Subject: [PATCH 2/2] Fix tests that depended on availability for lazy vars. --- test/Sema/availability_versions.swift | 20 ++------------------ test/stdlib/CodableTests.swift | 18 +++++++++--------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index 70834960c8a53..3399be7291795 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -324,7 +324,7 @@ class ClassWithUnavailableProperties { @available(OSX, introduced: 10.9) lazy var availableOn10_9Stored: Int = 9 - @available(OSX, introduced: 10.51) + @available(OSX, introduced: 10.51) // expected-error {{stored properties cannot be marked potentially unavailable with '@available'}} lazy var availableOn10_51Stored : Int = 10 @available(OSX, introduced: 10.9) @@ -418,7 +418,6 @@ class ClassWithReferencesInInitializers { lazy var lazyPropWithInitializer10_51: Int = globalFuncAvailableOn10_51() lazy var lazyPropWithInitializer10_52: Int = globalFuncAvailableOn10_52() // expected-error {{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}} - // expected-note@-1 {{add @available attribute to enclosing property}} } func accessUnavailableProperties(_ o: ClassWithUnavailableProperties) { @@ -716,21 +715,9 @@ class ClassWithDeclarationsOfUnavailableClasses { // expected-note@-1 5{{add @available attribute to enclosing class}} @available(OSX, introduced: 10.51) - init() { - unavailablePropertyOfUnavailableType = ClassAvailableOn10_51() - unavailablePropertyOfUnavailableType = ClassAvailableOn10_51() - } + init() {} var propertyOfUnavailableType: ClassAvailableOn10_51 // expected-error {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}} - - @available(OSX, introduced: 10.51) - lazy var unavailablePropertyOfUnavailableType: ClassAvailableOn10_51 = ClassAvailableOn10_51() - - @available(OSX, introduced: 10.51) - lazy var unavailablePropertyOfOptionalUnavailableType: ClassAvailableOn10_51? = nil - - @available(OSX, introduced: 10.51) - lazy var unavailablePropertyOfUnavailableTypeWithInitializer: ClassAvailableOn10_51 = ClassAvailableOn10_51() @available(OSX, introduced: 10.51) static var unavailableStaticPropertyOfUnavailableType: ClassAvailableOn10_51 = ClassAvailableOn10_51() @@ -1294,15 +1281,12 @@ class ClassForFixit { lazy var fixitForReferenceInLazyPropertyType: ClassAvailableOn10_51? = nil // expected-error@-1 {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}} - // expected-note@-2 {{add @available attribute to enclosing property}} {{3-3=@available(macOS 10.51, *)\n }} private lazy var fixitForReferenceInPrivateLazyPropertyType: ClassAvailableOn10_51? = nil // expected-error@-1 {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}} - // expected-note@-2 {{add @available attribute to enclosing property}} {{3-3=@available(macOS 10.51, *)\n }} lazy private var fixitForReferenceInLazyPrivatePropertyType: ClassAvailableOn10_51? = nil // expected-error@-1 {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}} - // expected-note@-2 {{add @available attribute to enclosing property}} {{3-3=@available(macOS 10.51, *)\n }} static var fixitForReferenceInStaticPropertyType: ClassAvailableOn10_51? = nil // expected-error@-1 {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}} diff --git a/test/stdlib/CodableTests.swift b/test/stdlib/CodableTests.swift index a8da6edcc097f..89cacefbbaac7 100644 --- a/test/stdlib/CodableTests.swift +++ b/test/stdlib/CodableTests.swift @@ -434,7 +434,7 @@ class TestCodable : TestCodableSuper { // MARK: - DateInterval @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - lazy var dateIntervalValues: [Int : DateInterval] = [ + static let dateIntervalValues: [Int : DateInterval] = [ #line : DateInterval(), #line : DateInterval(start: Date.distantPast, end: Date()), #line : DateInterval(start: Date(), end: Date.distantFuture), @@ -443,14 +443,14 @@ class TestCodable : TestCodableSuper { @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) func test_DateInterval_JSON() { - for (testLine, interval) in dateIntervalValues { + for (testLine, interval) in Self.dateIntervalValues { expectRoundTripEqualityThroughJSON(for: interval, lineNumber: testLine) } } @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) func test_DateInterval_Plist() { - for (testLine, interval) in dateIntervalValues { + for (testLine, interval) in Self.dateIntervalValues { expectRoundTripEqualityThroughPlist(for: interval, lineNumber: testLine) } } @@ -641,7 +641,7 @@ class TestCodable : TestCodableSuper { // MARK: - Measurement @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - lazy var unitValues: [Int : Dimension] = [ + static let unitValues: [Int : Dimension] = [ #line : UnitAcceleration.metersPerSecondSquared, #line : UnitMass.kilograms, #line : UnitLength.miles @@ -649,7 +649,7 @@ class TestCodable : TestCodableSuper { @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) func test_Measurement_JSON() { - for (testLine, unit) in unitValues { + for (testLine, unit) in Self.unitValues { // FIXME: // Terminating due to uncaught exception NSInvalidArgumentException: // *** You must override baseUnit in your class NSDimension to define its base unit. @@ -660,7 +660,7 @@ class TestCodable : TestCodableSuper { @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) func test_Measurement_Plist() { - for (testLine, unit) in unitValues { + for (testLine, unit) in Self.unitValues { // FIXME: // Terminating due to uncaught exception NSInvalidArgumentException: // *** You must override baseUnit in your class NSDimension to define its base unit. @@ -741,7 +741,7 @@ class TestCodable : TestCodableSuper { // MARK: - PersonNameComponents @available(macOS 10.11, iOS 9.0, watchOS 2.0, tvOS 9.0, *) - lazy var personNameComponentsValues: [Int : PersonNameComponents] = [ + static let personNameComponentsValues: [Int : PersonNameComponents] = [ #line : makePersonNameComponents(givenName: "John", familyName: "Appleseed"), #line : makePersonNameComponents(givenName: "John", familyName: "Appleseed", nickname: "Johnny"), #line : makePersonNameComponents(namePrefix: "Dr.", givenName: "Jane", middleName: "A.", familyName: "Appleseed", nameSuffix: "Esq.", nickname: "Janie") @@ -749,14 +749,14 @@ class TestCodable : TestCodableSuper { @available(macOS 10.11, iOS 9.0, watchOS 2.0, tvOS 9.0, *) func test_PersonNameComponents_JSON() { - for (testLine, components) in personNameComponentsValues { + for (testLine, components) in Self.personNameComponentsValues { expectRoundTripEqualityThroughJSON(for: components, lineNumber: testLine) } } @available(macOS 10.11, iOS 9.0, watchOS 2.0, tvOS 9.0, *) func test_PersonNameComponents_Plist() { - for (testLine, components) in personNameComponentsValues { + for (testLine, components) in Self.personNameComponentsValues { expectRoundTripEqualityThroughPlist(for: components, lineNumber: testLine) } }