Skip to content

Commit c62eb2e

Browse files
committed
[swift_newtype] Handle un-audited CF types
Special case logic for CF types, which may be coming in as unmanaged. In this case, we will use audit information if present to import with the new type, otherwise we have to fall back to Unmanaged<CF...>. We still import global variables that must be unmanaged onto the new type, though they keep their unmanaged types. This helps to consolidate the definitions, as well as make future migration easier if they get audited. Test cases included.
1 parent b5d29b5 commit c62eb2e

File tree

5 files changed

+101
-6
lines changed

5 files changed

+101
-6
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,16 @@ namespace {
13341334
isInSystemModule(dc), decl->getUnderlyingType()->isBlockPointerType(),
13351335
OTK_None);
13361336

1337+
// If the type is Unmanaged, that is it is not CF ARC audited,
1338+
// we will store the underlying type and leave it up to the use site
1339+
// to determine whether to use this new_type, or an Unmanaged<CF...> type.
1340+
if (auto genericType = storedUnderlyingType->getAs<BoundGenericType>()) {
1341+
if (genericType->getDecl() == Impl.SwiftContext.getUnmanagedDecl()) {
1342+
assert(genericType->getGenericArgs().size() == 1 && "other args?");
1343+
storedUnderlyingType = genericType->getGenericArgs()[0];
1344+
}
1345+
}
1346+
13371347
// Find a bridged type, which may be different
13381348
auto computedPropertyUnderlyingType = Impl.importType(
13391349
decl->getUnderlyingType(), ImportTypeKind::Property,

lib/ClangImporter/ImportType.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ namespace {
9090

9191
/// The source type created a new Swift type, using swift_newtype
9292
SwiftNewtype,
93+
94+
/// The source type created a new Swift type, using swift_newtype, of an
95+
/// original underlying CFPointer. This distinction is necessary to
96+
/// trigger audit-checking.
97+
SwiftNewtypeFromCFPointer,
9398
};
9499

95100
ImportHintKind Kind;
@@ -121,7 +126,6 @@ namespace {
121126
case ImportHint::NSUInteger:
122127
case ImportHint::Reference:
123128
case ImportHint::Void:
124-
case ImportHint::SwiftNewtype:
125129
return false;
126130

127131
case ImportHint::Block:
@@ -130,6 +134,8 @@ namespace {
130134
case ImportHint::ObjCPointer:
131135
case ImportHint::CFunctionPointer:
132136
case ImportHint::OtherPointer:
137+
case ImportHint::SwiftNewtype:
138+
case ImportHint::SwiftNewtypeFromCFPointer:
133139
return true;
134140
}
135141
}
@@ -555,8 +561,10 @@ namespace {
555561
ImportHint hint = ImportHint::None;
556562

557563
if (Impl.getSwiftNewtypeAttr(type->getDecl(), /*useSwift2Name=*/false)) {
558-
hint = ImportHint::SwiftNewtype;
559-
564+
if (ClangImporter::Implementation::isCFTypeDecl(type->getDecl()))
565+
hint = ImportHint::SwiftNewtypeFromCFPointer;
566+
else
567+
hint = ImportHint::SwiftNewtype;
560568
// For certain special typedefs, we don't want to use the imported type.
561569
} else if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
562570
switch (specialKind.getValue()) {
@@ -997,6 +1005,21 @@ static bool isNSString(Type type) {
9971005
return false;
9981006
}
9991007

1008+
static Type findNewtypeUnderlyingType(Type newtype) {
1009+
auto structDecl = newtype->getStructOrBoundGenericStruct();
1010+
assert(structDecl && structDecl->getClangNode() &&
1011+
structDecl->getClangNode().getAsDecl() &&
1012+
structDecl->getClangNode()
1013+
.castAsDecl()
1014+
->getAttr<clang::SwiftNewtypeAttr>() &&
1015+
"Called on a non-swift_newtype decl");
1016+
for (auto member : structDecl->getMembers())
1017+
if (auto varDecl = dyn_cast<VarDecl>(member))
1018+
if (varDecl->getName().str() == "rawValue")
1019+
return varDecl->getType();
1020+
assert(0 && "no rawValue variable member");
1021+
}
1022+
10001023
static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
10011024
clang::QualType clangType,
10021025
Type importedType,
@@ -1178,6 +1201,15 @@ static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
11781201
importedType = getUnmanagedType(impl, importedType);
11791202
}
11801203

1204+
// For types we import as new types in Swift, if the use is CF un-audited,
1205+
// then we have to for it to be unmanaged
1206+
if (hint == ImportHint::SwiftNewtypeFromCFPointer &&
1207+
!isCFAudited(importKind)) {
1208+
auto underlyingType = findNewtypeUnderlyingType(importedType);
1209+
if (underlyingType)
1210+
importedType = getUnmanagedType(impl, underlyingType);
1211+
}
1212+
11811213
// If we have a bridged Objective-C type and we are allowed to
11821214
// bridge, do so.
11831215
if (hint == ImportHint::ObjCBridged && canBridgeTypes(importKind) &&

test/IDE/Inputs/custom-modules/Newtype.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,16 @@ extern const NSString *Notification;
5050
extern const NSString *kSNNotification
5151
__attribute((swift_name("swiftNamedNotification")));
5252

53+
// Test CFStringRef
54+
typedef CFStringRef CFNewType __attribute((swift_newtype(struct)));
55+
56+
// CF audited
57+
_Pragma("clang arc_cf_code_audited begin")
58+
extern const CFNewType MyCFNewTypeValue;
59+
CFNewType FooAudited(void);
60+
_Pragma("clang arc_cf_code_audited end")
61+
extern const CFNewType MyCFNewTypeValueUnauditedButConst;
62+
63+
// un-audited CFStringRef
64+
extern CFNewType MyCFNewTypeValueUnaudited;
65+
CFNewType FooUnaudited(void);

test/IDE/newtype.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RUN: %build-clang-importer-objc-overlays
44
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk-nosource) -I %t -I %S/Inputs/custom-modules -enable-swift-newtype -print-module -source-filename %s -module-to-print=Newtype > %t.printed.A.txt
55
// RUN: FileCheck %s -check-prefix=PRINT -strict-whitespace < %t.printed.A.txt
6+
// RUN: %target-parse-verify-swift -sdk %clang-importer-sdk -I %S/Inputs/custom-modules -enable-swift-newtype -I %t
67
// REQUIRES: objc_interop
78

89
// PRINT-LABEL: struct ErrorDomain : RawRepresentable, _SwiftNewtypeWrapper, Equatable, Hashable, Comparable, _ObjectiveCBridgeable {
@@ -48,6 +49,7 @@
4849
// PRINT-NEXT: static let PI: MyFloat
4950
// PRINT-NEXT: static let version: MyFloat
5051
// PRINT-NEXT: }
52+
//
5153
// PRINT-LABEL: extension NSURLResourceKey {
5254
// PRINT-NEXT: static let isRegularFileKey: NSURLResourceKey
5355
// PRINT-NEXT: static let isDirectoryKey: NSURLResourceKey
@@ -61,8 +63,18 @@
6163
// PRINT-NEXT: let kNotification: String
6264
// PRINT-NEXT: let Notification: String
6365
// PRINT-NEXT: let swiftNamedNotification: String
64-
65-
// RUN: %target-parse-verify-swift -sdk %clang-importer-sdk -I %S/Inputs/custom-modules -enable-swift-newtype -I %t
66+
//
67+
// PRINT-LABEL: struct CFNewType : RawRepresentable, _SwiftNewtypeWrapper {
68+
// PRINT-NEXT: init(rawValue: CFString)
69+
// PRINT-NEXT: let rawValue: CFString
70+
// PRINT-NEXT: }
71+
// PRINT-NEXT: extension CFNewType {
72+
// PRINT-NEXT: static let MyCFNewTypeValue: CFNewType
73+
// PRINT-NEXT: static let MyCFNewTypeValueUnauditedButConst: CFNewType
74+
// PRINT-NEXT: static var MyCFNewTypeValueUnaudited: Unmanaged<CFString>
75+
// PRINT-NEXT: }
76+
// PRINT-NEXT: func FooAudited() -> CFNewType
77+
// PRINT-NEXT: func FooUnaudited() -> Unmanaged<CFString>
6678
import Newtype
6779

6880
func tests() {
@@ -73,7 +85,7 @@ func tests() {
7385
fooErr.process()
7486
Foo().process() // expected-error{{value of type 'Foo' has no member 'process'}}
7587

76-
let thirdEnum = ClosedEnum.thirdEntry
88+
let thirdEnum = ClosedEnum.thirdEntry!
7789
thirdEnum.process()
7890
// expected-error@-1{{value of type 'ClosedEnum' has no member 'process'}}
7991

@@ -82,6 +94,10 @@ func tests() {
8294

8395
let _ = NSNotificationName.Foo
8496
let _ = NSNotificationName.bar
97+
let _ : CFNewType = CFNewType.MyCFNewTypeValue
98+
let _ : CFNewType = CFNewType.MyCFNewTypeValueUnauditedButConst
99+
let _ : CFNewType = CFNewType.MyCFNewTypeValueUnaudited
100+
// expected-error@-1{{cannot convert value of type 'Unmanaged<CFString>!' to specified type 'CFNewType'}}
85101
}
86102

87103
func acceptSwiftNewtypeWrapper<T : _SwiftNewtypeWrapper>(_ t: T) { }

test/IRGen/newtype.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,27 @@ public func getGlobalNotification(_ x: Int) -> String {
3333
}
3434
// CHECK: ret
3535
}
36+
37+
// CHECK-LABEL: _TF7newtype17getCFNewTypeValueFT6useVarSb_VSC9CFNewType
38+
public func getCFNewTypeValue(useVar: Bool) -> CFNewType {
39+
if (useVar) {
40+
return CFNewType.MyCFNewTypeValue
41+
// CHECK: load {{.*}} @MyCFNewTypeValue
42+
} else {
43+
return FooAudited()
44+
// CHECK: call {{.*}} @FooAudited()
45+
}
46+
// CHECK: ret
47+
}
48+
49+
// CHECK-LABEL: _TF7newtype21getUnmanagedCFNewTypeFT6useVarSb_GVs9UnmanagedCSo8CFString_
50+
public func getUnmanagedCFNewType(useVar: Bool) -> Unmanaged<CFString> {
51+
if (useVar) {
52+
return CFNewType.MyCFNewTypeValueUnaudited
53+
// CHECK: load {{.*}} @MyCFNewTypeValueUnaudited
54+
} else {
55+
return FooUnaudited()
56+
// CHECK: call {{.*}} @FooUnaudited()
57+
}
58+
// CHECK: ret
59+
}

0 commit comments

Comments
 (0)