Skip to content

Commit 35be4a1

Browse files
committed
NCGenerics: avoid feature-guarding in some cases
With the generalization of Optional to support noncopyable types, our feature-guarding in swiftinterface files would double-print functions that simply refer to the Optional type. Since NoncopyableGenerics is a suppressible feature, by default a second version of Optional and UnsafePointer are emitted into swiftinterface files, where the ~Copyable generalization is stripped away. We can rely on that to avoid double-printing the function, if the types substituted for the generic parameters are all Copyable. We need a bit more checking for when `@_disallowFeatureSuppression(NoncopyableGenerics)` is used, since this trick relies on there always being a definition of the type we refer to, whether the feature is enabled or not. resolves rdar://127389991 (cherry picked from commit f0f91ee)
1 parent 62a3fbf commit 35be4a1

File tree

4 files changed

+85
-12
lines changed

4 files changed

+85
-12
lines changed

lib/AST/FeatureSet.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,21 @@ static bool usesFeatureRawLayout(Decl *decl) {
504504
UNINTERESTING_FEATURE(Embedded)
505505
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
506506

507+
static bool disallowFeatureSuppression(StringRef featureName, Decl *decl);
508+
509+
static bool allBoundTypesAreCopyable(Type type, DeclContext *context) {
510+
assert(type->getAnyNominal());
511+
auto bgt = type->getAs<BoundGenericType>();
512+
if (!bgt)
513+
return false; // nothing is bound.
514+
515+
for (auto argInterfaceTy : bgt->getGenericArgs())
516+
if (context->mapTypeIntoContext(argInterfaceTy)->isNoncopyable())
517+
return false;
518+
519+
return true;
520+
}
521+
507522
static bool usesFeatureNoncopyableGenerics(Decl *decl) {
508523
if (decl->getAttrs().hasAttribute<PreInverseGenericsAttr>())
509524
return true;
@@ -521,15 +536,29 @@ static bool usesFeatureNoncopyableGenerics(Decl *decl) {
521536

522537
if (isa<AbstractFunctionDecl>(valueDecl) ||
523538
isa<AbstractStorageDecl>(valueDecl)) {
524-
if (valueDecl->getInterfaceType().findIf([&](Type type) -> bool {
525-
if (auto *nominalDecl = type->getAnyNominal()) {
526-
if (isa<StructDecl, EnumDecl, ClassDecl>(nominalDecl))
527-
return usesFeatureNoncopyableGenerics(nominalDecl);
528-
}
529-
return false;
530-
})) {
539+
auto *context = decl->getInnermostDeclContext();
540+
auto usesFeature = valueDecl->getInterfaceType().findIf(
541+
[&](Type type) -> bool {
542+
auto *nominalDecl = type->getAnyNominal();
543+
if (!nominalDecl || !isa<StructDecl, EnumDecl, ClassDecl>(nominalDecl))
544+
return false;
545+
546+
if (!usesFeatureNoncopyableGenerics(nominalDecl))
547+
return false;
548+
549+
// If we only _refer_ to a TypeDecl that uses NoncopyableGenerics,
550+
// and a suppressed version of that decl is in the interface, then we're
551+
// only referring to the un-suppressed version if any of the bound types
552+
// are noncopyable. (rdar://127389991)
553+
if (!disallowFeatureSuppression("NoncopyableGenerics", nominalDecl)
554+
&& allBoundTypesAreCopyable(type, context)) {
555+
return false;
556+
}
557+
558+
return true;
559+
});
560+
if (usesFeature)
531561
return true;
532-
}
533562
}
534563
}
535564

test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,18 @@ public func borrowsNoncopyable<T: ~Copyable>(_ t: borrowing T) {}
112112

113113
@_disallowFeatureSuppression(NoncopyableGenerics)
114114
public func suppressesNoncopyableGenerics<T: ~Copyable>(_ t: borrowing T) {}
115+
116+
// coverage for rdar://127389991
117+
@_disallowFeatureSuppression(NoncopyableGenerics)
118+
public struct LoudlyNC<T: ~Copyable> {}
119+
public func _indexHumongousDonuts<TTT, T>(_ aggregate: UnsafePointer<TTT>, _ index: Int) -> T {
120+
return UnsafeRawPointer(aggregate).load(
121+
fromByteOffset: index * MemoryLayout<T>.stride, as: T.self)
122+
}
123+
public func referToLoud(_ t: LoudlyNC<String>) {}
124+
@_disallowFeatureSuppression(NoncopyableGenerics) public func referToLoudProperGuarding(_ t: LoudlyNC<String>) {}
125+
public struct NoCopyPls: ~Copyable {}
126+
public func substCopyable(_ t: String?) {}
127+
public func substGenericCopyable<T>(_ t: T?) {}
128+
public func substNC(_ t: borrowing NoCopyPls?) {}
129+
public func substGenericNC<T: ~Copyable>(_ t: borrowing T?) {}

test/ModuleInterface/features.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,9 @@ public class OldSchool2: MP {
9090
// CHECK: public struct UsesRP {
9191
public struct UsesRP {
9292
// CHECK: #if compiler(>=5.3) && $RethrowsProtocol
93-
// CHECK-NEXT: #if $NoncopyableGenerics
9493
// CHECK-NEXT: public var value: (any FeatureTest.RP)? {
9594
// CHECK-NOT: #if compiler(>=5.3) && $RethrowsProtocol
9695
// CHECK: get
97-
// CHECK: #else
98-
// CHECK-NEXT: public var value: (any FeatureTest.RP)? {
99-
// CHECK-NEXT: get
10096
public var value: RP? {
10197
nil
10298
}

test/ModuleInterface/noncopyable_generics.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,39 @@ import NoncopyableGenerics_Misc
165165
// CHECK-MISC-NEXT: public func suppressesNoncopyableGenerics<T>(_ t: borrowing T) where T : ~Copyable
166166
// CHECK-MISC-NEXT: #endif
167167

168+
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
169+
// CHECK-MISC-NEXT: public struct LoudlyNC<T> where T : ~Copyable {
170+
// CHECK-MISC-NEXT: }
171+
// CHECK-MISC-NEXT: #endif
172+
// CHECK-MISC-NEXT: public func _indexHumongousDonuts<TTT, T>(_ aggregate: Swift.UnsafePointer<TTT>, _ index: Swift.Int) -> T
173+
// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics
174+
// CHECK-MISC-NEXT: public func referToLoud(_ t: {{.*}}.LoudlyNC<Swift.String>)
175+
// CHECK-MISC-NEXT: #else
176+
// CHECK-MISC-NEXT: public func referToLoud(_ t: {{.*}}.LoudlyNC<Swift.String>)
177+
// CHECK-MISC-NEXT: #endif
178+
// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics
179+
// CHECK-MISC-NEXT: public func referToLoudProperGuarding(_ t: {{.*}}.LoudlyNC<Swift.String>)
180+
// CHECK-MISC-NEXT: #endif
181+
// CHECK-MISC-NEXT: public struct NoCopyPls : ~Swift.Copyable {
182+
// CHECK-MISC-NEXT: }
183+
// CHECK-MISC-NEXT: public func substCopyable(_ t: Swift.String?)
184+
// CHECK-MISC-NEXT: public func substGenericCopyable<T>(_ t: T?)
185+
186+
// NOTE: we really shouldn't be emitting the else branch for the two funcs
187+
// below, since the suppressed version isn't valid. We don't have a good way of
188+
// fixing that right now, either.
189+
190+
// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics
191+
// CHECK-MISC-NEXT: public func substNC(_ t: borrowing {{.*}}.NoCopyPls?)
192+
// CHECK-MISC-NEXT: #else
193+
// CHECK-MISC-NEXT: public func substNC(_ t: borrowing {{.*}}.NoCopyPls?)
194+
// CHECK-MISC-NEXT: #endif
195+
// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics
196+
// CHECK-MISC-NEXT: public func substGenericNC<T>(_ t: borrowing T?) where T : ~Copyable
197+
// CHECK-MISC-NEXT: #else
198+
// CHECK-MISC-NEXT: public func substGenericNC<T>(_ t: borrowing T?)
199+
// CHECK-MISC-NEXT: #endif
200+
168201

169202
import Swiftskell
170203

0 commit comments

Comments
 (0)