Skip to content

Commit 83ed4bc

Browse files
committed
Merge pull request #2272 from milseman/newtype
[Clang importer] Initial support for swift_newtype / swift_wrapper
2 parents b6985b5 + fb9d8d8 commit 83ed4bc

File tree

9 files changed

+275
-86
lines changed

9 files changed

+275
-86
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 33 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,24 @@ 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+
// Skip a leading 'k' in a 'kConstant' pattern
2560+
if (baseName.size() >= 2 && baseName[0] == 'k' &&
2561+
clang::isUppercase(baseName[1]))
2562+
baseName.drop_front(1);
2563+
2564+
bool nonIdentifier = false;
2565+
auto pre =
2566+
getCommonWordPrefix(newtypeDecl->getName(), baseName, nonIdentifier);
2567+
if (pre.size()) {
2568+
baseName = baseName.drop_front(pre.size());
2569+
strippedPrefix = true;
2570+
}
2571+
}
2572+
25412573
if (!result.isSubscriptAccessor()) {
25422574
// Check whether the module in which the declaration resides has a
25432575
// module prefix and will map into Swift as a type. If so, strip

lib/ClangImporter/ImportDecl.cpp

Lines changed: 114 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,45 @@ 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_Enum:
1284+
// TODO: import as closed enum instead
1285+
1286+
// For now, fall through and treat as a struct
1287+
case clang::SwiftNewtypeAttr::NK_Struct: {
1288+
1289+
auto underlyingType = Impl.importType(
1290+
Decl->getUnderlyingType(), ImportTypeKind::Typedef,
1291+
isInSystemModule(DC),
1292+
Decl->getUnderlyingType()->isBlockPointerType());
1293+
1294+
auto &cxt = Impl.SwiftContext;
1295+
auto Loc = Impl.importSourceLoc(Decl->getLocation());
1296+
1297+
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
1298+
Decl, Loc, Name, Loc, None, nullptr, DC);
1299+
structDecl->computeType();
1300+
1301+
ProtocolDecl *protocols[] = {
1302+
cxt.getProtocol(KnownProtocolKind::RawRepresentable),
1303+
};
1304+
makeStructRawValued(structDecl, underlyingType,
1305+
{KnownProtocolKind::RawRepresentable},
1306+
protocols);
1307+
1308+
Impl.ImportedDecls[Decl->getCanonicalDecl()] = structDecl;
1309+
Impl.registerExternalDecl(structDecl);
1310+
return structDecl;
1311+
}
1312+
}
1313+
1314+
}
1315+
}
1316+
12781317
if (!SwiftType) {
12791318
// Import typedefs of blocks as their fully-bridged equivalent Swift
12801319
// type. That matches how we want to use them in most cases. All other
@@ -1379,6 +1418,73 @@ namespace {
13791418
return constructor;
13801419
}
13811420

1421+
/// Make a struct declaration into a raw-value-backed struct
1422+
///
1423+
/// \param structDecl the struct to make a raw value for
1424+
/// \param underlyingType the type of the raw value
1425+
/// \param synthesizedProtocolAttrs synthesized protocol attributes to add
1426+
/// \param protocols the protocols to make this struct conform to
1427+
/// \param setterAccessibility the accessiblity of the raw value's setter
1428+
/// \param isLet whether the raw value should be a let
1429+
/// \param makeUnlabeledValueInit whether to also create an unlabeled init
1430+
/// \param isImplicit whether to mark the rawValue as implicit
1431+
///
1432+
/// This will perform most of the work involved in making a new Swift struct
1433+
/// be backed by a raw value. This will populated derived protocols and
1434+
/// synthesized protocols, add the new variable and pattern bindings, and
1435+
/// create the inits parameterized over a raw value
1436+
///
1437+
template <unsigned N>
1438+
void makeStructRawValued(
1439+
StructDecl *structDecl,
1440+
Type underlyingType,
1441+
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
1442+
ProtocolDecl *const(&protocols)[N],
1443+
Accessibility setterAccessibility = Accessibility::Private,
1444+
bool isLet = true,
1445+
bool makeUnlabeledValueInit = false,
1446+
bool isImplicit = true) {
1447+
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));
1453+
1454+
// 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);
1463+
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);
1470+
structDecl->setHasDelayedMembers();
1471+
1472+
// Create constructors to initialize that value from a value of the
1473+
// underlying type.
1474+
if (makeUnlabeledValueInit)
1475+
structDecl->addMember(createValueConstructor(
1476+
structDecl, var,
1477+
/*wantCtorParamNames=*/false,
1478+
/*wantBody=*/!Impl.hasFinishedTypeChecking()));
1479+
structDecl->addMember(
1480+
createValueConstructor(structDecl, var,
1481+
/*wantCtorParamNames=*/true,
1482+
/*wantBody=*/!Impl.hasFinishedTypeChecking()));
1483+
1484+
structDecl->addMember(patternBinding);
1485+
structDecl->addMember(var);
1486+
}
1487+
13821488
/// \brief Create a constructor that initializes a struct from its members.
13831489
ConstructorDecl *createValueConstructor(StructDecl *structDecl,
13841490
ArrayRef<VarDecl *> members,
@@ -1625,45 +1731,10 @@ namespace {
16251731
Loc, name, Loc, None, nullptr, dc);
16261732
structDecl->computeType();
16271733

1628-
// Note that this is a raw option set type.
1629-
structDecl->getAttrs().add(
1630-
new (Impl.SwiftContext) SynthesizedProtocolAttr(
1631-
KnownProtocolKind::OptionSet));
1632-
1633-
1634-
// Create a field to store the underlying value.
1635-
auto varName = Impl.SwiftContext.Id_rawValue;
1636-
auto var = new (Impl.SwiftContext) VarDecl(/*static*/ false,
1637-
/*IsLet*/ true,
1638-
SourceLoc(), varName,
1639-
underlyingType,
1640-
structDecl);
1641-
var->setImplicit();
1642-
var->setAccessibility(Accessibility::Public);
1643-
var->setSetterAccessibility(Accessibility::Private);
1644-
1645-
// Create a pattern binding to describe the variable.
1646-
Pattern *varPattern = createTypedNamedPattern(var);
1647-
1648-
auto patternBinding =
1649-
PatternBindingDecl::create(Impl.SwiftContext, SourceLoc(),
1650-
StaticSpellingKind::None, SourceLoc(),
1651-
varPattern, nullptr, structDecl);
1652-
1653-
// Create the init(rawValue:) constructor.
1654-
auto labeledValueConstructor = createValueConstructor(
1655-
structDecl, var,
1656-
/*wantCtorParamNames=*/true,
1657-
/*wantBody=*/!Impl.hasFinishedTypeChecking());
1658-
1659-
// Build an OptionSet conformance for the type.
16601734
ProtocolDecl *protocols[]
16611735
= {cxt.getProtocol(KnownProtocolKind::OptionSet)};
1662-
populateInheritedTypes(structDecl, protocols);
1663-
1664-
structDecl->addMember(labeledValueConstructor);
1665-
structDecl->addMember(patternBinding);
1666-
structDecl->addMember(var);
1736+
makeStructRawValued(structDecl, underlyingType,
1737+
{KnownProtocolKind::OptionSet}, protocols);
16671738
return structDecl;
16681739
}
16691740

