Skip to content

Commit 5aac345

Browse files
committed
Merge pull request #2654 from milseman/newtype
[swift_newtype] Structs have label-less init
2 parents 012ae93 + 134f41c commit 5aac345

File tree

6 files changed

+158
-53
lines changed

6 files changed

+158
-53
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 104 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,35 @@ namespace inferred_attributes {
6464
}
6565
}
6666

67+
namespace {
68+
enum class MakeStructRawValuedFlags {
69+
/// whether to also create an unlabeled init
70+
MakeUnlabeledValueInit = 0x01,
71+
72+
/// whether the raw value should be a let
73+
IsLet = 0x02,
74+
75+
/// whether to mark the rawValue as implicit
76+
IsImplicit = 0x04,
77+
};
78+
using MakeStructRawValuedOptions = OptionSet<MakeStructRawValuedFlags>;
79+
}
80+
81+
static MakeStructRawValuedOptions
82+
getDefaultMakeStructRawValuedOptions() {
83+
MakeStructRawValuedOptions opts;
84+
opts -= MakeStructRawValuedFlags::MakeUnlabeledValueInit; // default off
85+
opts |= MakeStructRawValuedFlags::IsLet; // default on
86+
opts |= MakeStructRawValuedFlags::IsImplicit; // default on
87+
return opts;
88+
}
6789

6890
static bool isInSystemModule(DeclContext *D) {
6991
if (cast<ClangModuleUnit>(D->getModuleScopeContext())->isSystemModule())
7092
return true;
7193
return false;
7294
}
7395

