Skip to content

Commit b04cff1

Browse files
authored
Merge pull request #25709 from jrose-apple/5.1-imply-in-the-sky
[5.1] [ModuleInterface] Explicitly print implied Hashable et al on enums
2 parents cddec31 + 4177f9e commit b04cff1

File tree

6 files changed

+107
-22
lines changed

6 files changed

+107
-22
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: 42 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,20 +214,30 @@ 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'.
213227
}
228+
229+
// Check for synthesized protocols, like Hashable on enums.
230+
if (auto *nominal = dyn_cast<NominalTypeDecl>(D)) {
231+
SmallVector<ProtocolConformance *, 4> localConformances =
232+
nominal->getLocalConformances(ConformanceLookupKind::NonInherited);
233+
234+
for (auto *conf : localConformances) {
235+
if (conf->getSourceKind() != ConformanceEntryKind::Synthesized)
236+
continue;
237+
ExtraProtocols.push_back({conf->getProtocol(),
238+
getAvailabilityAttrs(D, availableAttrs)});
239+
}
240+
}
214241
}
215242

216243
/// For each type directly inherited by \p extension, record any protocols
@@ -332,8 +359,7 @@ class InheritedProtocolCollector {
332359
// a bit more memory.
333360
// FIXME: This will pick the availability attributes from the first sight
334361
// of a protocol rather than the maximally available case.
335-
SmallVector<std::pair<ProtocolDecl *, AvailableAttrList>, 16>
336-
protocolsToPrint;
362+
SmallVector<ProtocolAndAvailability, 16> protocolsToPrint;
337363
for (const auto &protoAndAvailability : ExtraProtocols) {
338364
protoAndAvailability.first->walkInheritedProtocols(
339365
[&](ProtocolDecl *inherited) -> TypeWalker::Action {
@@ -354,8 +380,11 @@ class InheritedProtocolCollector {
354380

355381
for (const auto &protoAndAvailability : protocolsToPrint) {
356382
StreamPrinter printer(out);
357-
for (const AvailableAttr *attr : protoAndAvailability.second)
358-
attr->print(printer, printOptions);
383+
// FIXME: Shouldn't this be an implicit conversion?
384+
TinyPtrVector<const DeclAttribute *> attrs;
385+
attrs.insert(attrs.end(), protoAndAvailability.second.begin(),
386+
protoAndAvailability.second.end());
387+
DeclAttributes::print(printer, printOptions, attrs);
359388

360389
printer << "extension ";
361390
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 {}

test/ParseableInterface/enums-layout.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -emit-module-interface-path %t/Lib.swiftinterface -typecheck -enable-library-evolution -enable-objc-interop -disable-objc-attr-requires-foundation-module -swift-version 5 %S/Inputs/enums-layout-helper.swift
2+
// RUN: %target-swift-frontend -emit-module-interface-path %t/Lib.swiftinterface -typecheck -enable-library-evolution -enable-objc-interop -disable-objc-attr-requires-foundation-module -swift-version 5 %S/Inputs/enums-layout-helper.swift -module-name Lib
33
// RUN: %FileCheck %S/Inputs/enums-layout-helper.swift < %t/Lib.swiftinterface
44
// RUN: %target-swift-frontend -enable-objc-interop -O -emit-ir -primary-file %s -I %t | %FileCheck %s
55

test/ParseableInterface/synthesized.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// RUN: %target-swift-frontend -typecheck -emit-parseable-module-interface-path - %s -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s
1+
// RUN: %target-swift-frontend -typecheck -emit-parseable-module-interface-path - %s -disable-objc-attr-requires-foundation-module -enable-objc-interop > %t.swiftinterface
2+
// RUN: %FileCheck %s < %t.swiftinterface
3+
// RUN: %FileCheck -check-prefix=NEGATIVE %s < %t.swiftinterface
24

35
// CHECK-LABEL: public enum HasRawValue : Swift.Int {
46
public enum HasRawValue: Int {
@@ -23,3 +25,35 @@ public enum HasRawValue: Int {
2325
// CHECK-DAG: @inlinable get{{$}}
2426
// CHECK-DAG: }
2527
// CHECK: {{^}$}}
28+
29+
// CHECK-LABEL: public enum NoRawValueWithExplicitEquatable : Swift.Equatable {
30+
public enum NoRawValueWithExplicitEquatable : Equatable {
31+
// CHECK-NEXT: case a, b, c
32+
case a, b, c
33+
} // CHECK: {{^}$}}
34+
35+
// CHECK-LABEL: public enum NoRawValueWithExplicitHashable {
36+
public enum NoRawValueWithExplicitHashable {
37+
// CHECK-NEXT: case a, b, c
38+
case a, b, c
39+
} // CHECK: {{^}$}}
40+
41+
// CHECK-LABEL: extension NoRawValueWithExplicitHashable : Swift.Hashable {
42+
extension NoRawValueWithExplicitHashable : Hashable {
43+
// CHECK-NEXT: public func foo()
44+
public func foo() {}
45+
} // CHECK: {{^}$}}
46+
47+
// CHECK: extension synthesized.HasRawValue : Swift.Equatable {}
48+
// CHECK: extension synthesized.HasRawValue : Swift.Hashable {}
49+
// CHECK: extension synthesized.HasRawValue : Swift.RawRepresentable {}
50+
51+
// CHECK: extension synthesized.ObjCEnum : Swift.Equatable {}
52+
// CHECK: extension synthesized.ObjCEnum : Swift.Hashable {}
53+
// CHECK: extension synthesized.ObjCEnum : Swift.RawRepresentable {}
54+
55+
// CHECK: extension synthesized.NoRawValueWithExplicitEquatable : Swift.Hashable {}
56+
// NEGATIVE-NOT: extension {{.+}}NoRawValueWithExplicitEquatable : Swift.Equatable
57+
58+
// NEGATIVE-NOT: NoRawValueWithExplicitHashable : Swift.Equatable
59+
// NEGATIVE-NOT: NoRawValueWithExplicitHashable : Swift.Hashable {}

0 commit comments

Comments
 (0)