Skip to content

Commit faceb55

Browse files
committed
[Clang Importer] Initial support for swift_newtype(struct)
This introduces support for swift_newtype(struct) attribute (also known as swift_wrapper). The Clang importer will create a brand new struct corresponding to the annotated typedefs, which has a backing raw value. Globals of that type are imported as static members on the struct. Additionally, this interacts seamlessly with prior import-as-member work, meaning that the newly created type can be imported onto. Tests included.
1 parent 65288ee commit faceb55

File tree

7 files changed

+135
-3
lines changed

7 files changed

+135
-3
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2034,6 +2034,17 @@ static bool moduleIsInferImportAsMember(const clang::NamedDecl *decl,
20342034
return false;
20352035
}
20362036

2037+
// If this decl is associated with a swift_newtype typedef, return it, otherwise
2038+
// null
2039+
static clang::TypedefNameDecl *findSwiftNewtype(const clang::Decl *decl) {
2040+
if (auto varDecl = dyn_cast<clang::VarDecl>(decl))
2041+
if (auto typedefTy = varDecl->getType()->getAs<clang::TypedefType>())
2042+
if (typedefTy->getDecl()->hasAttr<clang::SwiftNewtypeAttr>())
2043+
return typedefTy->getDecl();
2044+
2045+
return nullptr;
2046+
}
2047+
20372048
auto ClangImporter::Implementation::importFullName(
20382049
const clang::NamedDecl *D,
20392050
ImportNameOptions options,
@@ -2068,8 +2079,11 @@ auto ClangImporter::Implementation::importFullName(
20682079
result.EffectiveContext = enumDecl->getRedeclContext();
20692080
break;
20702081
}
2082+
// Import onto a swift_newtype if present
2083+
} else if (auto newtypeDecl = findSwiftNewtype(D)) {
2084+
result.EffectiveContext = newtypeDecl;
2085+
// Everything else goes into its redeclaration context.
20712086
} else {
2072-
// Everything else goes into its redeclaration context.
20732087
result.EffectiveContext = dc->getRedeclContext();
20742088
}
20752089

