Skip to content

Commit e0414a4

Browse files
committed
[swift_newtype] Structs have label-less init
Adds an unlabeled rawValue init for swift_newtype(struct), which expresses the extensibility theme better. This makes the use and creation of new instances of that type more succinct. swift_newtype(enum) still requires the explicit label, as it is non-extensible.
1 parent b2aaa61 commit e0414a4

File tree

6 files changed

+116
-37
lines changed

6 files changed

+116
-37
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,11 +1328,20 @@ namespace {
13281328
Decl *importSwiftNewtype(const clang::TypedefNameDecl *decl,
13291329
clang::SwiftNewtypeAttr *newtypeAttr,
13301330
DeclContext *dc, Identifier name) {
1331+
// The only (current) difference between swift_newtype(struct) and
1332+
// swift_newtype(enum), until we can get real enum support, is that enums
1333+
// have no un-labeld inits(). This is because enums are to be considered
1334+
// closed, and if constructed from a rawValue, should be very explicit.
1335+
bool unlabeledCtor = false;
1336+
13311337
switch (newtypeAttr->getNewtypeKind()) {
13321338
case clang::SwiftNewtypeAttr::NK_Enum:
1333-
// TODO: import as closed enum instead
1334-
// For now, fall through and treat as a struct
1339+
unlabeledCtor = false;
1340+
// TODO: import as closed enum instead
1341+
break;
1342+
13351343
case clang::SwiftNewtypeAttr::NK_Struct:
1344+
unlabeledCtor = true;
13361345
break;
13371346
// No other cases yet
13381347
}
@@ -1421,13 +1430,15 @@ namespace {
14211430
// Simple, our stored type is equivalent to our computed
14221431
// type.
14231432
makeStructRawValued(structDecl, storedUnderlyingType,
1424-
synthesizedProtocols, protocols);
1433+
synthesizedProtocols, protocols,
1434+
/*makeUnlabeledValueInit=*/unlabeledCtor);
14251435
} else {
14261436
// We need to make a stored rawValue or storage type, and a
14271437
// computed one of bridged type.
14281438
makeStructRawValuedWithBridge(structDecl, storedUnderlyingType,
14291439
computedPropertyUnderlyingType,
1430-
synthesizedProtocols, protocols);
1440+
synthesizedProtocols, protocols,
1441+
/*makeUnlabeledValueInit=*/unlabeledCtor);
14311442
}
14321443

14331444
Impl.ImportedDecls[{decl->getCanonicalDecl(), useSwift2Name}] =
@@ -1706,9 +1717,9 @@ namespace {
17061717
Type underlyingType,
17071718
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
17081719
ArrayRef<ProtocolDecl *> protocols,
1720+
bool makeUnlabeledValueInit = false,
17091721
Accessibility setterAccessibility = Accessibility::Private,
17101722
bool isLet = true,
1711-
bool makeUnlabeledValueInit = false,
17121723
bool isImplicit = true) {
17131724
auto &cxt = Impl.SwiftContext;
17141725
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs, protocols);
@@ -1753,14 +1764,12 @@ namespace {
17531764
/// over a bridged type that will cast to the stored type, as appropriate.
17541765
///
17551766
void makeStructRawValuedWithBridge(
1756-
StructDecl *structDecl,
1757-
Type storedUnderlyingType,
1758-
Type bridgedType,
1767+
StructDecl *structDecl, Type storedUnderlyingType, Type bridgedType,
17591768
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
1760-
ArrayRef<ProtocolDecl *> protocols) {
1769+
ArrayRef<ProtocolDecl *> protocols,
1770+
bool makeUnlabeledValueInit = false) {
17611771
auto &cxt = Impl.SwiftContext;
1762-
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs,
1763-
protocols);
1772+
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs, protocols);
17641773

