Skip to content

Commit d93f0d1

Browse files
committed
Merge pull request #2454 from milseman/newtype
[swift_new_type] Audited and un-audited CF reference types
2 parents f952d2e + c62eb2e commit d93f0d1

File tree

5 files changed

+210
-104
lines changed

5 files changed

+210
-104
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 119 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,114 @@ namespace {
13071307
return alias;
13081308
}
13091309

1310+
/// Create a swift_newtype struct corresponding to a typedef. Returns
1311+
/// nullptr if unable.
1312+
Decl *importSwiftNewtype(const clang::TypedefNameDecl *decl,
1313+
clang::SwiftNewtypeAttr *newtypeAttr,
1314+
DeclContext *dc, Identifier name) {
1315+
switch (newtypeAttr->getNewtypeKind()) {
1316+
case clang::SwiftNewtypeAttr::NK_Enum:
1317+
// TODO: import as closed enum instead
1318+
// For now, fall through and treat as a struct
1319+
case clang::SwiftNewtypeAttr::NK_Struct:
1320+
break;
1321+
// No other cases yet
1322+
}
1323+
1324+
auto &cxt = Impl.SwiftContext;
1325+
auto Loc = Impl.importSourceLoc(decl->getLocation());
1326+
1327+
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
1328+
decl, Loc, name, Loc, None, nullptr, dc);
1329+
structDecl->computeType();
1330+
1331+
// Import the type of the underlying storage
1332+
auto storedUnderlyingType = Impl.importType(
1333+
decl->getUnderlyingType(), ImportTypeKind::Value,
1334+
isInSystemModule(dc), decl->getUnderlyingType()->isBlockPointerType(),
1335+
OTK_None);
1336+
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+
1347+
// Find a bridged type, which may be different
1348+
auto computedPropertyUnderlyingType = Impl.importType(
1349+
decl->getUnderlyingType(), ImportTypeKind::Property,
1350+
isInSystemModule(dc), decl->getUnderlyingType()->isBlockPointerType(),
1351+
OTK_None);
1352+
1353+
bool isBridged =
1354+
!storedUnderlyingType->isEqual(computedPropertyUnderlyingType);
1355+
1356+
// Determine the set of protocols to which the synthesized
1357+
// type will conform.
1358+
SmallVector<ProtocolDecl *, 4> protocols;
1359+
SmallVector<KnownProtocolKind, 4> synthesizedProtocols;
1360+
1361+
// Local function to add a known protocol.
1362+
auto addKnown = [&](KnownProtocolKind kind) {
1363+
if (auto proto = cxt.getProtocol(kind)) {
1364+
protocols.push_back(proto);
1365+
synthesizedProtocols.push_back(kind);
1366+
}
1367+
};
1368+
1369+
// Add conformances that are always available.
1370+
addKnown(KnownProtocolKind::RawRepresentable);
1371+
addKnown(KnownProtocolKind::SwiftNewtypeWrapper);
1372+
1373+
// Local function to add a known protocol only when the
1374+
// underlying type conforms to it.
1375+
auto computedNominal = computedPropertyUnderlyingType->getAnyNominal();
1376+
auto transferKnown = [&](KnownProtocolKind kind) {
1377+
if (!computedNominal)
1378+
return;
1379+
1380+
auto proto = cxt.getProtocol(kind);
1381+
if (!proto)
1382+
return;
1383+
1384+
SmallVector<ProtocolConformance *, 1> conformances;
1385+
if (computedNominal->lookupConformance(
1386+
computedNominal->getParentModule(), proto, conformances)) {
1387+
protocols.push_back(proto);
1388+
synthesizedProtocols.push_back(kind);
1389+
}
1390+
};
1391+
1392+
// Transfer conformances. Each of these needs a forwarding
1393+
// implementation in the standard library.
1394+
transferKnown(KnownProtocolKind::Equatable);
1395+
transferKnown(KnownProtocolKind::Hashable);
1396+
transferKnown(KnownProtocolKind::Comparable);
1397+
transferKnown(KnownProtocolKind::ObjectiveCBridgeable);
1398+
1399+
if (!isBridged) {
1400+
// Simple, our stored type is equivalent to our computed
1401+
// type.
1402+
makeStructRawValued(structDecl, storedUnderlyingType,
1403+
synthesizedProtocols, protocols);
1404+
} else {
1405+
// We need to make a stored rawValue or storage type, and a
1406+
// computed one of bridged type.
1407+
makeStructRawValuedWithBridge(structDecl, storedUnderlyingType,
1408+
computedPropertyUnderlyingType,
1409+
synthesizedProtocols, protocols);
1410+
}
1411+
1412+
Impl.ImportedDecls[{decl->getCanonicalDecl(), useSwift2Name}] =
1413+
structDecl;
1414+
Impl.registerExternalDecl(structDecl);
1415+
return structDecl;
1416+
}
1417+
13101418
Decl *VisitTypedefNameDecl(const clang::TypedefNameDecl *Decl) {
13111419
Optional<ImportedName> swift3Name;
13121420
auto importedName = importFullName(Decl, swift3Name);
@@ -1390,6 +1498,13 @@ namespace {
13901498
if (!underlying)
13911499
return nullptr;
13921500

1501+
// Check for a newtype
1502+
if (auto newtypeAttr =
1503+
Impl.getSwiftNewtypeAttr(Decl, useSwift2Name))
1504+
if (auto newtype =
1505+
importSwiftNewtype(Decl, newtypeAttr, DC, Name))
1506+
return newtype;
1507+
13931508
// Create a typealias for this CF typedef.
13941509
TypeAliasDecl *typealias = nullptr;
13951510
typealias = Impl.createDeclWithClangNode<TypeAliasDecl>(
@@ -1467,104 +1582,10 @@ namespace {
14671582
return nullptr;
14681583

14691584
// Check for swift_newtype
1470-
if (!SwiftType) {
1471-
if (auto newtypeAttr = Impl.getSwiftNewtypeAttr(Decl, useSwift2Name)) {
1472-
switch (newtypeAttr->getNewtypeKind()) {
1473-
case clang::SwiftNewtypeAttr::NK_Enum:
1474-
// TODO: import as closed enum instead
1475-
1476-
// For now, fall through and treat as a struct
1477-
case clang::SwiftNewtypeAttr::NK_Struct: {
1478-
1479-
auto &cxt = Impl.SwiftContext;
1480-
auto Loc = Impl.importSourceLoc(Decl->getLocation());
1481-
1482-
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
1483-
Decl, Loc, Name, Loc, None, nullptr, DC);
1484-
structDecl->computeType();
1485-
1486-
// Import the type of the underlying storage
1487-
auto storedUnderlyingType = Impl.importType(
1488-
Decl->getUnderlyingType(), ImportTypeKind::Value,
1489-
isInSystemModule(DC),
1490-
Decl->getUnderlyingType()->isBlockPointerType(),
1491-
OTK_None);
1492-
1493-
// Find a bridged type, which may be different
1494-
auto computedPropertyUnderlyingType = Impl.importType(
1495-
Decl->getUnderlyingType(), ImportTypeKind::Property,
1496-
isInSystemModule(DC),
1497-
Decl->getUnderlyingType()->isBlockPointerType(),
1498-
OTK_None);
1499-
1500-
bool isBridged =
1501-
!storedUnderlyingType->isEqual(computedPropertyUnderlyingType);
1502-
1503-
// Determine the set of protocols to which the synthesized
1504-
// type will conform.
1505-
SmallVector<ProtocolDecl *, 4> protocols;
1506-
SmallVector<KnownProtocolKind, 4> synthesizedProtocols;
1507-
1508-
// Local function to add a known protocol.
1509-
auto addKnown = [&](KnownProtocolKind kind) {
1510-
if (auto proto = cxt.getProtocol(kind)) {
1511-
protocols.push_back(proto);
1512-
synthesizedProtocols.push_back(kind);
1513-
}
1514-
};
1515-
1516-
// Add conformances that are always available.
1517-
addKnown(KnownProtocolKind::RawRepresentable);
1518-
addKnown(KnownProtocolKind::SwiftNewtypeWrapper);
1519-
1520-
// Local function to add a known protocol only when the
1521-
// underlying type conforms to it.
1522-
auto computedNominal =
1523-
computedPropertyUnderlyingType->getAnyNominal();
1524-
auto transferKnown = [&](KnownProtocolKind kind) {
1525-
if (!computedNominal) return;
1526-
1527-
auto proto = cxt.getProtocol(kind);
1528-
if (!proto) return;
1529-
1530-
SmallVector<ProtocolConformance *, 1> conformances;
1531-
if (computedNominal->lookupConformance(
1532-
computedNominal->getParentModule(), proto, conformances)) {
1533-
protocols.push_back(proto);
1534-
synthesizedProtocols.push_back(kind);
1535-
}
1536-
};
1537-
1538-
// Transfer conformances. Each of these needs a forwarding
1539-
// implementation in the standard library.
1540-
transferKnown(KnownProtocolKind::Equatable);
1541-
transferKnown(KnownProtocolKind::Hashable);
1542-
transferKnown(KnownProtocolKind::Comparable);
1543-
transferKnown(KnownProtocolKind::ObjectiveCBridgeable);
1544-
1545-
if (!isBridged) {
1546-
// Simple, our stored type is equivalent to our computed
1547-
// type.
1548-
makeStructRawValued(structDecl, storedUnderlyingType,
1549-
synthesizedProtocols, protocols);
1550-
} else {
1551-
// We need to make a stored rawValue or storage type, and a
1552-
// computed one of bridged type.
1553-
makeStructRawValuedWithBridge(
1554-
structDecl, storedUnderlyingType,
1555-
computedPropertyUnderlyingType,
1556-
synthesizedProtocols, protocols);
1557-
}
1558-
1559-
Impl.ImportedDecls[{Decl->getCanonicalDecl(), useSwift2Name}]
1560-
= structDecl;
1561-
Impl.registerExternalDecl(structDecl);
1562-
return structDecl;
1563-
}
1564-
}
1565-
1566-
}
1567-
}
1585+
if (!SwiftType)
1586+
if (auto newtypeAttr = Impl.getSwiftNewtypeAttr(Decl, useSwift2Name))
1587+
if (auto newtype = importSwiftNewtype(Decl, newtypeAttr, DC, Name))
1588+
return newtype;
15681589

15691590
if (!SwiftType) {
15701591
// Import typedefs of blocks as their fully-bridged equivalent Swift

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) { }

0 commit comments

Comments
 (0)