74-
7596
/// Create a typedpattern(namedpattern(decl))
7697
static Pattern *createTypedNamedPattern(VarDecl *decl) {
7798
ASTContext &Ctx = decl->getASTContext();
@@ -1328,11 +1349,20 @@ namespace {
13281349
Decl *importSwiftNewtype(const clang::TypedefNameDecl *decl,
13291350
clang::SwiftNewtypeAttr *newtypeAttr,
13301351
DeclContext *dc, Identifier name) {
1352+
// The only (current) difference between swift_newtype(struct) and
1353+
// swift_newtype(enum), until we can get real enum support, is that enums
1354+
// have no un-labeld inits(). This is because enums are to be considered
1355+
// closed, and if constructed from a rawValue, should be very explicit.
1356+
bool unlabeledCtor = false;
1357+
13311358
switch (newtypeAttr->getNewtypeKind()) {
13321359
case clang::SwiftNewtypeAttr::NK_Enum:
1333-
// TODO: import as closed enum instead
1334-
// For now, fall through and treat as a struct
1360+
unlabeledCtor = false;
1361+
// TODO: import as closed enum instead
1362+
break;
1363+
13351364
case clang::SwiftNewtypeAttr::NK_Struct:
1365+
unlabeledCtor = true;
13361366
break;
13371367
// No other cases yet
13381368
}
@@ -1420,14 +1450,19 @@ namespace {
14201450
if (!isBridged) {
14211451
// Simple, our stored type is equivalent to our computed
14221452
// type.
1453+
auto options = getDefaultMakeStructRawValuedOptions();
1454+
if (unlabeledCtor)
1455+
options |= MakeStructRawValuedFlags::MakeUnlabeledValueInit;
1456+
14231457
makeStructRawValued(structDecl, storedUnderlyingType,
1424-
synthesizedProtocols, protocols);
1458+
synthesizedProtocols, protocols, options);
14251459
} else {
14261460
// We need to make a stored rawValue or storage type, and a
14271461
// computed one of bridged type.
14281462
makeStructRawValuedWithBridge(structDecl, storedUnderlyingType,
14291463
computedPropertyUnderlyingType,
1430-
synthesizedProtocols, protocols);
1464+
synthesizedProtocols, protocols,
1465+
/*makeUnlabeledValueInit=*/unlabeledCtor);
14311466
}
14321467

14331468
Impl.ImportedDecls[{decl->getCanonicalDecl(), useSwift2Name}] =
@@ -1692,39 +1727,37 @@ namespace {
16921727
/// \param synthesizedProtocolAttrs synthesized protocol attributes to add
16931728
/// \param protocols the protocols to make this struct conform to
16941729
/// \param setterAccessibility the accessibility of the raw value's setter
1695-
/// \param isLet whether the raw value should be a let
1696-
/// \param makeUnlabeledValueInit whether to also create an unlabeled init
1697-
/// \param isImplicit whether to mark the rawValue as implicit
16981730
///
16991731
/// This will perform most of the work involved in making a new Swift struct
17001732
/// be backed by a raw value. This will populated derived protocols and
17011733
/// synthesized protocols, add the new variable and pattern bindings, and
17021734
/// create the inits parameterized over a raw value
17031735
///
17041736
void makeStructRawValued(
1705-
StructDecl *structDecl,
1706-
Type underlyingType,
1737+
StructDecl *structDecl, Type underlyingType,
17071738
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
17081739
ArrayRef<ProtocolDecl *> protocols,
1709-
Accessibility setterAccessibility = Accessibility::Private,
1710-
bool isLet = true,
1711-
bool makeUnlabeledValueInit = false,
1712-
bool isImplicit = true) {
1740+
MakeStructRawValuedOptions options =
1741+
getDefaultMakeStructRawValuedOptions(),
1742+
Accessibility setterAccessibility = Accessibility::Private) {
17131743
auto &cxt = Impl.SwiftContext;
17141744
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs, protocols);
17151745

17161746
// Create a variable to store the underlying value.
17171747
VarDecl *var;
17181748
PatternBindingDecl *patternBinding;
1719-
std::tie(var, patternBinding) =
1720-
createVarWithPattern(cxt, structDecl, cxt.Id_rawValue, underlyingType,
1721-
isLet, isImplicit, setterAccessibility);
1749+
std::tie(var, patternBinding) = createVarWithPattern(
1750+
cxt, structDecl, cxt.Id_rawValue, underlyingType,
1751+
options.contains(MakeStructRawValuedFlags::IsLet),
1752+
options.contains(MakeStructRawValuedFlags::IsImplicit),
1753+
setterAccessibility);
17221754

17231755
structDecl->setHasDelayedMembers();
17241756

17251757
// Create constructors to initialize that value from a value of the
17261758
// underlying type.
1727-
if (makeUnlabeledValueInit)
1759+
if (options.contains(
1760+
MakeStructRawValuedFlags::MakeUnlabeledValueInit))
17281761
structDecl->addMember(createValueConstructor(
17291762
structDecl, var,
17301763
/*wantCtorParamNames=*/false,
@@ -1753,14 +1786,12 @@ namespace {
17531786
/// over a bridged type that will cast to the stored type, as appropriate.
17541787
///
17551788
void makeStructRawValuedWithBridge(
1756-
StructDecl *structDecl,
1757-
Type storedUnderlyingType,
1758-
Type bridgedType,
1789+
StructDecl *structDecl, Type storedUnderlyingType, Type bridgedType,
17591790
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
1760-
ArrayRef<ProtocolDecl *> protocols) {
1791+
ArrayRef<ProtocolDecl *> protocols,
1792+
bool makeUnlabeledValueInit = false) {
17611793
auto &cxt = Impl.SwiftContext;
1762-
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs,
1763-
protocols);
1794+
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs, protocols);
17641795

17651796
auto storedVarName = cxt.getIdentifier("_rawValue");
17661797
auto computedVarName = cxt.Id_rawValue;
@@ -1774,46 +1805,70 @@ namespace {
17741805

17751806
//
17761807
// Create a computed value variable
1777-
auto computedVar = new (cxt) VarDecl(/*static*/ false,
1778-
/*IsLet*/ false,
1779-
SourceLoc(), computedVarName,
1780-
bridgedType, structDecl);
1808+
auto computedVar =
1809+
new (cxt) VarDecl(/*static*/ false,
1810+
/*IsLet*/ false, SourceLoc(), computedVarName,
1811+
bridgedType, structDecl);
17811812
computedVar->setImplicit();
17821813
computedVar->setAccessibility(Accessibility::Public);
17831814
computedVar->setSetterAccessibility(Accessibility::Private);
17841815

17851816
// Create the getter for the computed value variable.
1786-
auto computedVarGetter = makeNewtypeBridgedRawValueGetter(Impl,
1787-
structDecl,
1788-
computedVar,
1789-
storedVar);
1817+
auto computedVarGetter = makeNewtypeBridgedRawValueGetter(
1818+
Impl, structDecl, computedVar, storedVar);
17901819

17911820
// Create a pattern binding to describe the variable.
17921821
Pattern *computedVarPattern = createTypedNamedPattern(computedVar);
17931822
auto computedPatternBinding = PatternBindingDecl::create(
17941823
cxt, SourceLoc(), StaticSpellingKind::None, SourceLoc(),
17951824
computedVarPattern, nullptr, structDecl);
17961825

1797-
auto init = createValueConstructor(structDecl, computedVar,
1798-
/*wantCtorParamNames=*/true,
1826+
auto init = createRawValueBridgingConstructor(
1827+
structDecl, computedVar, storedVar,
1828+
/*wantLabel*/ true, !Impl.hasFinishedTypeChecking());
1829+
1830+
ConstructorDecl *unlabeledCtor = nullptr;
1831+
if (makeUnlabeledValueInit)
1832+
unlabeledCtor = createRawValueBridgingConstructor(
1833+
structDecl, computedVar, storedVar,
1834+
/*wantLabel*/ false, !Impl.hasFinishedTypeChecking());
1835+
1836+
structDecl->setHasDelayedMembers();
1837+
if (unlabeledCtor)
1838+
structDecl->addMember(unlabeledCtor);
1839+
structDecl->addMember(init);
1840+
structDecl->addMember(storedPatternBinding);
1841+
structDecl->addMember(storedVar);
1842+
structDecl->addMember(computedPatternBinding);
1843+
structDecl->addMember(computedVar);
1844+
structDecl->addMember(computedVarGetter);
1845+
}
1846+
1847+
/// Create a rawValue-ed constructor that bridges to its underlying storage.
1848+
ConstructorDecl *createRawValueBridgingConstructor(
1849+
StructDecl *structDecl, VarDecl *computedRawValue,
1850+
VarDecl *storedRawValue, bool wantLabel, bool wantBody) {
1851+
auto &cxt = Impl.SwiftContext;
1852+
auto init = createValueConstructor(structDecl, computedRawValue,
1853+
/*wantCtorParamNames=*/wantLabel,
17991854
/*wantBody=*/false);
18001855
// Insert our custom init body
1801-
if (!Impl.hasFinishedTypeChecking()) {
1856+
if (wantBody) {
18021857
auto selfDecl = init->getParameterList(0)->get(0);
18031858

18041859
// Construct left-hand side.
18051860
Expr *lhs = new (cxt) DeclRefExpr(selfDecl, DeclNameLoc(),
18061861
/*Implicit=*/true);
1807-
lhs = new (cxt) MemberRefExpr(lhs, SourceLoc(), storedVar,
1862+
lhs = new (cxt) MemberRefExpr(lhs, SourceLoc(), storedRawValue,
18081863
DeclNameLoc(), /*Implicit=*/true);
18091864

18101865
// Construct right-hand side.
18111866
// FIXME: get the parameter from the init, and plug it in here.
18121867
auto rhs = new (cxt)
1813-
CoerceExpr(new (cxt) DeclRefExpr(
1814-
init->getParameterList(1)->get(0),
1815-
DeclNameLoc(),
1816-
/*Implicit=*/true), {}, {nullptr, storedUnderlyingType});
1868+
CoerceExpr(new (cxt) DeclRefExpr(init->getParameterList(1)->get(0),
1869+
DeclNameLoc(),
1870+
/*Implicit=*/true),
1871+
{}, {nullptr, storedRawValue->getType()});
18171872

18181873
// Add assignment.
18191874
auto assign = new (cxt) AssignExpr(lhs, SourceLoc(), rhs,
@@ -1822,13 +1877,7 @@ namespace {
18221877
init->setBody(body);
18231878
}
18241879

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);
1880+
return init;
18321881
}
18331882

18341883
/// \brief Create a constructor that initializes a struct from its members.
@@ -2176,12 +2225,16 @@ namespace {
21762225
ProtocolDecl *protocols[]
21772226
= {cxt.getProtocol(KnownProtocolKind::RawRepresentable),
21782227
cxt.getProtocol(KnownProtocolKind::Equatable)};
2228+
2229+
auto options = getDefaultMakeStructRawValuedOptions();
2230+
options |= MakeStructRawValuedFlags::MakeUnlabeledValueInit;
2231+
options -= MakeStructRawValuedFlags::IsLet;
2232+
options -= MakeStructRawValuedFlags::IsImplicit;
2233+
21792234
makeStructRawValued(structDecl, underlyingType,
21802235
{KnownProtocolKind::RawRepresentable}, protocols,
2181-
/*setterAccessibility=*/Accessibility::Public,
2182-
/*isLet=*/false,
2183-
/*makeUnlabeledValueInit=*/true,
2184-
/*isImplicit=*/false);
2236+
options,
2237+
/*setterAccessibility=*/Accessibility::Public);
21852238

21862239
result = structDecl;
21872240
break;

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)