Skip to content

Commit f0f91ee

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
1 parent e805679 commit f0f91ee

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
@@ -506,6 +506,21 @@ UNINTERESTING_FEATURE(Embedded)
506506
UNINTERESTING_FEATURE(Volatile)
507507
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
508508

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

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

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)