diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 6fd2824cec83d..6675e6382673e 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -506,6 +506,21 @@ UNINTERESTING_FEATURE(Embedded) UNINTERESTING_FEATURE(Volatile) UNINTERESTING_FEATURE(SuppressedAssociatedTypes) +static bool disallowFeatureSuppression(StringRef featureName, Decl *decl); + +static bool allBoundTypesAreCopyable(Type type, DeclContext *context) { + assert(type->getAnyNominal()); + auto bgt = type->getAs(); + if (!bgt) + return false; // nothing is bound. + + for (auto argInterfaceTy : bgt->getGenericArgs()) + if (context->mapTypeIntoContext(argInterfaceTy)->isNoncopyable()) + return false; + + return true; +} + static bool usesFeatureNoncopyableGenerics(Decl *decl) { if (decl->getAttrs().hasAttribute()) return true; @@ -523,15 +538,29 @@ static bool usesFeatureNoncopyableGenerics(Decl *decl) { if (isa(valueDecl) || isa(valueDecl)) { - if (valueDecl->getInterfaceType().findIf([&](Type type) -> bool { - if (auto *nominalDecl = type->getAnyNominal()) { - if (isa(nominalDecl)) - return usesFeatureNoncopyableGenerics(nominalDecl); - } - return false; - })) { + auto *context = decl->getInnermostDeclContext(); + auto usesFeature = valueDecl->getInterfaceType().findIf( + [&](Type type) -> bool { + auto *nominalDecl = type->getAnyNominal(); + if (!nominalDecl || !isa(nominalDecl)) + return false; + + if (!usesFeatureNoncopyableGenerics(nominalDecl)) + return false; + + // If we only _refer_ to a TypeDecl that uses NoncopyableGenerics, + // and a suppressed version of that decl is in the interface, then we're + // only referring to the un-suppressed version if any of the bound types + // are noncopyable. (rdar://127389991) + if (!disallowFeatureSuppression("NoncopyableGenerics", nominalDecl) + && allBoundTypesAreCopyable(type, context)) { + return false; + } + + return true; + }); + if (usesFeature) return true; - } } } diff --git a/test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift b/test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift index 07077d4234ed1..53362d86e1596 100644 --- a/test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift +++ b/test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift @@ -112,3 +112,18 @@ public func borrowsNoncopyable(_ t: borrowing T) {} @_disallowFeatureSuppression(NoncopyableGenerics) public func suppressesNoncopyableGenerics(_ t: borrowing T) {} + +// coverage for rdar://127389991 +@_disallowFeatureSuppression(NoncopyableGenerics) +public struct LoudlyNC {} +public func _indexHumongousDonuts(_ aggregate: UnsafePointer, _ index: Int) -> T { + return UnsafeRawPointer(aggregate).load( + fromByteOffset: index * MemoryLayout.stride, as: T.self) +} +public func referToLoud(_ t: LoudlyNC) {} +@_disallowFeatureSuppression(NoncopyableGenerics) public func referToLoudProperGuarding(_ t: LoudlyNC) {} +public struct NoCopyPls: ~Copyable {} +public func substCopyable(_ t: String?) {} +public func substGenericCopyable(_ t: T?) {} +public func substNC(_ t: borrowing NoCopyPls?) {} +public func substGenericNC(_ t: borrowing T?) {} diff --git a/test/ModuleInterface/features.swift b/test/ModuleInterface/features.swift index a6254473a6d2d..ae02eb31ad722 100644 --- a/test/ModuleInterface/features.swift +++ b/test/ModuleInterface/features.swift @@ -90,13 +90,9 @@ public class OldSchool2: MP { // CHECK: public struct UsesRP { public struct UsesRP { // CHECK: #if compiler(>=5.3) && $RethrowsProtocol - // CHECK-NEXT: #if $NoncopyableGenerics // CHECK-NEXT: public var value: (any FeatureTest.RP)? { // CHECK-NOT: #if compiler(>=5.3) && $RethrowsProtocol // CHECK: get - // CHECK: #else - // CHECK-NEXT: public var value: (any FeatureTest.RP)? { - // CHECK-NEXT: get public var value: RP? { nil } diff --git a/test/ModuleInterface/noncopyable_generics.swift b/test/ModuleInterface/noncopyable_generics.swift index 2425eb2dd585b..7cba459dec50c 100644 --- a/test/ModuleInterface/noncopyable_generics.swift +++ b/test/ModuleInterface/noncopyable_generics.swift @@ -165,6 +165,39 @@ import NoncopyableGenerics_Misc // CHECK-MISC-NEXT: public func suppressesNoncopyableGenerics(_ t: borrowing T) where T : ~Copyable // CHECK-MISC-NEXT: #endif +// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics +// CHECK-MISC-NEXT: public struct LoudlyNC where T : ~Copyable { +// CHECK-MISC-NEXT: } +// CHECK-MISC-NEXT: #endif +// CHECK-MISC-NEXT: public func _indexHumongousDonuts(_ aggregate: Swift.UnsafePointer, _ index: Swift.Int) -> T +// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics +// CHECK-MISC-NEXT: public func referToLoud(_ t: {{.*}}.LoudlyNC) +// CHECK-MISC-NEXT: #else +// CHECK-MISC-NEXT: public func referToLoud(_ t: {{.*}}.LoudlyNC) +// CHECK-MISC-NEXT: #endif +// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics +// CHECK-MISC-NEXT: public func referToLoudProperGuarding(_ t: {{.*}}.LoudlyNC) +// CHECK-MISC-NEXT: #endif +// CHECK-MISC-NEXT: public struct NoCopyPls : ~Swift.Copyable { +// CHECK-MISC-NEXT: } +// CHECK-MISC-NEXT: public func substCopyable(_ t: Swift.String?) +// CHECK-MISC-NEXT: public func substGenericCopyable(_ t: T?) + +// NOTE: we really shouldn't be emitting the else branch for the two funcs +// below, since the suppressed version isn't valid. We don't have a good way of +// fixing that right now, either. + +// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics +// CHECK-MISC-NEXT: public func substNC(_ t: borrowing {{.*}}.NoCopyPls?) +// CHECK-MISC-NEXT: #else +// CHECK-MISC-NEXT: public func substNC(_ t: borrowing {{.*}}.NoCopyPls?) +// CHECK-MISC-NEXT: #endif +// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics +// CHECK-MISC-NEXT: public func substGenericNC(_ t: borrowing T?) where T : ~Copyable +// CHECK-MISC-NEXT: #else +// CHECK-MISC-NEXT: public func substGenericNC(_ t: borrowing T?) +// CHECK-MISC-NEXT: #endif + import Swiftskell