Skip to content

Commit aa1a685

Browse files
committed
Merge pull request #2319 from milseman/newtype
[Clang importer] swift_newtype with bridged computed rawValue
2 parents 80564dc + 15e7d1f commit aa1a685

File tree

3 files changed

+198
-41
lines changed

3 files changed

+198
-41
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 170 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,30 @@ static Pattern *createTypedNamedPattern(VarDecl *decl) {
8686
return P;
8787
}
8888

89+
/// Create a var member for this struct, along with its pattern binding, and add
90+
/// it as a member
91+
static std::pair<VarDecl *, PatternBindingDecl *>
92+
createVarWithPattern(ASTContext &cxt, DeclContext *dc, Identifier name, Type ty,
93+
bool isLet, bool isImplicit,
94+
Accessibility setterAccessibility) {
95+
// Create a variable to store the underlying value.
96+
auto var = new (cxt) VarDecl(
97+
/*static*/ false,
98+
/*IsLet*/ isLet, SourceLoc(), name, ty, dc);
99+
if (isImplicit)
100+
var->setImplicit();
101+
var->setAccessibility(Accessibility::Public);
102+
var->setSetterAccessibility(setterAccessibility);
103+
104+
// Create a pattern binding to describe the variable.
105+
Pattern *varPattern = createTypedNamedPattern(var);
106+
auto patternBinding =
107+
PatternBindingDecl::create(cxt, SourceLoc(), StaticSpellingKind::None,
108+
SourceLoc(), varPattern, nullptr, dc);
109+
110+
return {var, patternBinding};
111+
}
112+
89113
#ifndef NDEBUG
90114
static bool verifyNameMapping(MappedTypeNameKind NameMapping,
91115
StringRef left, StringRef right) {
@@ -1286,11 +1310,6 @@ namespace {
12861310
// For now, fall through and treat as a struct
12871311
case clang::SwiftNewtypeAttr::NK_Struct: {
12881312

1289-
auto underlyingType = Impl.importType(
1290-
Decl->getUnderlyingType(), ImportTypeKind::Typedef,
1291-
isInSystemModule(DC),
1292-
Decl->getUnderlyingType()->isBlockPointerType());
1293-
12941313
auto &cxt = Impl.SwiftContext;
12951314
auto Loc = Impl.importSourceLoc(Decl->getLocation());
12961315

@@ -1301,9 +1320,33 @@ namespace {
13011320
ProtocolDecl *protocols[] = {
13021321
cxt.getProtocol(KnownProtocolKind::RawRepresentable),
13031322
};
1304-
makeStructRawValued(structDecl, underlyingType,
1305-
{KnownProtocolKind::RawRepresentable},
1306-
protocols);
1323+
1324+
// Import the type of the underlying storage
1325+
auto storedUnderlyingType = Impl.importType(
1326+
Decl->getUnderlyingType(), ImportTypeKind::Value,
1327+
isInSystemModule(DC),
1328+
Decl->getUnderlyingType()->isBlockPointerType());
1329+
1330+
// Find a bridged type, which may be different
1331+
auto computedPropertyUnderlyingType = Impl.importType(
1332+
Decl->getUnderlyingType(), ImportTypeKind::Property,
1333+
isInSystemModule(DC),
1334+
Decl->getUnderlyingType()->isBlockPointerType());
1335+
1336+
if (storedUnderlyingType.getCanonicalTypeOrNull() ==
1337+
computedPropertyUnderlyingType.getCanonicalTypeOrNull()) {
1338+
// Simple, our stored type is already bridged
1339+
makeStructRawValued(structDecl, storedUnderlyingType,
1340+
{KnownProtocolKind::RawRepresentable},
1341+
protocols);
1342+
} else {
1343+
// We need to make a stored rawValue or storage type, and a
1344+
// computed one of bridged type.
1345+
makeStructRawValuedWithBridge(
1346+
structDecl, storedUnderlyingType,
1347+
computedPropertyUnderlyingType,
1348+
{KnownProtocolKind::RawRepresentable}, protocols);
1349+
}
13071350

13081351
Impl.ImportedDecls[Decl->getCanonicalDecl()] = structDecl;
13091352
Impl.registerExternalDecl(structDecl);
@@ -1445,28 +1488,15 @@ namespace {
14451488
bool makeUnlabeledValueInit = false,
14461489
bool isImplicit = true) {
14471490
auto &cxt = Impl.SwiftContext;
1448-
populateInheritedTypes(structDecl, protocols);
1449-
1450-
// Note synthesized protocols
1451-
for (auto kind : synthesizedProtocolAttrs)
1452-
structDecl->getAttrs().add(new (cxt) SynthesizedProtocolAttr(kind));
1491+
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs, protocols);
14531492

14541493
// Create a variable to store the underlying value.
1455-
auto varName = cxt.Id_rawValue;
1456-
auto var = new (cxt) VarDecl(
1457-
/*static*/ false,
1458-
/*IsLet*/ isLet, SourceLoc(), varName, underlyingType, structDecl);
1459-
if (isImplicit)
1460-
var->setImplicit();
1461-
var->setAccessibility(Accessibility::Public);
1462-
var->setSetterAccessibility(setterAccessibility);
1494+
VarDecl *var;
1495+
PatternBindingDecl *patternBinding;
1496+
std::tie(var, patternBinding) =
1497+
createVarWithPattern(cxt, structDecl, cxt.Id_rawValue, underlyingType,
1498+
isLet, isImplicit, setterAccessibility);
14631499

1464-
// Create a pattern binding to describe the variable.
1465-
Pattern *varPattern = createTypedNamedPattern(var);
1466-
1467-
auto patternBinding = PatternBindingDecl::create(
1468-
cxt, SourceLoc(), StaticSpellingKind::None, SourceLoc(), varPattern,
1469-
nullptr, structDecl);
14701500
structDecl->setHasDelayedMembers();
14711501

14721502
// Create constructors to initialize that value from a value of the
@@ -1480,11 +1510,101 @@ namespace {
14801510
createValueConstructor(structDecl, var,
14811511
/*wantCtorParamNames=*/true,
14821512
/*wantBody=*/!Impl.hasFinishedTypeChecking()));
1483-
14841513
structDecl->addMember(patternBinding);
14851514
structDecl->addMember(var);
14861515
}
14871516

1517+
/// Make a struct declaration into a raw-value-backed struct, with
1518+
/// bridged computed rawValue property which differs from stored backing
1519+
///
1520+
/// \param structDecl the struct to make a raw value for
1521+
/// \param storedUnderlyingType the type of the stored raw value
1522+
/// \param bridgedType the type of the 'rawValue' computed property bridge
1523+
/// \param synthesizedProtocolAttrs synthesized protocol attributes to add
1524+
/// \param protocols the protocols to make this struct conform to
1525+
///
1526+
/// This will perform most of the work involved in making a new Swift struct
1527+
/// be backed by a stored raw value and computed raw value of bridged type.
1528+
/// This will populated derived protocols and synthesized protocols, add the
1529+
/// new variable and pattern bindings, and create the inits parameterized
1530+
/// over a bridged type that will cast to the stored type, as appropriate.
1531+
///
1532+
template <unsigned N> void makeStructRawValuedWithBridge(
1533+
StructDecl *structDecl,
1534+
Type storedUnderlyingType,
1535+
Type bridgedType,
1536+
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
1537+
ProtocolDecl *const(&protocols)[N]) {
1538+
auto &cxt = Impl.SwiftContext;
1539+
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs,
1540+
protocols);
1541+
1542+
auto storedVarName = cxt.getIdentifier("_rawValue");
1543+
auto computedVarName = cxt.Id_rawValue;
1544+
1545+
// Create a variable to store the underlying value.
1546+
VarDecl *storedVar;
1547+
PatternBindingDecl *storedPatternBinding;
1548+
std::tie(storedVar, storedPatternBinding) = createVarWithPattern(
1549+
cxt, structDecl, storedVarName, storedUnderlyingType, /*isLet=*/false,
1550+
/*isImplicit=*/true, Accessibility::Private);
1551+
1552+
//
1553+
// Create a computed value variable
1554+
auto dre = new (cxt) DeclRefExpr(
1555+
storedVar, {}, true, AccessSemantics::Ordinary, storedUnderlyingType);
1556+
auto coerce = new (cxt)
1557+
CoerceExpr(dre, {}, {nullptr, bridgedType});
1558+
auto computedVar = cast<VarDecl>(Impl.createConstant(
1559+
computedVarName, structDecl, bridgedType, coerce,
1560+
ConstantConvertKind::Coerce, false, {}));
1561+
computedVar->setImplicit();
1562+
computedVar->setAccessibility(Accessibility::Public);
1563+
1564+
// Create a pattern binding to describe the variable.
1565+
1566+
Pattern *computedVarPattern = createTypedNamedPattern(computedVar);
1567+
auto computedPatternBinding = PatternBindingDecl::create(
1568+
cxt, SourceLoc(), StaticSpellingKind::None, SourceLoc(),
1569+
computedVarPattern, nullptr, structDecl);
1570+
1571+
auto init = createValueConstructor(structDecl, computedVar,
1572+
/*wantCtorParamNames=*/true,
1573+
/*wantBody=*/false);
1574+
// Insert our custom init body
1575+
if (!Impl.hasFinishedTypeChecking()) {
1576+
auto selfDecl = ParamDecl::createSelf(SourceLoc(), structDecl,
1577+
/*static*/ false, /*inout*/ true);
1578+
// Construct left-hand side.
1579+
Expr *lhs = new (cxt) DeclRefExpr(selfDecl, DeclNameLoc(),
1580+
/*Implicit=*/true);
1581+
lhs = new (cxt) MemberRefExpr(lhs, SourceLoc(), storedVar,
1582+
DeclNameLoc(), /*Implicit=*/true);
1583+
1584+
// Construct right-hand side.
1585+
// FIXME: get the parameter from the init, and plug it in here.
1586+
auto rhs = new (cxt)
1587+
DeclRefExpr(init->getParameterList(1)->get(0), DeclNameLoc(),
1588+
/*Implicit=*/true);
1589+
1590+
// Add assignment.
1591+
auto assign = new (cxt) AssignExpr(lhs, SourceLoc(), rhs,
1592+
/*Implicit=*/true);
1593+
auto body = BraceStmt::create(cxt, SourceLoc(), {assign}, SourceLoc());
1594+
init->setBody(body);
1595+
1596+
// We want to inline away as much of this as we can
1597+
init->getAttrs().add(new (cxt) TransparentAttr(/*implicit*/ true));
1598+
}
1599+
1600+
structDecl->setHasDelayedMembers();
1601+
structDecl->addMember(init);
1602+
structDecl->addMember(storedPatternBinding);
1603+
structDecl->addMember(storedVar);
1604+
structDecl->addMember(computedPatternBinding);
1605+
structDecl->addMember(computedVar);
1606+
}
1607+
14881608
/// \brief Create a constructor that initializes a struct from its members.
14891609
ConstructorDecl *createValueConstructor(StructDecl *structDecl,
14901610
ArrayRef<VarDecl *> members,
@@ -1711,6 +1831,20 @@ namespace {
17111831
nominal->setCheckedInheritanceClause();
17121832
}
17131833

1834+
/// Add protocol conformances and synthesized protocol attributes
1835+
template <unsigned N>
1836+
void
1837+
addProtocolsToStruct(StructDecl *structDecl,
1838+
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
1839+
ProtocolDecl *const(&protocols)[N]) {
1840+
populateInheritedTypes(structDecl, protocols);
1841+
1842+
// Note synthesized protocols
1843+
for (auto kind : synthesizedProtocolAttrs)
1844+
structDecl->getAttrs().add(new (Impl.SwiftContext)
1845+
SynthesizedProtocolAttr(kind));
1846+
}
1847+
17141848
NominalTypeDecl *importAsOptionSetType(DeclContext *dc,
17151849
Identifier name,
17161850
const clang::EnumDecl *decl) {
@@ -6200,9 +6334,14 @@ ClangImporter::Implementation::createConstant(Identifier name, DeclContext *dc,
62006334
ClangNode ClangN) {
62016335
auto &context = SwiftContext;
62026336

6203-
auto var = createDeclWithClangNode<VarDecl>(ClangN,
6204-
isStatic, /*IsLet*/ false,
6205-
SourceLoc(), name, type, dc);
6337+
VarDecl *var = nullptr;
6338+
if (ClangN) {
6339+
var = createDeclWithClangNode<VarDecl>(ClangN, isStatic, /*IsLet*/ false,
6340+
SourceLoc(), name, type, dc);
6341+
} else {
6342+
var = new (SwiftContext)
6343+
VarDecl(isStatic, /*IsLet*/ false, SourceLoc(), name, type, dc);
6344+
}
62066345

62076346
// Form the argument patterns.
62086347
SmallVector<ParameterList*, 3> getterArgs;

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@import Foundation;
22

3-
typedef NSString *SNTErrorDomain __attribute((swift_newtype(struct)))
3+
typedef NSString *__nonnull SNTErrorDomain __attribute((swift_newtype(struct)))
44
__attribute((swift_name("ErrorDomain")));
55

66
extern void SNTErrorDomainProcess(SNTErrorDomain d)
@@ -18,9 +18,15 @@ extern const SNTErrorDomain SNTFive
1818
extern const SNTErrorDomain SNTElsewhere
1919
__attribute((swift_name("Foo.err")));
2020

21-
typedef NSString *SNTClosedEnum __attribute((swift_newtype(enum)))
21+
typedef NSString *__nullable SNTClosedEnum __attribute((swift_newtype(enum)))
2222
__attribute((swift_name("ClosedEnum")));
2323

2424
extern const SNTClosedEnum SNTFirstClosedEntryEnum;
2525
extern const SNTClosedEnum SNTSecondEntry;
2626
extern const SNTClosedEnum SNTClosedEnumThirdEntry;
27+
28+
typedef NSString * IUONewtype __attribute((swift_newtype(struct)));
29+
30+
typedef float MyFloat __attribute((swift_newtype(struct)));
31+
extern const MyFloat globalFloat;
32+

test/IDE/newtype.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
// REQUIRES: objc_interop
44

55
// PRINT-LABEL: struct ErrorDomain : RawRepresentable {
6-
// PRINT-NEXT: init(rawValue: NSString)
7-
// PRINT-NEXT: let rawValue: NSString
6+
// PRINT-NEXT: init(rawValue: String)
7+
// PRINT-NEXT: var _rawValue: NSString
8+
// PRINT-NEXT: var rawValue: String { get }
89
// PRINT-NEXT: }
910
// PRINT-NEXT: extension ErrorDomain {
1011
// PRINT-NEXT: func process()
@@ -20,17 +21,28 @@
2021
// PRINT-NEXT: extension Foo {
2122
// PRINT-NEXT: static let err: ErrorDomain
2223
// PRINT-NEXT: }
23-
//
24-
// TODO: update test when we can import it as actual new enum
25-
// PRINT-LABEL: struct ClosedEnum : RawRepresentable {
26-
// PRINT-NEXT: init(rawValue: NSString)
27-
// PRINT-NEXT: let rawValue: NSString
24+
// PRINT-NEXT: struct ClosedEnum : RawRepresentable {
25+
// PRINT-NEXT: init(rawValue: String?)
26+
// PRINT-NEXT: var _rawValue: NSString?
27+
// PRINT-NEXT: var rawValue: String? { get }
2828
// PRINT-NEXT: }
2929
// PRINT-NEXT: extension ClosedEnum {
3030
// PRINT-NEXT: static let firstClosedEntryEnum: ClosedEnum
3131
// PRINT-NEXT: static let secondEntry: ClosedEnum
3232
// PRINT-NEXT: static let thirdEntry: ClosedEnum
3333
// PRINT-NEXT: }
34+
// PRINT-NEXT: struct IUONewtype : RawRepresentable {
35+
// PRINT-NEXT: init(rawValue: String!)
36+
// PRINT-NEXT: var _rawValue: NSString!
37+
// PRINT-NEXT: var rawValue: String! { get }
38+
// PRINT-NEXT: }
39+
// PRINT-NEXT: struct MyFloat : RawRepresentable {
40+
// PRINT-NEXT: init(rawValue: Float)
41+
// PRINT-NEXT: let rawValue: Float
42+
// PRINT-NEXT: }
43+
// PRINT-NEXT: extension MyFloat {
44+
// PRINT-NEXT: static let globalFloat: MyFloat
45+
// PRINT-NEXT: }
3446

3547
// RUN: %target-parse-verify-swift -I %S/Inputs/custom-modules -enable-swift-newtype
3648
import Newtype
@@ -47,7 +59,7 @@ func tests() {
4759
thirdEnum.process()
4860
// expected-error@-1{{value of type 'ClosedEnum' has no member 'process'}}
4961

50-
let _ = ErrorDomain(rawValue: thirdEnum.rawValue)
62+
let _ = ErrorDomain(rawValue: thirdEnum.rawValue!)
5163
let _ = ClosedEnum(rawValue: errOne.rawValue)
5264

5365
}

0 commit comments

Comments
 (0)