Skip to content

Commit 210cfb2

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
1 parent 65b0b38 commit 210cfb2

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
@@ -7861,7 +7861,24 @@ swift::getInheritedForPrinting(
78617861
// Collect explicit inherited types.
78627862
for (auto i : inherited.getIndices()) {
78637863
if (auto ty = inherited.getResolvedType(i)) {
7864+
// Preserve inverses separately, because the `foundUnprintable` logic
7865+
// doesn't handle compositions with a mix of printable and unprintable
7866+
// types! That's handled later by `InheritedProtocolCollector`.
7867+
//
7868+
// Generally speaking, `getInheritedForPrinting` needs to be
7869+
// querying `InheritedProtocolCollector` to find out what protocols it
7870+
// should print in the inheritance clause, to reduce code duplication
7871+
// in the printer.
7872+
InvertibleProtocolSet printableInverses;
7873+
78647874
bool foundUnprintable = ty.findIf([&](Type subTy) {
7875+
{
7876+
// We canonicalize the composition to ensure no inverses are missed.
7877+
auto subCanTy = subTy->getCanonicalType();
7878+
if (auto PCT = subCanTy->getAs<ProtocolCompositionType>()) {
7879+
printableInverses.insertAll(PCT->getInverses());
7880+
}
7881+
}
78657882
if (auto aliasTy = dyn_cast<TypeAliasType>(subTy.getPointer()))
78667883
return !options.shouldPrint(aliasTy->getDecl());
78677884
if (auto NTD = subTy->getAnyNominal()) {
@@ -7870,8 +7887,22 @@ swift::getInheritedForPrinting(
78707887
}
78717888
return false;
78727889
});
7873-
if (foundUnprintable)
7890+
7891+
// Preserve any inverses that appeared in the unprintable type.
7892+
if (foundUnprintable) {
7893+
if (printableInverses.contains(InvertibleProtocolKind::Copyable)
7894+
&& options.SuppressNoncopyableGenerics)
7895+
printableInverses.remove(InvertibleProtocolKind::Copyable);
7896+
7897+
if (!printableInverses.empty()) {
7898+
auto inversesTy = ProtocolCompositionType::get(decl->getASTContext(),
7899+
/*members=*/{},
7900+
printableInverses,
7901+
/*anyObject=*/false);
7902+
Results.push_back(InheritedEntry(TypeLoc::withoutLoc(inversesTy)));
7903+
}
78747904
continue;
7905+
}
78757906

78767907
// Suppress Copyable and ~Copyable.
78777908
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)