Skip to content

Commit 0c67ddd

Browse files
committed
[Clang importer] swift_newtype with bridged computed rawValue
For swift_newtype structs that we create, we sometimes need to provide a bridged type interface. In these cases, we use the original non-bridged type as an underlying stored value, and create a computed rawValue of bridged type. We similarly create an init() taking the bridged type, and cast it appropriately to/from storage. Tests updated.
1 parent d574c19 commit 0c67ddd

File tree

3 files changed

+158
-21
lines changed

3 files changed

+158
-21
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 141 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,11 +1286,6 @@ namespace {
12861286
// For now, fall through and treat as a struct
12871287
case clang::SwiftNewtypeAttr::NK_Struct: {
12881288

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

@@ -1301,9 +1296,33 @@ namespace {
13011296
ProtocolDecl *protocols[] = {
13021297
cxt.getProtocol(KnownProtocolKind::RawRepresentable),
13031298
};
1304-
makeStructRawValued(structDecl, underlyingType,
1305-
{KnownProtocolKind::RawRepresentable},
1306-
protocols);
1299+
1300+
// Import the type of the underlying storage
1301+
auto storedUnderlyingType = Impl.importType(
1302+
Decl->getUnderlyingType(), ImportTypeKind::Value,
1303+
isInSystemModule(DC),
1304+
Decl->getUnderlyingType()->isBlockPointerType());
1305+
1306+
// Find a bridged type, which may be different
1307+
auto computedPropertyUnderlyingType = Impl.importType(
1308+
Decl->getUnderlyingType(), ImportTypeKind::Property,
1309+
isInSystemModule(DC),
1310+
Decl->getUnderlyingType()->isBlockPointerType());
1311+
1312+
if (storedUnderlyingType.getCanonicalTypeOrNull() ==
1313+
computedPropertyUnderlyingType.getCanonicalTypeOrNull()) {
1314+
// Simple, our stored type is already bridged
1315+
makeStructRawValued(structDecl, storedUnderlyingType,
1316+
{KnownProtocolKind::RawRepresentable},
1317+
protocols);
1318+
} else {
1319+
// We need to make a stored rawValue or storage type, and a
1320+
// computed one of bridged type.
1321+
makeStructRawValuedWithBridge(
1322+
structDecl, storedUnderlyingType,
1323+
computedPropertyUnderlyingType,
1324+
{KnownProtocolKind::RawRepresentable}, protocols);
1325+
}
13071326

13081327
Impl.ImportedDecls[Decl->getCanonicalDecl()] = structDecl;
13091328
Impl.registerExternalDecl(structDecl);
@@ -1485,6 +1504,112 @@ namespace {
14851504
structDecl->addMember(var);
14861505
}
14871506

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

6203-
auto var = createDeclWithClangNode<VarDecl>(ClangN,
6204-
isStatic, /*IsLet*/ false,
6205-
SourceLoc(), name, type, dc);
6328+
VarDecl *var = nullptr;
6329+
if (ClangN) {
6330+
var = createDeclWithClangNode<VarDecl>(ClangN, isStatic, /*IsLet*/ false,
6331+
SourceLoc(), name, type, dc);
6332+
} else {
6333+
var = new (SwiftContext)
6334+
VarDecl(isStatic, /*IsLet*/ false, SourceLoc(), name, type, dc);
6335+
}
62066336

62076337
// Form the argument patterns.
62086338
SmallVector<ParameterList*, 3> getterArgs;

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

Lines changed: 4 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,11 @@ 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)));

test/IDE/newtype.swift

Lines changed: 13 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,21 @@
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: }
3439

3540
// RUN: %target-parse-verify-swift -I %S/Inputs/custom-modules
3641
import Newtype
@@ -47,7 +52,7 @@ func tests() {
4752
thirdEnum.process()
4853
// expected-error@-1{{value of type 'ClosedEnum' has no member 'process'}}
4954

50-
let _ = ErrorDomain(rawValue: thirdEnum.rawValue)
55+
let _ = ErrorDomain(rawValue: thirdEnum.rawValue!)
5156
let _ = ClosedEnum(rawValue: errOne.rawValue)
5257

5358
}

0 commit comments

Comments
 (0)