Skip to content

Commit e43a9f5

Browse files
authored
[ModuleInterface] Propagate availability for synthesized extensions (#24753)
Otherwise, we can synthesize an extension that's extending a type that's unavailable on a particular platform, or that conforms to a protocol that hasn't been introduced on the minimum deployment target.
1 parent b9f42b3 commit e43a9f5

File tree

2 files changed

+92
-34
lines changed

2 files changed

+92
-34
lines changed

lib/Frontend/ParseableInterfaceSupport.cpp

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/AST/ASTContext.h"
14+
#include "swift/AST/ASTPrinter.h"
1415
#include "swift/AST/Decl.h"
1516
#include "swift/AST/DiagnosticsFrontend.h"
1617
#include "swift/AST/DiagnosticsSema.h"
@@ -163,47 +164,69 @@ namespace {
163164
class InheritedProtocolCollector {
164165
static const StringLiteral DummyProtocolName;
165166

167+
using AvailableAttrList = TinyPtrVector<const AvailableAttr *>;
168+
using ProtocolAndAvailability =
169+
std::pair<ProtocolDecl *, AvailableAttrList>;
170+
166171
/// Protocols that will be included by the ASTPrinter without any extra work.
167172
SmallVector<ProtocolDecl *, 8> IncludedProtocols;
168-
/// Protocols that will not be printed by the ASTPrinter.
169-
SmallVector<ProtocolDecl *, 8> ExtraProtocols;
173+
/// Protocols that will not be printed by the ASTPrinter, along with the
174+
/// availability they were declared with.
175+
SmallVector<ProtocolAndAvailability, 8> ExtraProtocols;
170176
/// Protocols that can be printed, but whose conformances are constrained with
171177
/// something that \e can't be printed.
172178
SmallVector<const ProtocolType *, 8> ConditionalConformanceProtocols;
173179

180+
/// Helper to extract the `@available` attributes on a decl.
181+
AvailableAttrList getAvailabilityAttrs(const Decl *D) {
182+
AvailableAttrList result;
183+
auto attrRange = D->getAttrs().getAttributes<AvailableAttr>();
184+
result.insert(result.begin(), attrRange.begin(), attrRange.end());
185+
return result;
186+
}
187+
174188
/// For each type in \p directlyInherited, classify the protocols it refers to
175189
/// as included for printing or not, and record them in the appropriate
176190
/// vectors.
177-
void recordProtocols(ArrayRef<TypeLoc> directlyInherited) {
191+
void recordProtocols(ArrayRef<TypeLoc> directlyInherited, const Decl *D) {
192+
Optional<AvailableAttrList> availableAttrs;
193+
178194
for (TypeLoc inherited : directlyInherited) {
179195
Type inheritedTy = inherited.getType();
180196
if (!inheritedTy || !inheritedTy->isExistentialType())
181197
continue;
182198

183199
bool canPrintNormally = isPublicOrUsableFromInline(inheritedTy);
184-
SmallVectorImpl<ProtocolDecl *> &whichProtocols =
185-
canPrintNormally ? IncludedProtocols : ExtraProtocols;
200+
if (!canPrintNormally && !availableAttrs.hasValue())
201+
availableAttrs = getAvailabilityAttrs(D);
186202

187203
ExistentialLayout layout = inheritedTy->getExistentialLayout();
188-
for (ProtocolType *protoTy : layout.getProtocols())
189-
whichProtocols.push_back(protoTy->getDecl());
204+
for (ProtocolType *protoTy : layout.getProtocols()) {
205+
if (canPrintNormally)
206+
IncludedProtocols.push_back(protoTy->getDecl());
207+
else
208+
ExtraProtocols.push_back({protoTy->getDecl(),
209+
availableAttrs.getValue()});
210+
}
190211
// FIXME: This ignores layout constraints, but currently we don't support
191212
// any of those besides 'AnyObject'.
192213
}
193214
}
194215

195-
/// For each type in \p directlyInherited, record any protocols that we would
196-
/// have printed in ConditionalConformanceProtocols.
197-
void recordConditionalConformances(ArrayRef<TypeLoc> directlyInherited) {
198-
for (TypeLoc inherited : directlyInherited) {
216+
/// For each type directly inherited by \p extension, record any protocols
217+
/// that we would have printed in ConditionalConformanceProtocols.
218+
void recordConditionalConformances(const ExtensionDecl *extension) {
219+
for (TypeLoc inherited : extension->getInherited()) {
199220
Type inheritedTy = inherited.getType();
200221
if (!inheritedTy || !inheritedTy->isExistentialType())
201222
continue;
202223

203224
ExistentialLayout layout = inheritedTy->getExistentialLayout();
204-
for (ProtocolType *protoTy : layout.getProtocols())
205-
if (isPublicOrUsableFromInline(protoTy))
206-
ConditionalConformanceProtocols.push_back(protoTy);
225+
for (ProtocolType *protoTy : layout.getProtocols()) {
226+
if (!isPublicOrUsableFromInline(protoTy))
227+
continue;
228+
ConditionalConformanceProtocols.push_back(protoTy);
229+
}
207230
// FIXME: This ignores layout constraints, but currently we don't support
208231
// any of those besides 'AnyObject'.
209232
}
@@ -243,7 +266,7 @@ class InheritedProtocolCollector {
243266
if (!isPublicOrUsableFromInline(nominal))
244267
return;
245268

246-
map[nominal].recordProtocols(directlyInherited);
269+
map[nominal].recordProtocols(directlyInherited, D);
247270

248271
// Recurse to find any nested types.
249272
for (const Decl *member : memberContext->getMembers())
@@ -264,7 +287,7 @@ class InheritedProtocolCollector {
264287
if (!isPublicOrUsableFromInline(nominal))
265288
return;
266289

267-
map[nominal].recordConditionalConformances(extension->getInherited());
290+
map[nominal].recordConditionalConformances(extension);
268291
// No recursion here because extensions are never nested.
269292
}
270293

@@ -307,16 +330,19 @@ class InheritedProtocolCollector {
307330
// Note: We could do this in one pass, but the logic is easier to
308331
// understand if we build up the list and then print it, even if it takes
309332
// a bit more memory.
310-
SmallVector<ProtocolDecl *, 16> protocolsToPrint;
311-
for (ProtocolDecl *proto : ExtraProtocols) {
312-
proto->walkInheritedProtocols(
333+
// FIXME: This will pick the availability attributes from the first sight
334+
// of a protocol rather than the maximally available case.
335+
SmallVector<std::pair<ProtocolDecl *, AvailableAttrList>, 16>
336+
protocolsToPrint;
337+
for (const auto &protoAndAvailability : ExtraProtocols) {
338+
protoAndAvailability.first->walkInheritedProtocols(
313339
[&](ProtocolDecl *inherited) -> TypeWalker::Action {
314340
if (!handledProtocols.insert(inherited).second)
315341
return TypeWalker::Action::SkipChildren;
316342

317343
if (isPublicOrUsableFromInline(inherited) &&
318344
conformanceDeclaredInModule(M, nominal, inherited)) {
319-
protocolsToPrint.push_back(inherited);
345+
protocolsToPrint.push_back({inherited, protoAndAvailability.second});
320346
return TypeWalker::Action::SkipChildren;
321347
}
322348

@@ -326,14 +352,20 @@ class InheritedProtocolCollector {
326352
if (protocolsToPrint.empty())
327353
return;
328354

329-
out << "extension ";
330-
nominal->getDeclaredType().print(out, printOptions);
331-
out << " : ";
332-
swift::interleave(protocolsToPrint,
333-
[&out, &printOptions](ProtocolDecl *proto) {
334-
proto->getDeclaredType()->print(out, printOptions);
335-
}, [&out] { out << ", "; });
336-
out << " {}\n";
355+
for (const auto &protoAndAvailability : protocolsToPrint) {
356+
StreamPrinter printer(out);
357+
for (const AvailableAttr *attr : protoAndAvailability.second)
358+
attr->print(printer, printOptions);
359+
360+
printer << "extension ";
361+
nominal->getDeclaredType().print(printer, printOptions);
362+
printer << " : ";
363+
364+
ProtocolDecl *proto = protoAndAvailability.first;
365+
proto->getDeclaredType()->print(printer, printOptions);
366+
367+
printer << " {}\n";
368+
}
337369
}
338370

339371
/// If there were any conditional conformances that couldn't be printed,
@@ -346,7 +378,7 @@ class InheritedProtocolCollector {
346378
return false;
347379
assert(nominal->isGenericContext());
348380

349-
out << "extension ";
381+
out << "@available(*, unavailable)\nextension ";
350382
nominal->getDeclaredType().print(out, printOptions);
351383
out << " : ";
352384
swift::interleave(ConditionalConformanceProtocols,

test/ParseableInterface/conformances.swift

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,14 @@ public struct OuterGeneric<T> {
8787
public protocol ConditionallyConformed {}
8888
public protocol ConditionallyConformedAgain {}
8989

90-
// CHECK-END: extension conformances.OuterGeneric : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
90+
// CHECK-END: @available(*, unavailable)
91+
// CHECK-END-NEXT: extension conformances.OuterGeneric : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
9192
extension OuterGeneric: ConditionallyConformed where T: PrivateProto {}
9293
extension OuterGeneric: ConditionallyConformedAgain where T == PrivateProto {}
9394

9495
// CHECK-END: extension conformances.OuterGeneric.Inner : conformances.PublicBaseProto {}
95-
// CHECK-END: extension conformances.OuterGeneric.Inner : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
96+
// CHECK-END: @available(*, unavailable)
97+
// CHECK-END-NEXT: extension conformances.OuterGeneric.Inner : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
9698
extension OuterGeneric.Inner: ConditionallyConformed where T: PrivateProto {}
9799
extension OuterGeneric.Inner: ConditionallyConformedAgain where T == PrivateProto {}
98100

@@ -120,10 +122,12 @@ public struct D1: PublicSubProto, PrivateSubProto {}
120122
// NEGATIVE-NOT: extension conformances.D2
121123
public struct D2: PrivateSubProto, PublicSubProto {}
122124
// CHECK: public struct D3 {
123-
// CHECK-END: extension conformances.D3 : conformances.PublicBaseProto, conformances.PublicSubProto {}
125+
// CHECK-END: extension conformances.D3 : conformances.PublicBaseProto {}
126+
// CHECK-END: extension conformances.D3 : conformances.PublicSubProto {}
124127
public struct D3: PrivateSubProto & PublicSubProto {}
125128
// CHECK: public struct D4 {
126-
// CHECK-END: extension conformances.D4 : conformances.APublicSubProto, conformances.PublicBaseProto {}
129+
// CHECK-END: extension conformances.D4 : conformances.APublicSubProto {}
130+
// CHECK-END: extension conformances.D4 : conformances.PublicBaseProto {}
127131
public struct D4: APublicSubProto & PrivateSubProto {}
128132
// CHECK: public struct D5 {
129133
// CHECK: extension D5 : PublicSubProto {
@@ -165,7 +169,8 @@ public struct MultiGeneric<T, U, V> {}
165169
extension MultiGeneric: PublicProto where U: PrivateProto {}
166170

167171
// CHECK: public struct MultiGeneric<T, U, V> {
168-
// CHECK-END: extension conformances.MultiGeneric : conformances.PublicProto where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
172+
// CHECK-END: @available(*, unavailable)
173+
// CHECK-END-NEXT: extension conformances.MultiGeneric : conformances.PublicProto where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
169174

170175

171176
internal struct InternalImpl_BAD: PrivateSubProto {}
@@ -186,6 +191,27 @@ extension WrapperForInternal.InternalImplConstrained2_BAD: PublicProto where T:
186191
internal protocol ExtraHashable: Hashable {}
187192
extension Bool: ExtraHashable {}
188193

194+
@available(iOS, unavailable)
195+
@available(macOS, unavailable)
196+
public struct CoolTVType: PrivateSubProto {}
197+
// CHECK: public struct CoolTVType {
198+
// CHECK-END: @available(OSX, unavailable)
199+
// CHECK-END-NEXT: @available(iOS, unavailable)
200+
// CHECK-END-NEXT: extension conformances.CoolTVType : conformances.PublicBaseProto {}
201+
202+
@available(macOS 10.99, *)
203+
public struct VeryNewMacType: PrivateSubProto {}
204+
// CHECK: public struct VeryNewMacType {
205+
// CHECK-END: @available(OSX, introduced: 10.99)
206+
// CHECK-END-NEXT: extension conformances.VeryNewMacType : conformances.PublicBaseProto {}
207+
208+
public struct VeryNewMacProto {}
209+
@available(macOS 10.98, *)
210+
extension VeryNewMacProto: PrivateSubProto {}
211+
// CHECK: public struct VeryNewMacProto {
212+
// CHECK-END: @available(OSX, introduced: 10.98)
213+
// CHECK-END-NEXT: extension conformances.VeryNewMacProto : conformances.PublicBaseProto {}
214+
189215
// NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Hashable
190216
// NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Equatable
191217

0 commit comments

Comments
 (0)