Skip to content

Commit 5c9ec49

Browse files
committed
ASTPrinter: handle inverses in compositions
The basic inheritance clause emission in ASTPrinter operates on InheritedEntry's, but does not canonicalize types. It's been designed to consider an entire composition unprintable because one member is unprintable (e.g., the protocol is not public). This rejection is what was causing `~Copyable` in some compositions to be missing from interface files (rdar://126090425). Fixing that is the purpose of this patch. What happens, then, if you mix public and nonpublic protocols in a composition? A second facility called the InheritedProtocolCollector later does find the public protocols, and emits extensions at the end of the interface file to declare the additional conformances the ininitial declaration printer missed. We can't generally emit `~Copyable` on an extension, so the fix can't happening there. Refactoring things so there's one source of truth about the protocols being printed is a sizable refactoring that I will defer for another time. resolves rdar://126090425 (cherry picked from commit 210cfb2)
1 parent 5c01e7f commit 5c9ec49

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7868,7 +7868,24 @@ swift::getInheritedForPrinting(
78687868
// Collect explicit inherited types.
78697869
for (auto i : inherited.getIndices()) {
78707870
if (auto ty = inherited.getResolvedType(i)) {
7871+
// Preserve inverses separately, because the `foundUnprintable` logic
7872+
// doesn't handle compositions with a mix of printable and unprintable
7873+
// types! That's handled later by `InheritedProtocolCollector`.
7874+
//
7875+
// Generally speaking, `getInheritedForPrinting` needs to be
7876+
// querying `InheritedProtocolCollector` to find out what protocols it
7877+
// should print in the inheritance clause, to reduce code duplication
7878+
// in the printer.
7879+
InvertibleProtocolSet printableInverses;
7880+
78717881
bool foundUnprintable = ty.findIf([&](Type subTy) {
7882+
{
7883+
// We canonicalize the composition to ensure no inverses are missed.
7884+
auto subCanTy = subTy->getCanonicalType();
7885+
if (auto PCT = subCanTy->getAs<ProtocolCompositionType>()) {
7886+
printableInverses.insertAll(PCT->getInverses());
7887+
}
7888+
}
78727889
if (auto aliasTy = dyn_cast<TypeAliasType>(subTy.getPointer()))
78737890
return !options.shouldPrint(aliasTy->getDecl());
78747891
if (auto NTD = subTy->getAnyNominal()) {
@@ -7877,8 +7894,22 @@ swift::getInheritedForPrinting(
78777894
}
78787895
return false;
78797896
});
7880-
if (foundUnprintable)
7897+
7898+
// Preserve any inverses that appeared in the unprintable type.
7899+
if (foundUnprintable) {
7900+
if (printableInverses.contains(InvertibleProtocolKind::Copyable)
7901+
&& options.SuppressNoncopyableGenerics)
7902+
printableInverses.remove(InvertibleProtocolKind::Copyable);
7903+
7904+
if (!printableInverses.empty()) {
7905+
auto inversesTy = ProtocolCompositionType::get(decl->getASTContext(),
7906+
/*members=*/{},
7907+
printableInverses,
7908+
/*anyObject=*/false);
7909+
Results.push_back(InheritedEntry(TypeLoc::withoutLoc(inversesTy)));
7910+
}
78817911
continue;
7912+
}
78827913

78837914
// Suppress Copyable and ~Copyable.
78847915
if (options.SuppressNoncopyableGenerics) {

test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,13 @@ public func substCopyable(_ t: String?) {}
127127
public func substGenericCopyable<T>(_ t: T?) {}
128128
public func substNC(_ t: borrowing NoCopyPls?) {}
129129
public func substGenericNC<T: ~Copyable>(_ t: borrowing T?) {}
130+
131+
// coverage for rdar://126090425
132+
protocol P : ~Copyable {} // NOTE: it's important that this is NOT public.
133+
protocol Q: ~Copyable {} // NOTE: it's important that this is NOT public.
134+
public protocol Publik: ~Copyable {}
135+
public struct Concrete : (P & ~Copyable) {}
136+
public struct Generic<T: Publik & ~Copyable> : (P & ~Copyable) {}
137+
public struct VeryNested: (P & (Q & ~Copyable & Publik) & (P & ~Copyable)) {}
138+
public struct Twice: P & ~Copyable, Q & ~Copyable {}
139+
public struct RegularTwice: ~Copyable, ~Copyable {}

test/ModuleInterface/noncopyable_generics.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,31 @@ import NoncopyableGenerics_Misc
192192
// CHECK-MISC-NEXT: public func substGenericNC<T>(_ t: borrowing T?)
193193
// CHECK-MISC-NEXT: #endif
194194

195+
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
196+
// CHECK-MISC-NEXT: public protocol Publik : ~Copyable {
197+
// CHECK-MISC-NEXT: }
198+
// CHECK-MISC-NEXT: #else
199+
// CHECK-MISC-NEXT: public protocol Publik {
200+
// CHECK-MISC-NEXT: }
201+
// CHECK-MISC-NEXT: #endif
202+
// CHECK-MISC-NEXT: public struct Concrete : ~Copyable {
203+
// CHECK-MISC-NEXT: }
204+
// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics
205+
// CHECK-MISC-NEXT: public struct Generic<T> : ~Copyable where T : {{.*}}.Publik, T : ~Copyable {
206+
// CHECK-MISC-NEXT: }
207+
// CHECK-MISC-NEXT: #else
208+
// CHECK-MISC-NEXT: public struct Generic<T> where T : {{.*}}.Publik {
209+
// CHECK-MISC-NEXT: }
210+
// CHECK-MISC-NEXT: #endif
211+
// CHECK-MISC-NEXT: public struct VeryNested : ~Copyable {
212+
// CHECK-MISC-NEXT: }
213+
// CHECK-MISC-NEXT: public struct Twice : ~Copyable, ~Copyable {
214+
// CHECK-MISC-NEXT: }
215+
// CHECK-MISC-NEXT: public struct RegularTwice : ~Swift.Copyable, ~Swift.Copyable {
216+
// CHECK-MISC-NEXT: }
217+
218+
// NOTE: below are extensions emitted at the end of NoncopyableGenerics_Misc.swift
219+
// CHECK-MISC: extension {{.*}}.VeryNested : {{.*}}.Publik {}
195220

196221
import Swiftskell
197222

0 commit comments

Comments
 (0)