Skip to content

Commit 08b6c5f

Browse files
authored
[ClangImporter] Add support for 'SwiftImportAsNonGeneric' in API notes (swiftlang#6962)
Generic Objective-C classes with this annotation will be imported as non-generic in Swift. The Swift 3 behavior hardcoded a certain set of class /hierarchies/ as permanently non-generic, and this is preserved in Swift 3 mode. Actually using this API note in a versioned way (as opposed to just marking the class non-generic in all language versions) will cause horrible source compatibility problems in the mix-and-match cases, where Swift 3 code presents a non-generic type that Swift 4 expects to be generic or vice versa. Fixes for this will come later; right now it's more important to add support for the feature at all. To avoid unwanted changes in Swift 4, this commit also adds API notes to make any existing classes in the previously-hardcoded set continue to import as non-generic even in Swift 4. The difference is that /subclasses/ of these classes may come in as generic. (If we want to make a change here, that can be a separate commit.) rdar://problem/31226414 (Swift side of rdar://problem/28455962)
1 parent 85915d9 commit 08b6c5f

File tree

9 files changed

+75
-21
lines changed

9 files changed

+75
-21
lines changed

apinotes/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ set(SWIFT_API_NOTES_INPUTS
3838
QuickLook
3939
SafariServices
4040
SceneKit
41+
ScriptingBridge
4142
SpriteKit
4243
StoreKit
4344
TVMLKit

apinotes/Foundation.apinotes

+25-8
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ Classes:
55
SwiftBridge: AffineTransform
66
- Name: NSArray
77
SwiftBridge: Swift.Array
8+
SwiftImportAsNonGeneric: true
89
Methods:
910
- Selector: 'pathsMatchingExtensions:'
1011
SwiftName: pathsMatchingExtensions(_:)
1112
MethodKind: Instance
1213
- Selector: 'filteredArrayUsingPredicate:'
1314
SwiftName: filtered(using:)
1415
MethodKind: Instance
16+
- Name: NSMutableArray
17+
SwiftImportAsNonGeneric: true
18+
Methods:
19+
- Selector: 'removeObjectIdenticalTo:inRange:'
20+
SwiftName: removeObject(identicalTo:in:)
21+
MethodKind: Instance
22+
- Selector: 'removeObjectIdenticalTo:'
23+
SwiftName: removeObject(identicalTo:)
24+
MethodKind: Instance
1525
- Name: NSCachedURLResponse
1626
SwiftName: CachedURLResponse
1727
- Name: NSCharacterSet
@@ -51,6 +61,8 @@ Classes:
5161
- Selector: 'hasMemberInPlane:'
5262
SwiftName: hasMemberInPlane(_:)
5363
MethodKind: Instance
64+
- Name: NSCountedSet
65+
SwiftImportAsNonGeneric: true
5466
- Name: NSData
5567
SwiftBridge: Data
5668
Methods:
@@ -85,6 +97,8 @@ Classes:
8597
SwiftBridge: DateComponents
8698
- Name: NSDateInterval
8799
SwiftBridge: DateInterval
100+
- Name: NSEnumerator
101+
SwiftImportAsNonGeneric: true
88102
- Name: NSError
89103
SwiftBridge: Swift.Error
90104
Methods:
@@ -93,12 +107,18 @@ Classes:
93107
MethodKind: Class
94108
- Name: NSDictionary
95109
SwiftBridge: Swift.Dictionary
110+
SwiftImportAsNonGeneric: true
111+
- Name: NSMutableDictionary
112+
SwiftImportAsNonGeneric: true
96113
- Name: NSSet
97114
SwiftBridge: Swift.Set
115+
SwiftImportAsNonGeneric: true
98116
Methods:
99117
- Selector: 'filteredSetUsingPredicate:'
100118
SwiftName: filtered(using:)
101119
MethodKind: Instance
120+
- Name: NSMutableSet
121+
SwiftImportAsNonGeneric: true
102122
- Name: NSString
103123
SwiftBridge: Swift.String
104124
Methods:
@@ -313,14 +333,7 @@ Classes:
313333
MethodKind: Instance
314334
- Name: NSMeasurement
315335
SwiftBridge: Measurement
316-
- Name: NSMutableArray
317-
Methods:
318-
- Selector: 'removeObjectIdenticalTo:inRange:'
319-
SwiftName: removeObject(identicalTo:in:)
320-
MethodKind: Instance
321-
- Selector: 'removeObjectIdenticalTo:'
322-
SwiftName: removeObject(identicalTo:)
323-
MethodKind: Instance
336+
SwiftImportAsNonGeneric: true
324337
- Name: NSMutableData
325338
Methods:
326339
- Selector: 'appendBytes:length:'
@@ -445,6 +458,7 @@ Classes:
445458
- Name: unsignedIntegerValue
446459
SwiftName: uintValue
447460
- Name: NSOrderedSet
461+
SwiftImportAsNonGeneric: true
448462
Methods:
449463
- Selector: 'enumerateObjectsWithOptions:usingBlock:'
450464
SwiftName: enumerateObjects(options:using:)
@@ -470,6 +484,8 @@ Classes:
470484
- Selector: 'indexOfObjectWithOptions:passingTest:'
471485
SwiftName: index(_:ofObjectPassingTest:)
472486
MethodKind: Instance
487+
- Name: NSMutableOrderedSet
488+
SwiftImportAsNonGeneric: true
473489
- Name: NSTask
474490
SwiftName: Process
475491
Methods:
@@ -932,6 +948,7 @@ Classes:
932948
SwiftName: ValueTransformer
933949
- Name: NSDirectoryEnumerator
934950
SwiftName: FileManager.DirectoryEnumerator
951+
SwiftImportAsNonGeneric: true
935952
- Name: NSDimension
936953
SwiftName: Dimension
937954
- Name: NSUnit

apinotes/ScriptingBridge.apinotes

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
Name: ScriptingBridge
3+
Classes:
4+
- Name: SBElementArray
5+
SwiftImportAsNonGeneric: true

lib/ClangImporter/ImportDecl.cpp

+18-9
Original file line numberDiff line numberDiff line change
@@ -1713,16 +1713,25 @@ getAccessorPropertyType(const clang::FunctionDecl *accessor, bool isSetter,
17131713
/// Whether we should suppress importing the Objective-C generic type params
17141714
/// of this class as Swift generic type params.
17151715
static bool
1716-
shouldSuppressGenericParamsImport(const clang::ObjCInterfaceDecl *decl) {
1717-
while (decl) {
1718-
StringRef name = decl->getName();
1719-
if (name == "NSArray" || name == "NSDictionary" || name == "NSSet" ||
1720-
name == "NSOrderedSet" || name == "NSEnumerator" ||
1721-
name == "NSMeasurement") {
1722-
return true;
1716+
shouldSuppressGenericParamsImport(const LangOptions &langOpts,
1717+
const clang::ObjCInterfaceDecl *decl) {
1718+
if (decl->hasAttr<clang::SwiftImportAsNonGenericAttr>())
1719+
return true;
1720+
1721+
if (langOpts.isSwiftVersion3()) {
1722+
// In Swift 3 we used a hardcoded list of declarations, and made all of
1723+
// their subclasses drop their generic parameters when imported.
1724+
while (decl) {
1725+
StringRef name = decl->getName();
1726+
if (name == "NSArray" || name == "NSDictionary" || name == "NSSet" ||
1727+
name == "NSOrderedSet" || name == "NSEnumerator" ||
1728+
name == "NSMeasurement") {
1729+
return true;
1730+
}
1731+
decl = decl->getSuperClass();
17231732
}
1724-
decl = decl->getSuperClass();
17251733
}
1734+
17261735
return false;
17271736
}
17281737

@@ -6149,7 +6158,7 @@ Optional<GenericParamList *> SwiftDeclConverter::importObjCGenericParams(
61496158
if (!typeParamList) {
61506159
return nullptr;
61516160
}
6152-
if (shouldSuppressGenericParamsImport(decl)) {
6161+
if (shouldSuppressGenericParamsImport(Impl.SwiftContext.LangOpts, decl)) {
61536162
return nullptr;
61546163
}
61556164
assert(typeParamList->size() > 0);

test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.apinotes

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ SwiftVersions:
8484
- Name: accessorsOnlyRenamedRetypedClass
8585
PropertyKind: Class
8686
SwiftImportAsAccessors: true
87+
- Name: NewlyGenericSub
88+
SwiftImportAsNonGeneric: true
8789
Protocols:
8890
- Name: ProtoWithVersionedUnavailableMember
8991
Methods:

test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h

+5
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@ __attribute__((objc_root_class))
1515
-(nonnull id)methodWithA:(nonnull id)a;
1616
@end
1717

18+
__attribute__((objc_root_class))
19+
@interface Base
20+
@end
21+
1822
#endif // __OBJC__
1923

24+
#import <APINotesFrameworkTest/Classes.h>
2025
#import <APINotesFrameworkTest/ImportAsMember.h>
2126
#import <APINotesFrameworkTest/Properties.h>
2227
#import <APINotesFrameworkTest/Protocols.h>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifdef __OBJC__
2+
#pragma clang assume_nonnull begin
3+
4+
@interface NewlyGenericSub<Element> : Base
5+
+ (Element)defaultElement;
6+
@end
7+
8+
#pragma clang assume_nonnull end
9+
#endif // __OBJC__

test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/Properties.h

-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
#ifdef __OBJC__
22
#pragma clang assume_nonnull begin
33

4-
__attribute__((objc_root_class))
5-
@interface Base
6-
@end
7-
84
@interface TestProperties: Base
95
@property (nonatomic, readwrite, retain) id accessorsOnly;
106
@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClass;

test/APINotes/versioned-objc.swift

+10
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,14 @@ class ProtoWithVersionedUnavailableMemberImpl: ProtoWithVersionedUnavailableMemb
1313
func requirement() -> Any? { return nil }
1414
}
1515

16+
func testNonGeneric() {
17+
// CHECK-DIAGS-3:[[@LINE+1]]:{{[0-9]+}}: error: cannot convert value of type 'Any' to specified type 'Int'
18+
let _: Int = NewlyGenericSub.defaultElement()
19+
// CHECK-DIAGS-4:[[@LINE-1]]:{{[0-9]+}}: error: generic parameter 'Element' could not be inferred
20+
21+
// CHECK-DIAGS-3:[[@LINE+1]]:{{[0-9]+}}: error: cannot specialize non-generic type 'NewlyGenericSub'
22+
let _: Int = NewlyGenericSub<Base>.defaultElement()
23+
// CHECK-DIAGS-4:[[@LINE-1]]:{{[0-9]+}}: error: cannot convert value of type 'Base' to specified type 'Int'
24+
}
25+
1626
let unrelatedDiagnostic: Int = nil

0 commit comments

Comments
 (0)