Skip to content

Commit b901771

Browse files
committed
Enable availability attributes for associated types
Allow `@available` on associated types, which can be introduced after the protocol was introduced. These work precisely how on expects, because all of the infrastructure for introducing associated types later on has been available for a while, with two small restrictions: 1. If one uses the primary associated type syntax (e.g., `P<A, B>`), then the primary associated types must also be available in the current context. 2. Adding a new associated type to a resilient protocol requires that associated type to have a default.
1 parent baaa8f3 commit b901771

File tree

6 files changed

+105
-2
lines changed

6 files changed

+105
-2
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ DECL_ATTR(_silgen_name, SILGenName,
108108
OnAbstractFunction | OnVar | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
109109
0)
110110
DECL_ATTR(available, Available,
111-
OnAbstractFunction | OnGenericType | OnVar | OnSubscript | OnEnumElement | OnMacro | OnExtension | AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
111+
OnAbstractFunction | OnAssociatedType | OnGenericType | OnVar | OnSubscript | OnEnumElement | OnMacro | OnExtension | AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
112112
1)
113113
DECL_ATTR(objc, ObjC,
114114
OnAbstractFunction | OnClass | OnProtocol | OnExtension | OnVar | OnSubscript | OnEnum | OnEnumElement | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2851,7 +2851,6 @@ WARNING(assoc_type_default_conformance_failed,none,
28512851
NOTE(assoc_type_default_here,none,
28522852
"associated type %0 has default type %1 written here",
28532853
(const AssociatedTypeDecl *, Type))
2854-
28552854
ERROR(protocol_access,none,
28562855
"%select{protocol must be declared %select{"
28572856
"%select{private|fileprivate|internal|package|%error|%error}1"
@@ -6841,6 +6840,10 @@ ERROR(inlinable_decl_not_public,
68416840
ERROR(inlinable_resilient_deinit,
68426841
none, "deinitializer can only be '@inlinable' if the class is '@_fixed_layout'", ())
68436842

6843+
ERROR(resilient_associated_type_less_available_requires_default,none,
6844+
"associated type %0 that is less available than its protocol must have a "
6845+
"default", (const AssociatedTypeDecl *))
6846+
68446847
//------------------------------------------------------------------------------
68456848
// MARK: @_specialize diagnostics
68466849
//------------------------------------------------------------------------------

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3880,10 +3880,16 @@ class TypeReprAvailabilityWalker : public ASTWalker {
38803880
DeclAvailabilityFlags flags;
38813881

38823882
bool checkIdentTypeRepr(IdentTypeRepr *ITR) {
3883+
ArrayRef<AssociatedTypeDecl *> primaryAssociatedTypes;
3884+
38833885
if (auto *typeDecl = ITR->getBoundDecl()) {
38843886
auto range = ITR->getNameLoc().getSourceRange();
38853887
if (diagnoseDeclAvailability(typeDecl, range, nullptr, where, flags))
38863888
return true;
3889+
3890+
if (auto protocol = dyn_cast<ProtocolDecl>(typeDecl)) {
3891+
primaryAssociatedTypes = protocol->getPrimaryAssociatedTypes();
3892+
}
38873893
}
38883894

38893895
bool foundAnyIssues = false;
@@ -3895,6 +3901,17 @@ class TypeReprAvailabilityWalker : public ASTWalker {
38953901
for (auto *genericArg : GTR->getGenericArgs()) {
38963902
if (diagnoseTypeReprAvailability(genericArg, where, genericFlags))
38973903
foundAnyIssues = true;
3904+
3905+
// The associated type that is being specified must be available as
3906+
// well.
3907+
if (!primaryAssociatedTypes.empty()) {
3908+
auto primaryAssociatedType = primaryAssociatedTypes.front();
3909+
primaryAssociatedTypes = primaryAssociatedTypes.drop_front();
3910+
if (diagnoseDeclAvailability(
3911+
primaryAssociatedType, genericArg->getSourceRange(),
3912+
nullptr, where, genericFlags))
3913+
foundAnyIssues = true;
3914+
}
38983915
}
38993916
}
39003917

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,6 +2866,16 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
28662866
AT->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
28672867
}
28682868
}
2869+
2870+
// An associated type that was introduced after the protocol
2871+
auto module = AT->getDeclContext()->getParentModule();
2872+
if (!defaultType &&
2873+
module->getResilienceStrategy() == ResilienceStrategy::Resilient &&
2874+
AvailabilityInference::availableRange(proto, Ctx)
2875+
.isSupersetOf(AvailabilityInference::availableRange(AT, Ctx))) {
2876+
AT->diagnose(
2877+
diag::resilient_associated_type_less_available_requires_default, AT);
2878+
}
28692879
}
28702880

28712881
void checkUnsupportedNestedType(NominalTypeDecl *NTD) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %swift -typecheck -verify -target %target-cpu-apple-macosx12 %s
2+
// REQUIRES: OS=macosx
3+
4+
5+
protocol P { }
6+
extension Int: P { }
7+
8+
protocol P1<A, B> {
9+
associatedtype A
10+
11+
@available(macOS 13, *)
12+
associatedtype B: P
13+
}
14+
15+
@available(macOS 13, *)
16+
struct ModelP1<A, B: P>: P1 {
17+
}
18+
19+
// Associated types in where clauses
20+
func testWhereBad<T: P1, U>(_: T) where T.B == U { }
21+
// expected-error@-1{{'B' is only available in macOS 13 or newer}}
22+
// expected-note@-2{{add @available attribute to enclosing global function}}
23+
24+
@available(macOS 13, *)
25+
func testWhereGood<T: P1, U>(_: T) where T.B == U { }
26+
27+
// Associated types in opaque parameter position
28+
func testPrimaryOpaqueParamBad<U>(_: some P1<some Any, U>) {}
29+
// expected-error@-1 2{{'B' is only available in macOS 13 or newer}}
30+
// expected-note@-2 2{{add @available attribute to enclosing global function}}
31+
32+
@available(macOS 13, *)
33+
func testPrimaryOpaqueParamGood<U: P>(_: some P1<some Any, U>) {}
34+
35+
// Associated types in opaque result position
36+
func testPrimaryOpaqueResultBad<U: P>() -> some P1<String, U> {
37+
// expected-error@-1{{'B' is only available in macOS 13 or newer}}
38+
// expected-note@-2 2{{add @available attribute to enclosing global function}}
39+
return ModelP1<String, U>()
40+
// expected-error@-1{{'ModelP1' is only available in macOS 13 or newer}}
41+
// expected-note@-2{{add 'if #available' version check}}
42+
}
43+
44+
@available(macOS 13, *)
45+
func testPrimaryOpaqueResultGood<U: P>() -> some P1<String, U> {
46+
return ModelP1<String, U>()
47+
}
48+
49+
// Associated types in existentials
50+
func testPrimaryExistentialBad<U>(_: any P1<Int, U>) {}
51+
// expected-error@-1{{'B' is only available in macOS 13 or newer}}
52+
// expected-note@-2{{add @available attribute to enclosing global function}}
53+
54+
@available(macOS 13, *)
55+
func testPrimaryExistentialGood<U>(_: any P1<Int, U>) {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %swift -typecheck -verify -target %target-cpu-apple-macosx12 %s -enable-library-evolution
2+
// REQUIRES: OS=macosx
3+
4+
5+
protocol P { }
6+
extension Int: P { }
7+
8+
@available(macOS 12, *)
9+
protocol P1 {
10+
associatedtype A
11+
12+
@available(macOS 13, *)
13+
associatedtype B: P
14+
// expected-error@-1{{associated type 'B' that is less available than its protocol must have a default}}
15+
16+
@available(macOS 13, *)
17+
associatedtype C: P = Int
18+
}

0 commit comments

Comments
 (0)