Skip to content

Enable availability attributes for associated types #70735

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 3 commits into from
Jan 6, 2024
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
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ DECL_ATTR(_silgen_name, SILGenName,
OnAbstractFunction | OnVar | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
0)
DECL_ATTR(available, Available,
OnAbstractFunction | OnGenericType | OnVar | OnSubscript | OnEnumElement | OnMacro | OnExtension | AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
OnAbstractFunction | OnAssociatedType | OnGenericType | OnVar | OnSubscript | OnEnumElement | OnMacro | OnExtension | AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
1)
DECL_ATTR(objc, ObjC,
OnAbstractFunction | OnClass | OnProtocol | OnExtension | OnVar | OnSubscript | OnEnum | OnEnumElement | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
Expand Down
5 changes: 4 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2851,7 +2851,6 @@ WARNING(assoc_type_default_conformance_failed,none,
NOTE(assoc_type_default_here,none,
"associated type %0 has default type %1 written here",
(const AssociatedTypeDecl *, Type))

ERROR(protocol_access,none,
"%select{protocol must be declared %select{"
"%select{private|fileprivate|internal|package|%error|%error}1"
Expand Down Expand Up @@ -6841,6 +6840,10 @@ ERROR(inlinable_decl_not_public,
ERROR(inlinable_resilient_deinit,
none, "deinitializer can only be '@inlinable' if the class is '@_fixed_layout'", ())

ERROR(resilient_associated_type_less_available_requires_default,none,
"associated type %0 that is less available than its protocol must have a "
"default", (const AssociatedTypeDecl *))

//------------------------------------------------------------------------------
// MARK: @_specialize diagnostics
//------------------------------------------------------------------------------
Expand Down
17 changes: 17 additions & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3880,10 +3880,16 @@ class TypeReprAvailabilityWalker : public ASTWalker {
DeclAvailabilityFlags flags;

bool checkIdentTypeRepr(IdentTypeRepr *ITR) {
ArrayRef<AssociatedTypeDecl *> primaryAssociatedTypes;

if (auto *typeDecl = ITR->getBoundDecl()) {
auto range = ITR->getNameLoc().getSourceRange();
if (diagnoseDeclAvailability(typeDecl, range, nullptr, where, flags))
return true;

if (auto protocol = dyn_cast<ProtocolDecl>(typeDecl)) {
primaryAssociatedTypes = protocol->getPrimaryAssociatedTypes();
}
}

bool foundAnyIssues = false;
Expand All @@ -3895,6 +3901,17 @@ class TypeReprAvailabilityWalker : public ASTWalker {
for (auto *genericArg : GTR->getGenericArgs()) {
if (diagnoseTypeReprAvailability(genericArg, where, genericFlags))
foundAnyIssues = true;

// The associated type that is being specified must be available as
// well.
if (!primaryAssociatedTypes.empty()) {
auto primaryAssociatedType = primaryAssociatedTypes.front();
primaryAssociatedTypes = primaryAssociatedTypes.drop_front();
if (diagnoseDeclAvailability(
primaryAssociatedType, genericArg->getSourceRange(),
nullptr, where, genericFlags))
foundAnyIssues = true;
}
}
}

Expand Down
10 changes: 10 additions & 0 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2866,6 +2866,16 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
AT->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
}
}

// An associated type that was introduced after the protocol
auto module = AT->getDeclContext()->getParentModule();
if (!defaultType &&
module->getResilienceStrategy() == ResilienceStrategy::Resilient &&
AvailabilityInference::availableRange(proto, Ctx)
.isSupersetOf(AvailabilityInference::availableRange(AT, Ctx))) {
AT->diagnose(
diag::resilient_associated_type_less_available_requires_default, AT);
}
}

void checkUnsupportedNestedType(NominalTypeDecl *NTD) {
Expand Down
14 changes: 14 additions & 0 deletions test/IRGen/weak_import_associated_conformance_descriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public protocol StrongProtoWithWeakLinkedAssoc {
@_weakLinked associatedtype Assoc: P
}

public struct LibConformsToP: P { }

public protocol StrongProtoWithNewAssoc {
@available(macOS 10.50, *)
associatedtype NewerAssoc: P = LibConformsToP
}

@available(macOS 10.50, *)
public protocol WeakProtoWithAssoc {
associatedtype Assoc: P
Expand All @@ -40,11 +47,18 @@ struct ConformsToStrongProtoWithAssoc: StrongProtoWithAssoc {
typealias Assoc = ConformsToP
}


// CHECK: @"$s7Library30StrongProtoWithWeakLinkedAssocP0G0AC_AA1PTn" = extern_weak global %swift.protocol_requirement, align 4
struct ConformsToStrongProtoWithWeakLinkedAssoc: StrongProtoWithWeakLinkedAssoc {
typealias Assoc = ConformsToP
}

// CHECK: @"$s7Library23StrongProtoWithNewAssocP05NewerF0AC_AA1PTn" = extern_weak global %swift.protocol_requirement, align 4
// CHECK: @"$s10NewerAssoc7Library018StrongProtoWithNewB0PTl" = extern_weak global %swift.protocol_requirement, align 4
struct ConformsToStrongProtoWithNewAssoc: StrongProtoWithNewAssoc {
typealias NewAssoc = ConformsToP
}

// CHECK: @"$s7Library18WeakProtoWithAssocP0E0AC_AA1PTn" = extern_weak global %swift.protocol_requirement, align 4
@available(macOS 10.50, *)
struct ConformsToWeakProtoWithAssoc: WeakProtoWithAssoc {
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/invalid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,5 @@ class C_50734<@NSApplicationMain T: AnyObject> {} // expected-error {{@NSApplica
func f6_50734<@discardableResult T>(x: T) {} // expected-error {{'@discardableResult' attribute cannot be applied to this declaration}}
enum E_50734<@indirect T> {} // expected-error {{'indirect' is a declaration modifier, not an attribute}} expected-error {{'indirect' modifier cannot be applied to this declaration}}
protocol P {
@available(swift, introduced: 4.2) associatedtype Assoc // expected-error {{'@available' attribute cannot be applied to this declaration}}
@available(swift, introduced: 4) associatedtype Assoc
}
2 changes: 2 additions & 0 deletions test/api-digester/Outputs/Cake-abi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ cake: Accessor GlobalLetChangedToVar.Modify() is a new API without @available at
cake: Accessor GlobalLetChangedToVar.Set() is a new API without @available attribute
cake: Accessor fixedLayoutStruct2.BecomeFixedBinaryOrder.Modify() is a new API without @available attribute
cake: Accessor fixedLayoutStruct2.BecomeFixedBinaryOrder.Set() is a new API without @available attribute
cake: AssociatedType RequirementChanges.addedTypeWithDefault is a new API without @available attribute
cake: AssociatedType RequirementChanges.addedTypeWithoutDefault is a new API without @available attribute
cake: Class C0 is a new API without @available attribute
cake: Class C8 is a new API without @available attribute
cake: Constructor AddingNewDesignatedInit.init(_:) is a new API without @available attribute
Expand Down
14 changes: 14 additions & 0 deletions test/attr/attr_inlinable_available.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1467,8 +1467,11 @@ public protocol NoAvailableProtoWithAssoc { // expected-note 3 {{add @available
associatedtype B: BeforeInliningTargetProto
associatedtype C: AtInliningTargetProto
associatedtype D: BetweenTargetsProto // expected-error {{'BetweenTargetsProto' is only available in macOS 10.14.5 or newer; clients of 'Test' may have a lower deployment target}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype E: AtDeploymentTargetProto // expected-error {{'AtDeploymentTargetProto' is only available in macOS 10.15 or newer; clients of 'Test' may have a lower deployment target}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype F: AfterDeploymentTargetProto // expected-error {{'AfterDeploymentTargetProto' is only available in}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
}

@available(macOS 10.9, *)
Expand All @@ -1477,8 +1480,11 @@ public protocol BeforeInliningTargetProtoWithAssoc {
associatedtype B: BeforeInliningTargetProto
associatedtype C: AtInliningTargetProto
associatedtype D: BetweenTargetsProto // expected-error {{'BetweenTargetsProto' is only available in macOS 10.14.5 or newer; clients of 'Test' may have a lower deployment target}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype E: AtDeploymentTargetProto // expected-error {{'AtDeploymentTargetProto' is only available in macOS 10.15 or newer; clients of 'Test' may have a lower deployment target}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype F: AfterDeploymentTargetProto // expected-error {{'AfterDeploymentTargetProto' is only available in}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
}

@available(macOS 10.10, *)
Expand All @@ -1487,8 +1493,11 @@ public protocol AtInliningTargetProtoWithAssoc {
associatedtype B: BeforeInliningTargetProto
associatedtype C: AtInliningTargetProto
associatedtype D: BetweenTargetsProto // expected-error {{'BetweenTargetsProto' is only available in macOS 10.14.5 or newer; clients of 'Test' may have a lower deployment target}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype E: AtDeploymentTargetProto // expected-error {{'AtDeploymentTargetProto' is only available in macOS 10.15 or newer; clients of 'Test' may have a lower deployment target}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype F: AfterDeploymentTargetProto // expected-error {{'AfterDeploymentTargetProto' is only available in}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
}

@available(macOS 10.14.5, *)
Expand All @@ -1498,7 +1507,9 @@ public protocol BetweenTargetsProtoWithAssoc {
associatedtype C: AtInliningTargetProto
associatedtype D: BetweenTargetsProto
associatedtype E: AtDeploymentTargetProto // expected-error {{'AtDeploymentTargetProto' is only available in macOS 10.15 or newer; clients of 'Test' may have a lower deployment target}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype F: AfterDeploymentTargetProto // expected-error {{'AfterDeploymentTargetProto' is only available in}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
}

@available(macOS 10.15, *)
Expand All @@ -1509,6 +1520,7 @@ public protocol AtDeploymentTargetProtoWithAssoc {
associatedtype D: BetweenTargetsProto
associatedtype E: AtDeploymentTargetProto
associatedtype F: AfterDeploymentTargetProto // expected-error {{'AfterDeploymentTargetProto' is only available in}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
}

@available(macOS 11, *)
Expand All @@ -1529,6 +1541,7 @@ public protocol UnavailableProtoWithAssoc {
associatedtype D: BetweenTargetsProto
associatedtype E: AtDeploymentTargetProto
associatedtype F: AfterDeploymentTargetProto // expected-error {{'AfterDeploymentTargetProto' is only available in}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
associatedtype G: UnavailableProto
}

Expand All @@ -1540,6 +1553,7 @@ public protocol SPINoAvailableProtoWithAssoc { // expected-note 1 {{add @availab
associatedtype D: BetweenTargetsProto
associatedtype E: AtDeploymentTargetProto
associatedtype F: AfterDeploymentTargetProto // expected-error {{'AfterDeploymentTargetProto' is only available in}}
// expected-note@-1{{add @available attribute to enclosing associated type}}
}

// MARK: - Type aliases
Expand Down
55 changes: 55 additions & 0 deletions test/decl/protocol/associated_type_availability.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// RUN: %swift -typecheck -verify -target %target-cpu-apple-macosx12 %s
// REQUIRES: OS=macosx


protocol P { }
extension Int: P { }

protocol P1<A, B> {
associatedtype A

@available(macOS 13, *)
associatedtype B: P
}

@available(macOS 13, *)
struct ModelP1<A, B: P>: P1 {
}

// Associated types in where clauses
func testWhereBad<T: P1, U>(_: T) where T.B == U { }
// expected-error@-1{{'B' is only available in macOS 13 or newer}}
// expected-note@-2{{add @available attribute to enclosing global function}}

@available(macOS 13, *)
func testWhereGood<T: P1, U>(_: T) where T.B == U { }

// Associated types in opaque parameter position
func testPrimaryOpaqueParamBad<U>(_: some P1<some Any, U>) {}
// expected-error@-1 2{{'B' is only available in macOS 13 or newer}}
// expected-note@-2 2{{add @available attribute to enclosing global function}}

@available(macOS 13, *)
func testPrimaryOpaqueParamGood<U: P>(_: some P1<some Any, U>) {}

// Associated types in opaque result position
func testPrimaryOpaqueResultBad<U: P>() -> some P1<String, U> {
// expected-error@-1{{'B' is only available in macOS 13 or newer}}
// expected-note@-2 2{{add @available attribute to enclosing global function}}
return ModelP1<String, U>()
// expected-error@-1{{'ModelP1' is only available in macOS 13 or newer}}
// expected-note@-2{{add 'if #available' version check}}
}

@available(macOS 13, *)
func testPrimaryOpaqueResultGood<U: P>() -> some P1<String, U> {
return ModelP1<String, U>()
}

// Associated types in existentials
func testPrimaryExistentialBad<U>(_: any P1<Int, U>) {}
// expected-error@-1{{'B' is only available in macOS 13 or newer}}
// expected-note@-2{{add @available attribute to enclosing global function}}

@available(macOS 13, *)
func testPrimaryExistentialGood<U>(_: any P1<Int, U>) {}
18 changes: 18 additions & 0 deletions test/decl/protocol/associated_type_availability_resilient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: %swift -typecheck -verify -target %target-cpu-apple-macosx12 %s -enable-library-evolution
// REQUIRES: OS=macosx


protocol P { }
extension Int: P { }

@available(macOS 12, *)
protocol P1 {
associatedtype A

@available(macOS 13, *)
associatedtype B: P
// expected-error@-1{{associated type 'B' that is less available than its protocol must have a default}}

@available(macOS 13, *)
associatedtype C: P = Int
}