Skip to content

Commit 15bcd23

Browse files
authored
[ModuleInterface] Propagate availability for nested types too (#25662)
Previously the module interface printing would scrape the AvailableAttrs from the containing decl in order to print synthesized extensions for conformances that wouldn't otherwise be printed...but that missed the case where a containing lexical scope had the availability attributes instead. Now it walks up the chain of parent DeclContexts and collects the most specific AvailableAttr for each platform. This /still/ isn't formally correct because it doesn't merge availability for one platform (if something inside is deprecated unconditionally but outside has an "introduced" version), but it's going to match the vast majority of code out there. Pre-requisite for rdar://problem/50100142
1 parent 62dd6bd commit 15bcd23

File tree

4 files changed

+58
-20
lines changed

4 files changed

+58
-20
lines changed

include/swift/AST/Attr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,9 @@ class DeclAttributes {
15281528
void dump(const Decl *D = nullptr) const;
15291529
void print(ASTPrinter &Printer, const PrintOptions &Options,
15301530
const Decl *D = nullptr) const;
1531+
static void print(ASTPrinter &Printer, const PrintOptions &Options,
1532+
ArrayRef<const DeclAttribute *> FlattenedAttrs,
1533+
const Decl *D = nullptr);
15311534

15321535
template <typename T, typename DERIVED>
15331536
class iterator_base : public std::iterator<std::forward_iterator_tag, T *> {

lib/AST/Attr.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,14 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
332332
if (!DeclAttrs)
333333
return;
334334

335+
SmallVector<const DeclAttribute *, 8> orderedAttributes(begin(), end());
336+
print(Printer, Options, orderedAttributes, D);
337+
}
338+
339+
void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
340+
ArrayRef<const DeclAttribute *> FlattenedAttrs,
341+
const Decl *D) {
335342
using AttributeVector = SmallVector<const DeclAttribute *, 8>;
336-
AttributeVector orderedAttributes(begin(), end());
337-
std::reverse(orderedAttributes.begin(), orderedAttributes.end());
338343

339344
// Process attributes in passes.
340345
AttributeVector shortAvailableAttributes;
@@ -344,7 +349,7 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
344349
AttributeVector attributes;
345350
AttributeVector modifiers;
346351

347-
for (auto DA : orderedAttributes) {
352+
for (auto DA : llvm::reverse(FlattenedAttrs)) {
348353
if (!Options.PrintImplicitAttrs && DA->isImplicit())
349354
continue;
350355
if (!Options.PrintUserInaccessibleAttrs &&

lib/Frontend/ParseableInterfaceSupport.cpp

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,28 @@ class InheritedProtocolCollector {
178178
SmallVector<const ProtocolType *, 8> ConditionalConformanceProtocols;
179179

180180
/// 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;
181+
static AvailableAttrList
182+
getAvailabilityAttrs(const Decl *D, Optional<AvailableAttrList> &cache) {
183+
if (cache.hasValue())
184+
return cache.getValue();
185+
186+
cache.emplace();
187+
while (D) {
188+
for (auto *nextAttr : D->getAttrs().getAttributes<AvailableAttr>()) {
189+
// FIXME: This is just approximating the effects of nested availability
190+
// attributes for the same platform; formally they'd need to be merged.
191+
bool alreadyHasMoreSpecificAttrForThisPlatform =
192+
llvm::any_of(*cache, [nextAttr](const AvailableAttr *existingAttr) {
193+
return existingAttr->Platform == nextAttr->Platform;
194+
});
195+
if (alreadyHasMoreSpecificAttrForThisPlatform)
196+
continue;
197+
cache->push_back(nextAttr);
198+
}
199+
D = D->getDeclContext()->getAsDecl();
200+
}
201+
202+
return cache.getValue();
186203
}
187204

188205
/// For each type in \p directlyInherited, classify the protocols it refers to
@@ -197,16 +214,13 @@ class InheritedProtocolCollector {
197214
continue;
198215

199216
bool canPrintNormally = isPublicOrUsableFromInline(inheritedTy);
200-
if (!canPrintNormally && !availableAttrs.hasValue())
201-
availableAttrs = getAvailabilityAttrs(D);
202-
203217
ExistentialLayout layout = inheritedTy->getExistentialLayout();
204218
for (ProtocolType *protoTy : layout.getProtocols()) {
205219
if (canPrintNormally)
206220
IncludedProtocols.push_back(protoTy->getDecl());
207221
else
208222
ExtraProtocols.push_back({protoTy->getDecl(),
209-
availableAttrs.getValue()});
223+
getAvailabilityAttrs(D, availableAttrs)});
210224
}
211225
// FIXME: This ignores layout constraints, but currently we don't support
212226
// any of those besides 'AnyObject'.
@@ -332,8 +346,7 @@ class InheritedProtocolCollector {
332346
// a bit more memory.
333347
// FIXME: This will pick the availability attributes from the first sight
334348
// of a protocol rather than the maximally available case.
335-
SmallVector<std::pair<ProtocolDecl *, AvailableAttrList>, 16>
336-
protocolsToPrint;
349+
SmallVector<ProtocolAndAvailability, 16> protocolsToPrint;
337350
for (const auto &protoAndAvailability : ExtraProtocols) {
338351
protoAndAvailability.first->walkInheritedProtocols(
339352
[&](ProtocolDecl *inherited) -> TypeWalker::Action {
@@ -354,8 +367,11 @@ class InheritedProtocolCollector {
354367

355368
for (const auto &protoAndAvailability : protocolsToPrint) {
356369
StreamPrinter printer(out);
357-
for (const AvailableAttr *attr : protoAndAvailability.second)
358-
attr->print(printer, printOptions);
370+
// FIXME: Shouldn't this be an implicit conversion?
371+
TinyPtrVector<const DeclAttribute *> attrs;
372+
attrs.insert(attrs.end(), protoAndAvailability.second.begin(),
373+
protoAndAvailability.second.end());
374+
DeclAttributes::print(printer, printOptions, attrs);
359375

360376
printer << "extension ";
361377
nominal->getDeclaredType().print(printer, printOptions);

test/ParseableInterface/conformances.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,21 +195,21 @@ extension Bool: ExtraHashable {}
195195
@available(macOS, unavailable)
196196
public struct CoolTVType: PrivateSubProto {}
197197
// CHECK: public struct CoolTVType {
198-
// CHECK-END: @available(OSX, unavailable)
199-
// CHECK-END-NEXT: @available(iOS, unavailable)
198+
// CHECK-END: @available(iOS, unavailable)
199+
// CHECK-END-NEXT: @available(OSX, unavailable)
200200
// CHECK-END-NEXT: extension conformances.CoolTVType : conformances.PublicBaseProto {}
201201

202202
@available(macOS 10.99, *)
203203
public struct VeryNewMacType: PrivateSubProto {}
204204
// CHECK: public struct VeryNewMacType {
205-
// CHECK-END: @available(OSX, introduced: 10.99)
205+
// CHECK-END: @available(OSX 10.99, *)
206206
// CHECK-END-NEXT: extension conformances.VeryNewMacType : conformances.PublicBaseProto {}
207207

208208
public struct VeryNewMacProto {}
209209
@available(macOS 10.98, *)
210210
extension VeryNewMacProto: PrivateSubProto {}
211211
// CHECK: public struct VeryNewMacProto {
212-
// CHECK-END: @available(OSX, introduced: 10.98)
212+
// CHECK-END: @available(OSX 10.98, *)
213213
// CHECK-END-NEXT: extension conformances.VeryNewMacProto : conformances.PublicBaseProto {}
214214

215215
public struct PrivateProtoConformer {}
@@ -228,5 +228,19 @@ extension PrivateProtoConformer : PrivateProto {
228228
// NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Equatable
229229

230230

231+
@available(macOS 10.97, iOS 22, *)
232+
@available(tvOS, unavailable)
233+
@available(swift 4.2.123)
234+
public struct NestedAvailabilityOuter {
235+
@available(iOS 23, *)
236+
public struct Inner: PrivateSubProto {}
237+
}
238+
239+
// CHECK-END: @available(swift 4.2.123)
240+
// CHECK-END-NEXT: @available(OSX 10.97, iOS 23, *)
241+
// CHECK-END-NEXT: @available(tvOS, unavailable)
242+
// CHECK-END-NEXT: extension conformances.NestedAvailabilityOuter.Inner : conformances.PublicBaseProto {}
243+
244+
231245
// CHECK-END: @usableFromInline
232246
// CHECK-END-NEXT: internal protocol _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}

0 commit comments

Comments
 (0)