@@ -1714,49 +1785,12 @@ namespace {
17141785
ProtocolDecl *protocols[]
17151786
= {cxt.getProtocol(KnownProtocolKind::RawRepresentable),
17161787
cxt.getProtocol(KnownProtocolKind::Equatable)};
1717-
populateInheritedTypes(structDecl, protocols);
1718-
1719-
// Note that this is a raw representable type.
1720-
structDecl->getAttrs().add(
1721-
new (Impl.SwiftContext) SynthesizedProtocolAttr(
1722-
KnownProtocolKind::RawRepresentable));
1723-
1724-
// Create a variable to store the underlying value.
1725-
auto varName = Impl.SwiftContext.Id_rawValue;
1726-
auto var = new (Impl.SwiftContext) VarDecl(/*static*/ false,
1727-
/*IsLet*/ false,
1728-
SourceLoc(), varName,
1729-
underlyingType,
1730-
structDecl);
1731-
var->setAccessibility(Accessibility::Public);
1732-
var->setSetterAccessibility(Accessibility::Public);
1733-
1734-
// Create a pattern binding to describe the variable.
1735-
Pattern *varPattern = createTypedNamedPattern(var);
1736-
1737-
auto patternBinding =
1738-
PatternBindingDecl::create(Impl.SwiftContext, SourceLoc(),
1739-
StaticSpellingKind::None, SourceLoc(),
1740-
varPattern, nullptr, structDecl);
1741-
1742-
// Create a constructor to initialize that value from a value of the
1743-
// underlying type.
1744-
auto valueConstructor =
1745-
createValueConstructor(structDecl, var,
1746-
/*wantCtorParamNames=*/false,
1747-
/*wantBody=*/!Impl.hasFinishedTypeChecking());
1748-
auto labeledValueConstructor =
1749-
createValueConstructor(structDecl, var,
1750-
/*wantCtorParamNames=*/true,
1751-
/*wantBody=*/!Impl.hasFinishedTypeChecking());
1752-
1753-
structDecl->setHasDelayedMembers();
1754-
1755-
// Set the members of the struct.
1756-
structDecl->addMember(valueConstructor);
1757-
structDecl->addMember(labeledValueConstructor);
1758-
structDecl->addMember(patternBinding);
1759-
structDecl->addMember(var);
1788+
makeStructRawValued(structDecl, underlyingType,
1789+
{KnownProtocolKind::RawRepresentable}, protocols,
1790+
/*setterAccessibility=*/Accessibility::Public,
1791+
/*isLet=*/false,
1792+
/*makeUnlabeledValueInit=*/true,
1793+
/*isImplicit=*/false);
17601794

17611795
result = structDecl;
17621796
break;

lib/ClangImporter/ImportEnumInfo.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ void EnumInfo::classifyEnum(const clang::EnumDecl *decl,
8585
///
8686
/// This is used to derive the common prefix of enum constants so we can elide
8787
/// it from the Swift interface.
88-
static StringRef getCommonWordPrefix(StringRef a, StringRef b,
89-
bool &followedByNonIdentifier) {
88+
StringRef importer::getCommonWordPrefix(StringRef a, StringRef b,
89+
bool &followedByNonIdentifier) {
9090
auto aWords = camel_case::getWords(a), bWords = camel_case::getWords(b);
9191
auto aI = aWords.begin(), aE = aWords.end(), bI = bWords.begin(),
9292
bE = bWords.end();
@@ -124,7 +124,8 @@ static StringRef getCommonWordPrefix(StringRef a, StringRef b,
124124
/// in Cocoa and Cocoa Touch.
125125
///
126126
/// \see getCommonWordPrefix
127-
static StringRef getCommonPluralPrefix(StringRef singular, StringRef plural) {
127+
StringRef importer::getCommonPluralPrefix(StringRef singular,
128+
StringRef plural) {
128129
assert(!plural.empty());
129130

130131
if (singular.empty())

lib/ClangImporter/ImportEnumInfo.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,37 @@ class EnumInfo {
9797
void determineConstantNamePrefix(ASTContext &ctx, const clang::EnumDecl *);
9898
void classifyEnum(const clang::EnumDecl *, clang::Preprocessor &);
9999
};
100+
101+
// Utility functions of primary interest to enum constant naming
102+
103+
/// Returns the common prefix of two strings at camel-case word granularity.
104+
///
105+
/// For example, given "NSFooBar" and "NSFooBas", returns "NSFoo"
106+
/// (not "NSFooBa"). The returned StringRef is a slice of the "a" argument.
107+
///
108+
/// If either string has a non-identifier character immediately after the
109+
/// prefix, \p followedByNonIdentifier will be set to \c true. If both strings
110+
/// have identifier characters after the prefix, \p followedByNonIdentifier will
111+
/// be set to \c false. Otherwise, \p followedByNonIdentifier will not be
112+
/// changed from its initial value.
113+
///
114+
/// This is used to derive the common prefix of enum constants so we can elide
115+
/// it from the Swift interface.
116+
StringRef getCommonWordPrefix(StringRef a, StringRef b,
117+
bool &followedByNonIdentifier);
118+
119+
/// Returns the common word-prefix of two strings, allowing the second string
120+
/// to be a common English plural form of the first.
121+
///
122+
/// For example, given "NSProperty" and "NSProperties", the full "NSProperty"
123+
/// is returned. Given "NSMagicArmor" and "NSMagicArmory", only
124+
/// "NSMagic" is returned.
125+
///
126+
/// The "-s", "-es", and "-ies" patterns cover every plural NS_OPTIONS name
127+
/// in Cocoa and Cocoa Touch.
128+
///
129+
/// \see getCommonWordPrefix
130+
StringRef getCommonPluralPrefix(StringRef singular, StringRef plural);
100131
}
101132
}
102133

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.

0 commit comments

Comments
 (0)