Skip to content

AST: Improve accuracy of inferred availability for synthesized declarations #64310

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 2 commits into from
Mar 14, 2023
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
50 changes: 39 additions & 11 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,54 @@ void AvailabilityInference::applyInferredAvailableAttrs(
// a per-platform basis.
std::map<PlatformKind, InferredAvailability> Inferred;
for (const Decl *D : InferredFromDecls) {
for (const DeclAttribute *Attr : D->getAttrs()) {
auto *AvAttr = dyn_cast<AvailableAttr>(Attr);
if (!AvAttr || AvAttr->isInvalid())
continue;
do {
for (const DeclAttribute *Attr : D->getAttrs()) {
auto *AvAttr = dyn_cast<AvailableAttr>(Attr);
if (!AvAttr || AvAttr->isInvalid())
continue;

mergeWithInferredAvailability(AvAttr, Inferred[AvAttr->Platform]);
mergeWithInferredAvailability(AvAttr, Inferred[AvAttr->Platform]);

if (Message.empty() && !AvAttr->Message.empty())
Message = AvAttr->Message;
if (Message.empty() && !AvAttr->Message.empty())
Message = AvAttr->Message;

if (Rename.empty() && !AvAttr->Rename.empty()) {
Rename = AvAttr->Rename;
RenameDecl = AvAttr->RenameDecl;
if (Rename.empty() && !AvAttr->Rename.empty()) {
Rename = AvAttr->Rename;
RenameDecl = AvAttr->RenameDecl;
}
}

// Walk up the enclosing declaration hierarchy to make sure we aren't
// missing any inherited attributes.
D = AvailabilityInference::parentDeclForInferredAvailability(D);
} while (D);
}

DeclAttributes &Attrs = ToDecl->getAttrs();

// Some kinds of platform agnostic availability supersede any platform
// specific availability.
auto InferredAgnostic = Inferred.find(PlatformKind::none);
if (InferredAgnostic != Inferred.end()) {
switch (InferredAgnostic->second.PlatformAgnostic) {
case PlatformAgnosticAvailabilityKind::Deprecated:
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
case PlatformAgnosticAvailabilityKind::Unavailable:
Attrs.add(createAvailableAttr(PlatformKind::none,
InferredAgnostic->second, Message, Rename,
RenameDecl, Context));
return;

case PlatformAgnosticAvailabilityKind::None:
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
case PlatformAgnosticAvailabilityKind::NoAsync:
break;
}
}

// Create an availability attribute for each observed platform and add
// to ToDecl.
DeclAttributes &Attrs = ToDecl->getAttrs();
for (auto &Pair : Inferred) {
auto *Attr = createAvailableAttr(Pair.first, Pair.second, Message,
Rename, RenameDecl, Context);
Expand Down
12 changes: 12 additions & 0 deletions test/ModuleInterface/actor_availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,16 @@ extension Enum {
// CHECK-NEXT: get
// CHECK-NEXT: }
}

// CHECK: #if compiler(>=5.3) && $Actors
// CHECK-NEXT: @_hasMissingDesignatedInitializers @available(macOS, unavailable)
// CHECK-NEXT: public actor UnavailableExtensionNestedActor {
@available(macOS, unavailable)
public actor UnavailableExtensionNestedActor {
// CHECK: @available(iOS 13.4, tvOS 13.4, watchOS 6.2, *)
// CHECK-NEXT: @available(macOS, unavailable)
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
// CHECK-NEXT: get
// CHECK-NEXT: }
}
}
89 changes: 76 additions & 13 deletions test/Sema/generalized_accessors_availability.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.9 -typecheck -verify %s
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.50 -typecheck -verify %s

// REQUIRES: OS=macosx

Expand All @@ -8,14 +8,14 @@ struct SetterConditionallyAvailable<T> {
var wrappedValue: T {
get { fatalError() }

@available(macOS 10.10, *)
@available(macOS 10.51, *)
set { fatalError() }
}

var projectedValue: T {
get { fatalError() }

@available(macOS 10.10, *)
@available(macOS 10.51, *)
set { fatalError() }
}
}
Expand All @@ -25,14 +25,48 @@ struct ModifyConditionallyAvailable<T> {
var wrappedValue: T {
get { fatalError() }

@available(macOS 10.10, *)
@available(macOS 10.51, *)
_modify { fatalError() }
}

var projectedValue: T {
get { fatalError() }

@available(macOS 10.10, *)
@available(macOS 10.51, *)
_modify { fatalError() }
}
}

@propertyWrapper
struct SetterMoreAvailable<T> {
var wrappedValue: T {
get { fatalError() }

@available(macOS 10.49, *)
set { fatalError() }
}

var projectedValue: T {
get { fatalError() }

@available(macOS 10.49, *)
set { fatalError() }
}
}

@propertyWrapper
struct ModifyMoreAvailable<T> {
var wrappedValue: T {
get { fatalError() }

@available(macOS 10.49, *)
_modify { fatalError() }
}

var projectedValue: T {
get { fatalError() }

@available(macOS 10.49, *)
_modify { fatalError() }
}
}
Expand All @@ -41,7 +75,7 @@ struct Butt {
var modify_conditionally_available: Int {
get { fatalError() }

@available(macOS 10.10, *)
@available(macOS 10.51, *)
_modify { fatalError() }
}

Expand All @@ -50,16 +84,26 @@ struct Butt {

@ModifyConditionallyAvailable
var wrapped_modify_conditionally_available: Int

@SetterMoreAvailable
var wrapped_setter_more_available: Int

@ModifyMoreAvailable
var wrapped_modify_more_available: Int
}

func butt(x: inout Butt) { // expected-note*{{}}
x.modify_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
x.wrapped_setter_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
x.wrapped_modify_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
x.$wrapped_setter_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
x.$wrapped_modify_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
func butt(x: inout Butt) { // expected-note * {{}}
x.modify_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
x.wrapped_setter_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
x.wrapped_modify_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
x.$wrapped_setter_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
x.$wrapped_modify_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
x.wrapped_setter_more_available = 0
x.wrapped_modify_more_available = 0
x.$wrapped_setter_more_available = 0
x.$wrapped_modify_more_available = 0

if #available(macOS 10.10, *) {
if #available(macOS 10.51, *) {
x.modify_conditionally_available = 0
x.wrapped_setter_conditionally_available = 0
x.wrapped_modify_conditionally_available = 0
Expand All @@ -68,6 +112,25 @@ func butt(x: inout Butt) { // expected-note*{{}}
}
}

@available(macOS, unavailable)
extension Butt {
@available(iOS, unavailable)
struct Nested { // expected-note {{has been explicitly marked unavailable here}}
@SetterMoreAvailable
var wrapped_setter_more_available: Int // expected-note 2 {{has been explicitly marked unavailable here}}

@ModifyMoreAvailable
var wrapped_modify_more_available: Int // expected-note 2 {{has been explicitly marked unavailable here}}
}
}

func testButtNested(x: inout Butt.Nested) { // expected-error {{'Nested' is unavailable in macOS}}
x.wrapped_setter_more_available = 0 // expected-error {{is unavailable in macOS}}
x.wrapped_modify_more_available = 0 // expected-error {{is unavailable in macOS}}
x.$wrapped_setter_more_available = 0 // expected-error {{is unavailable in macOS}}
x.$wrapped_modify_more_available = 0 // expected-error {{is unavailable in macOS}}
}

@available(macOS 11.0, *)
struct LessAvailable {
@SetterConditionallyAvailable
Expand Down
33 changes: 33 additions & 0 deletions validation-test/Sema/SwiftUI/rdar106575142.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx12 -swift-version 5

// REQUIRES: objc_interop
// REQUIRES: OS=macosx

import SwiftUI

@available(macOS 12, *)
struct S {}

@available(iOS, unavailable)
extension S {
class NestedOtherPlatformUnavailable {
@Published var x: Int = 0
@Binding var y: Bool

init(y: Binding<Bool>) {
_y = y
}
}
}

@available(*, unavailable)
extension S {
class NestedAlwaysUnavailable {
@Published var x: Int = 0
@Binding var y: Bool

init(y: Binding<Bool>) {
_y = y
}
}
}