@@ -2538,6 +2552,31 @@ auto ClangImporter::Implementation::importFullName(
25382552

25392553
// Omit needless words.
25402554
StringScratchSpace omitNeedlessWordsScratch;
2555+
2556+
// swift_newtype-ed declarations may have common words with the type name
2557+
// stripped.
2558+
if (auto newtypeDecl = findSwiftNewtype(D)) {
2559+
SmallString<32> newNameBuffer;
2560+
auto newtypeNameWords = camel_case::getWords(newtypeDecl->getName());
2561+
auto declNameWords = camel_case::getWords(baseName);
2562+
2563+
// Scan the decl name, and drop any words that appear in order in the type
2564+
// name
2565+
for (auto wI = declNameWords.begin(), skipWI = newtypeNameWords.begin();
2566+
wI != declNameWords.end(); ++wI) {
2567+
if (skipWI != newtypeNameWords.end() && *wI == *skipWI) {
2568+
++skipWI;
2569+
continue;
2570+
}
2571+
newNameBuffer.append(*wI);
2572+
}
2573+
2574+
if (newNameBuffer.str() != baseName) {
2575+
baseName = omitNeedlessWordsScratch.copyString(newNameBuffer);
2576+
strippedPrefix = true;
2577+
}
2578+
}
2579+
25412580
if (!result.isSubscriptAccessor()) {
25422581
// Check whether the module in which the declaration resides has a
25432582
// module prefix and will map into Swift as a type. If so, strip

lib/ClangImporter/ImportDecl.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,44 @@ namespace {
12751275
if (!DC)
12761276
return nullptr;
12771277

1278+
// Check for swift_newtype
1279+
if (!SwiftType) {
1280+
if (auto newtypeAttr =
1281+
Decl->template getAttr<clang::SwiftNewtypeAttr>()) {
1282+
switch (newtypeAttr->getNewtypeKind()) {
1283+
case clang::SwiftNewtypeAttr::NK_Struct: {
1284+
1285+
auto underlyingType = Impl.importType(
1286+
Decl->getUnderlyingType(), ImportTypeKind::Typedef,
1287+
isInSystemModule(DC),
1288+
Decl->getUnderlyingType()->isBlockPointerType());
1289+
1290+
auto &cxt = Impl.SwiftContext;
1291+
auto Loc = Impl.importSourceLoc(Decl->getLocation());
1292+
1293+
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
1294+
Decl, Loc, Name, Loc, None, nullptr, DC);
1295+
structDecl->computeType();
1296+
1297+
ProtocolDecl *protocols[] = {
1298+
cxt.getProtocol(KnownProtocolKind::RawRepresentable),
1299+
};
1300+
makeStructRawValued(structDecl, underlyingType,
1301+
{KnownProtocolKind::RawRepresentable},
1302+
protocols);
1303+
1304+
Impl.ImportedDecls[Decl->getCanonicalDecl()] = structDecl;
1305+
Impl.registerExternalDecl(structDecl);
1306+
return structDecl;
1307+
}
1308+
1309+
case clang::SwiftNewtypeAttr::NK_Enum:
1310+
// TODO: support enum
1311+
break;
1312+
}
1313+
}
1314+
}
1315+
12781316
if (!SwiftType) {
12791317
// Import typedefs of blocks as their fully-bridged equivalent Swift
12801318
// type. That matches how we want to use them in most cases. All other

lib/ClangImporter/ImportType.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ namespace {
8787

8888
/// The source type is any other pointer type.
8989
OtherPointer,
90+
91+
/// The source type created a new Swift type, using swift_newtype
92+
SwiftNewtype,
9093
};
9194

9295
ImportHintKind Kind;
@@ -118,6 +121,7 @@ namespace {
118121
case ImportHint::NSUInteger:
119122
case ImportHint::Reference:
120123
case ImportHint::Void:
124+
case ImportHint::SwiftNewtype:
121125
return false;
122126

123127
case ImportHint::Block:
@@ -544,8 +548,11 @@ namespace {
544548
Type mappedType = decl->getDeclaredType();
545549
ImportHint hint = ImportHint::None;
546550

551+
if (type->getDecl()->hasAttr<clang::SwiftNewtypeAttr>()) {
552+
hint = ImportHint::SwiftNewtype;
553+
547554
// For certain special typedefs, we don't want to use the imported type.
548-
if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
555+
} else if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
549556
switch (specialKind.getValue()) {
550557
case MappedTypeNameKind::DoNothing:
551558
case MappedTypeNameKind::DefineAndUse:

lib/ClangImporter/SwiftLookupTable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MAJOR = 1;
146146
/// Lookup table minor version number.
147147
///
148148
/// When the format changes IN ANY WAY, this number should be incremented.
149-
const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 12; // CG import-as-member
149+
const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 13; // swift_newtype(struct)
150150

151151
/// A lookup table that maps Swift names to the set of Clang
152152
/// declarations with that particular name.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@import Foundation;
2+
3+
typedef NSString *SNTErrorDomain __attribute((swift_newtype(struct)))
4+
__attribute((swift_name("ErrorDomain")));
5+
6+
extern void SNTErrorDomainProcess(SNTErrorDomain d)
7+
__attribute((swift_name("ErrorDomain.process(self:)")));
8+
9+
typedef struct {} Foo;
10+
11+
extern const SNTErrorDomain SNTErrOne
12+
__attribute((swift_name("ErrorDomain.one")));
13+
extern const SNTErrorDomain SNTErrTwo;
14+
extern const SNTErrorDomain SNTErrorDomainThree;
15+
extern const SNTErrorDomain SNTFourErrorDomain;
16+
extern const SNTErrorDomain SNTFive
17+
__attribute((swift_name("stillAMember")));
18+
extern const SNTErrorDomain SNTElsewhere
19+
__attribute((swift_name("Foo.err")));

test/IDE/Inputs/custom-modules/module.map

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ module ImportedProtocols {
1111
}
1212
}
1313

14+
module Newtype {
15+
export *
16+
header "Newtype.h"
17+
}
18+
1419
module ImportAsMember {
1520
export *
1621

test/IDE/newtype.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -I %t -I %S/Inputs/custom-modules -print-module -source-filename %s -module-to-print=Newtype > %t.printed.A.txt
2+
// RUN: FileCheck %s -check-prefix=PRINT -strict-whitespace < %t.printed.A.txt
3+
// REQUIRES: objc_interop
4+
5+
// PRINT: struct ErrorDomain : RawRepresentable {
6+
// PRINT-NEXT: init(rawValue: NSString)
7+
// PRINT-NEXT: let rawValue: NSString
8+
// PRINT-NEXT: }
9+
// PRINT-NEXT: extension ErrorDomain {
10+
// PRINT-NEXT: func process()
11+
// PRINT-NEXT: static let one: ErrorDomain
12+
// PRINT-NEXT: static let errTwo: ErrorDomain
13+
// PRINT-NEXT: static let three: ErrorDomain
14+
// PRINT-NEXT: static let four: ErrorDomain
15+
// PRINT-NEXT: static let stillAMember: ErrorDomain
16+
// PRINT-NEXT: }
17+
// PRINT-NEXT: struct Foo {
18+
// PRINT-NEXT: init()
19+
// PRINT-NEXT: }
20+
// PRINT-NEXT: extension Foo {
21+
// PRINT-NEXT: static let err: ErrorDomain
22+
// PRINT-NEXT: }
23+
24+

0 commit comments

Comments
 (0)