17651774
auto storedVarName = cxt.getIdentifier("_rawValue");
17661775
auto computedVarName = cxt.Id_rawValue;
@@ -1774,46 +1783,70 @@ namespace {
17741783

17751784
//
17761785
// Create a computed value variable
1777-
auto computedVar = new (cxt) VarDecl(/*static*/ false,
1778-
/*IsLet*/ false,
1779-
SourceLoc(), computedVarName,
1780-
bridgedType, structDecl);
1786+
auto computedVar =
1787+
new (cxt) VarDecl(/*static*/ false,
1788+
/*IsLet*/ false, SourceLoc(), computedVarName,
1789+
bridgedType, structDecl);
17811790
computedVar->setImplicit();
17821791
computedVar->setAccessibility(Accessibility::Public);
17831792
computedVar->setSetterAccessibility(Accessibility::Private);
17841793

17851794
// Create the getter for the computed value variable.
1786-
auto computedVarGetter = makeNewtypeBridgedRawValueGetter(Impl,
1787-
structDecl,
1788-
computedVar,
1789-
storedVar);
1795+
auto computedVarGetter = makeNewtypeBridgedRawValueGetter(
1796+
Impl, structDecl, computedVar, storedVar);
17901797

17911798
// Create a pattern binding to describe the variable.
17921799
Pattern *computedVarPattern = createTypedNamedPattern(computedVar);
17931800
auto computedPatternBinding = PatternBindingDecl::create(
17941801
cxt, SourceLoc(), StaticSpellingKind::None, SourceLoc(),
17951802
computedVarPattern, nullptr, structDecl);
17961803

1797-
auto init = createValueConstructor(structDecl, computedVar,
1798-
/*wantCtorParamNames=*/true,
1804+
auto init = createRawValueBridgingConstructor(
1805+
structDecl, computedVar, storedVar,
1806+
/*wantLabel*/ true, !Impl.hasFinishedTypeChecking());
1807+
1808+
ConstructorDecl *unlabeledCtor = nullptr;
1809+
if (makeUnlabeledValueInit)
1810+
unlabeledCtor = createRawValueBridgingConstructor(
1811+
structDecl, computedVar, storedVar,
1812+
/*wantLabel*/ false, !Impl.hasFinishedTypeChecking());
1813+
1814+
structDecl->setHasDelayedMembers();
1815+
if (unlabeledCtor)
1816+
structDecl->addMember(unlabeledCtor);
1817+
structDecl->addMember(init);
1818+
structDecl->addMember(storedPatternBinding);
1819+
structDecl->addMember(storedVar);
1820+
structDecl->addMember(computedPatternBinding);
1821+
structDecl->addMember(computedVar);
1822+
structDecl->addMember(computedVarGetter);
1823+
}
1824+
1825+
/// Create a rawValue-ed constructor that bridges to its underlying storage.
1826+
ConstructorDecl *createRawValueBridgingConstructor(
1827+
StructDecl *structDecl, VarDecl *computedRawValue,
1828+
VarDecl *storedRawValue, bool wantLabel, bool wantBody) {
1829+
auto &cxt = Impl.SwiftContext;
1830+
auto init = createValueConstructor(structDecl, computedRawValue,
1831+
/*wantCtorParamNames=*/wantLabel,
17991832
/*wantBody=*/false);
18001833
// Insert our custom init body
1801-
if (!Impl.hasFinishedTypeChecking()) {
1834+
if (wantBody) {
18021835
auto selfDecl = init->getParameterList(0)->get(0);
18031836

18041837
// Construct left-hand side.
18051838
Expr *lhs = new (cxt) DeclRefExpr(selfDecl, DeclNameLoc(),
18061839
/*Implicit=*/true);
1807-
lhs = new (cxt) MemberRefExpr(lhs, SourceLoc(), storedVar,
1840+
lhs = new (cxt) MemberRefExpr(lhs, SourceLoc(), storedRawValue,
18081841
DeclNameLoc(), /*Implicit=*/true);
18091842

18101843
// Construct right-hand side.
18111844
// FIXME: get the parameter from the init, and plug it in here.
18121845
auto rhs = new (cxt)
1813-
CoerceExpr(new (cxt) DeclRefExpr(
1814-
init->getParameterList(1)->get(0),
1815-
DeclNameLoc(),
1816-
/*Implicit=*/true), {}, {nullptr, storedUnderlyingType});
1846+
CoerceExpr(new (cxt) DeclRefExpr(init->getParameterList(1)->get(0),
1847+
DeclNameLoc(),
1848+
/*Implicit=*/true),
1849+
{}, {nullptr, storedRawValue->getType()});
18171850

18181851
// Add assignment.
18191852
auto assign = new (cxt) AssignExpr(lhs, SourceLoc(), rhs,
@@ -1822,13 +1855,7 @@ namespace {
18221855
init->setBody(body);
18231856
}
18241857

1825-
structDecl->setHasDelayedMembers();
1826-
structDecl->addMember(init);
1827-
structDecl->addMember(storedPatternBinding);
1828-
structDecl->addMember(storedVar);
1829-
structDecl->addMember(computedPatternBinding);
1830-
structDecl->addMember(computedVar);
1831-
structDecl->addMember(computedVarGetter);
1858+
return init;
18321859
}
18331860

18341861
/// \brief Create a constructor that initializes a struct from its members.
@@ -2178,9 +2205,9 @@ namespace {
21782205
cxt.getProtocol(KnownProtocolKind::Equatable)};
21792206
makeStructRawValued(structDecl, underlyingType,
21802207
{KnownProtocolKind::RawRepresentable}, protocols,
2208+
/*makeUnlabeledValueInit=*/true,
21812209
/*setterAccessibility=*/Accessibility::Public,
21822210
/*isLet=*/false,
2183-
/*makeUnlabeledValueInit=*/true,
21842211
/*isImplicit=*/false);
21852212

21862213
result = structDecl;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ extern const MyFloat globalFloat;
3333
extern const MyFloat kPI;
3434
extern const MyFloat kVersion;
3535

36+
typedef int MyInt __attribute((swift_newtype(struct)));
37+
extern const MyInt kMyIntZero;
38+
extern const MyInt kMyIntOne;
39+
extern const int kRawInt;
40+
extern void takesMyInt(MyInt);
41+
3642
typedef NSString * NSURLResourceKey __attribute((swift_newtype(struct)));
3743
extern NSURLResourceKey const NSURLIsRegularFileKey;
3844
extern NSURLResourceKey const NSURLIsDirectoryKey;

test/IDE/newtype.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// REQUIRES: objc_interop
88

99
// PRINT-LABEL: struct ErrorDomain : RawRepresentable, _SwiftNewtypeWrapper, Equatable, Hashable, Comparable, _ObjectiveCBridgeable {
10+
// PRINT-NEXT: init(_ rawValue: String)
1011
// PRINT-NEXT: init(rawValue: String)
1112
// PRINT-NEXT: var _rawValue: NSString
1213
// PRINT-NEXT: var rawValue: String { get }
@@ -36,11 +37,13 @@
3637
// PRINT-NEXT: static let thirdEntry: ClosedEnum
3738
// PRINT-NEXT: }
3839
// PRINT-NEXT: struct IUONewtype : RawRepresentable, _SwiftNewtypeWrapper, Equatable, Hashable, Comparable, _ObjectiveCBridgeable {
40+
// PRINT-NEXT: init(_ rawValue: String)
3941
// PRINT-NEXT: init(rawValue: String)
4042
// PRINT-NEXT: var _rawValue: NSString
4143
// PRINT-NEXT: var rawValue: String { get }
4244
// PRINT-NEXT: }
4345
// PRINT-NEXT: struct MyFloat : RawRepresentable, _SwiftNewtypeWrapper, Equatable, Hashable, Comparable {
46+
// PRINT-NEXT: init(_ rawValue: Float)
4447
// PRINT-NEXT: init(rawValue: Float)
4548
// PRINT-NEXT: let rawValue: Float
4649
// PRINT-NEXT: }
@@ -50,6 +53,18 @@
5053
// PRINT-NEXT: static let version: MyFloat
5154
// PRINT-NEXT: }
5255
//
56+
// PRINT-LABEL: struct MyInt : RawRepresentable, _SwiftNewtypeWrapper, Equatable, Hashable, Comparable {
57+
// PRINT-NEXT: init(_ rawValue: Int32)
58+
// PRINT-NEXT: init(rawValue: Int32)
59+
// PRINT-NEXT: let rawValue: Int32
60+
// PRINT-NEXT: }
61+
// PRINT-NEXT: extension MyInt {
62+
// PRINT-NEXT: static let zero: MyInt!
63+
// PRINT-NEXT: static let one: MyInt!
64+
// PRINT-NEXT: }
65+
// PRINT-NEXT: let kRawInt: Int32
66+
// PRINT-NEXT: func takesMyInt(_: MyInt!)
67+
//
5368
// PRINT-LABEL: extension NSURLResourceKey {
5469
// PRINT-NEXT: static let isRegularFileKey: NSURLResourceKey
5570
// PRINT-NEXT: static let isDirectoryKey: NSURLResourceKey
@@ -65,6 +80,7 @@
6580
// PRINT-NEXT: let swiftNamedNotification: String
6681
//
6782
// PRINT-LABEL: struct CFNewType : RawRepresentable, _SwiftNewtypeWrapper {
83+
// PRINT-NEXT: init(_ rawValue: CFString)
6884
// PRINT-NEXT: init(rawValue: CFString)
6985
// PRINT-NEXT: let rawValue: CFString
7086
// PRINT-NEXT: }

test/IRGen/newtype.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// RUN: mkdir -p %t
33
// RUN: %build-irgen-test-overlays
44
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t -I %S/../IDE/Inputs/custom-modules) %s -emit-ir -enable-swift-newtype | FileCheck %s
5+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t -I %S/../IDE/Inputs/custom-modules) %s -emit-ir -enable-swift-newtype -O | FileCheck %s -check-prefix=OPT
56
import CoreFoundation
67
import Foundation
78
import Newtype
@@ -100,3 +101,32 @@ public func compareABIs() {
100101
// CHECK: declare void @takeMyABINewTypeNonNullNS(%0*)
101102
// CHECK: declare void @takeMyABIOldTypeNonNullNS(%0*)
102103
}
104+
105+
// OPT-LABEL: define i1 @_TF7newtype12compareInitsFT_Sb
106+
public func compareInits() -> Bool {
107+
let mf = MyInt(rawValue: 1)
108+
let mfNoLabel = MyInt(1)
109+
let res = mf.rawValue == MyInt.one.rawValue
110+
&& mfNoLabel.rawValue == MyInt.one.rawValue
111+
// OPT: [[ONE:%.*]] = load i32, i32* @kMyIntOne, align 4
112+
// OPT-NEXT: [[COMP:%.*]] = icmp eq i32 [[ONE]], 1
113+
114+
takesMyInt(mf)
115+
takesMyInt(mfNoLabel)
116+
takesMyInt(MyInt(rawValue: kRawInt))
117+
takesMyInt(MyInt(kRawInt))
118+
// OPT: tail call void @takesMyInt(i32 1)
119+
// OPT-NEXT: tail call void @takesMyInt(i32 1)
120+
// OPT-NEXT: [[RAWINT:%.*]] = load i32, i32* @kRawInt, align 4
121+
// OPT-NEXT: tail call void @takesMyInt(i32 [[RAWINT]])
122+
// OPT-NEXT: tail call void @takesMyInt(i32 [[RAWINT]])
123+
124+
return res
125+
// OPT-NEXT: ret i1 [[COMP]]
126+
}
127+
128+
// CHECK-LABEL: anchor
129+
// OPT-LABEL: anchor
130+
public func anchor() -> Bool {
131+
return false
132+
}

test/Interpreter/SDK/autolinking.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// This is specifically testing autolinking for immediate mode. Please do not
1515
// change it to use %target-build/%target-run
1616
// REQUIRES: swift_interpreter
17-
// REQUIRES: macosx
17+
// REQUIRES: OS=macosx
1818

1919

2020
import Darwin

test/Interpreter/import_as_member.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// RUN: %target-run %t/a.out | FileCheck %s
66

77
// REQUIRES: swift_interpreter
8-
// REQUIRES: macosx
8+
// REQUIRES: OS=macosx
99

1010
import ImportAsMember
1111

0 commit comments

Comments
 (0)