Skip to content

Commit ed61bcf

Browse files
committed
PrintAsClang: Use TypeDecl to associate Swift types to C printed type names
There was a hard to see misalignment between types accepted as representable in C languages defined in `BuiltinMappedTypes.def` and those with an associated C type name defined in `PrimitiveTypeMapping`. While `BuiltinMappedTypes` looked through typealiases, `PrimitiveTypeMapping` didn't and instead referred only to types by their identifier. So even when both files defined the same types by name, their underlying type were only partially handled. An affected type was `Unicode.Scalar` which is the underlying type of `CWideChar` and `CChar32`. `Unicode.Scalar` was accepted as a C representable type but was not printed using a corresponding C type name, breaking the generated compatibility header. Another issue with the use of identifiers in the PrimitiveTypeMapping logic was a limited support for nested types. It would track `Unicode.Scalar` only as `Scalar`. This change updates `PrimitiveTypeMapping` to keep track of TypeDecls instead of identifiers and look through typealiases. This fixes the issue with `Unicode.Scalar`, allowing it to be printed using the type defined by an alias and avoid matching an unrelated Scalar. It should also prevent future types behind typealiases to trigger this same kind of misalignment. rdar://157332446
1 parent 54362d8 commit ed61bcf

File tree

3 files changed

+91
-37
lines changed

3 files changed

+91
-37
lines changed

lib/PrintAsClang/PrimitiveTypeMapping.cpp

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,66 @@
1919

2020
using namespace swift;
2121

22+
/// Find the implementation of the named type in the named module if loaded.
23+
static TypeDecl *findTypeInModuleByName(ASTContext &ctx,
24+
Identifier moduleName,
25+
Identifier typeName) {
26+
auto module = ctx.getLoadedModule(moduleName);
27+
if (!module)
28+
return nullptr;
29+
30+
// Find all of the declarations with this name in the Swift module.
31+
SmallVector<ValueDecl *, 1> results;
32+
module->lookupValue(typeName, NLKind::UnqualifiedLookup, results);
33+
for (auto result : results) {
34+
if (auto nominal = dyn_cast<NominalTypeDecl>(result))
35+
return nominal;
36+
37+
if (auto typealias = dyn_cast<TypeAliasDecl>(result))
38+
return typealias;
39+
}
40+
41+
return nullptr;
42+
}
43+
2244
void PrimitiveTypeMapping::initialize(ASTContext &ctx) {
2345
assert(mappedTypeNames.empty() && "expected empty type map");
46+
47+
auto addMappedType = [&](Identifier moduleName, Identifier typeName,
48+
FullClangTypeInfo info) {
49+
auto decl = findTypeInModuleByName(ctx, moduleName, typeName);
50+
if (!decl)
51+
return;
52+
53+
// Always map a direct definition match. Either the nominal decl or the
54+
// typealias itself.
55+
mappedTypeNames[decl] = info;
56+
57+
// If the underlying type of a typealias doesn't have a type, set it here.
58+
// This aims to reproduce the typealias behavior from BuiltinMappedTypes.
59+
if (auto typealias = dyn_cast<TypeAliasDecl>(decl)) {
60+
auto underlying = typealias->getDeclaredInterfaceType()->getAnyNominal();
61+
if (underlying && !mappedTypeNames.contains(underlying))
62+
mappedTypeNames[underlying] = info;
63+
}
64+
};
65+
66+
// Map stdlib types.
2467
#define MAP(SWIFT_NAME, CLANG_REPR, NEEDS_NULLABILITY) \
25-
mappedTypeNames[{ctx.StdlibModuleName, ctx.getIdentifier(#SWIFT_NAME)}] = { \
26-
CLANG_REPR, std::optional<StringRef>(CLANG_REPR), \
27-
std::optional<StringRef>(CLANG_REPR), NEEDS_NULLABILITY}
68+
addMappedType(ctx.StdlibModuleName, \
69+
ctx.getIdentifier(#SWIFT_NAME), \
70+
{CLANG_REPR, std::optional<StringRef>(CLANG_REPR), \
71+
std::optional<StringRef>(CLANG_REPR), NEEDS_NULLABILITY})
2872
#define MAP_C(SWIFT_NAME, OBJC_REPR, C_REPR, NEEDS_NULLABILITY) \
29-
mappedTypeNames[{ctx.StdlibModuleName, ctx.getIdentifier(#SWIFT_NAME)}] = { \
30-
OBJC_REPR, std::optional<StringRef>(C_REPR), \
31-
std::optional<StringRef>(C_REPR), NEEDS_NULLABILITY}
73+
addMappedType(ctx.StdlibModuleName, \
74+
ctx.getIdentifier(#SWIFT_NAME), \
75+
{OBJC_REPR, std::optional<StringRef>(C_REPR), \
76+
std::optional<StringRef>(C_REPR), NEEDS_NULLABILITY})
3277
#define MAP_CXX(SWIFT_NAME, OBJC_REPR, C_REPR, CXX_REPR, NEEDS_NULLABILITY) \
33-
mappedTypeNames[{ctx.StdlibModuleName, ctx.getIdentifier(#SWIFT_NAME)}] = { \
34-
OBJC_REPR, std::optional<StringRef>(C_REPR), \
35-
std::optional<StringRef>(CXX_REPR), NEEDS_NULLABILITY}
78+
addMappedType(ctx.StdlibModuleName, \
79+
ctx.getIdentifier(#SWIFT_NAME), \
80+
{OBJC_REPR, std::optional<StringRef>(C_REPR), \
81+
std::optional<StringRef>(CXX_REPR), NEEDS_NULLABILITY})
3682

3783
MAP(CBool, "bool", false);
3884

@@ -79,35 +125,37 @@ void PrimitiveTypeMapping::initialize(ASTContext &ctx) {
79125
MAP(UnsafeRawPointer, "void const *", true);
80126
MAP(UnsafeMutableRawPointer, "void *", true);
81127

82-
Identifier ID_ObjectiveC = ctx.Id_ObjectiveC;
83-
mappedTypeNames[{ID_ObjectiveC, ctx.getIdentifier("ObjCBool")}] = {
84-
"BOOL", std::nullopt, std::nullopt, false};
85-
mappedTypeNames[{ID_ObjectiveC, ctx.getIdentifier("Selector")}] = {
86-
"SEL", std::nullopt, std::nullopt, true};
87-
mappedTypeNames[{ID_ObjectiveC, ctx.getIdentifier(swift::getSwiftName(
88-
KnownFoundationEntity::NSZone))}] = {
89-
"struct _NSZone *", std::nullopt, std::nullopt, true};
128+
// Map other module types.
129+
130+
addMappedType(ctx.Id_ObjectiveC, ctx.getIdentifier("ObjCBool"),
131+
{"BOOL", std::nullopt, std::nullopt, false});
132+
addMappedType(ctx.Id_ObjectiveC, ctx.getIdentifier("Selector"),
133+
{"SEL", std::nullopt, std::nullopt, true});
134+
addMappedType(ctx.Id_ObjectiveC,
135+
ctx.getIdentifier(swift::getSwiftName(
136+
KnownFoundationEntity::NSZone)),
137+
{"struct _NSZone *", std::nullopt, std::nullopt, true});
90138

91-
mappedTypeNames[{ctx.Id_Darwin, ctx.getIdentifier("DarwinBoolean")}] = {
92-
"Boolean", std::nullopt, std::nullopt, false};
139+
addMappedType(ctx.Id_Darwin, ctx.getIdentifier("DarwinBoolean"),
140+
{"Boolean", std::nullopt, std::nullopt, false});
93141

94-
mappedTypeNames[{ctx.Id_CoreGraphics, ctx.Id_CGFloat}] = {
95-
"CGFloat", std::nullopt, std::nullopt, false};
142+
addMappedType(ctx.Id_CoreGraphics, ctx.Id_CGFloat,
143+
{"CGFloat", std::nullopt, std::nullopt, false});
96144

97-
mappedTypeNames[{ctx.Id_CoreFoundation, ctx.Id_CGFloat}] = {
98-
"CGFloat", std::nullopt, std::nullopt, false};
145+
addMappedType(ctx.Id_CoreFoundation, ctx.Id_CGFloat,
146+
{"CGFloat", std::nullopt, std::nullopt, false});
99147

100148
// Use typedefs we set up for SIMD vector types.
101149
#define MAP_SIMD_TYPE(BASENAME, _, __) \
102150
StringRef simd2##BASENAME = "swift_" #BASENAME "2"; \
103-
mappedTypeNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "2")}] = { \
104-
simd2##BASENAME, simd2##BASENAME, simd2##BASENAME, false}; \
151+
addMappedType(ctx.Id_simd, ctx.getIdentifier(#BASENAME "2"), \
152+
{simd2##BASENAME, simd2##BASENAME, simd2##BASENAME, false}); \
105153
StringRef simd3##BASENAME = "swift_" #BASENAME "3"; \
106-
mappedTypeNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "3")}] = { \
107-
simd3##BASENAME, simd3##BASENAME, simd3##BASENAME, false}; \
154+
addMappedType(ctx.Id_simd, ctx.getIdentifier(#BASENAME "3"), \
155+
{simd3##BASENAME, simd3##BASENAME, simd3##BASENAME, false}); \
108156
StringRef simd4##BASENAME = "swift_" #BASENAME "4"; \
109-
mappedTypeNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "4")}] = { \
110-
simd4##BASENAME, simd4##BASENAME, simd4##BASENAME, false};
157+
addMappedType(ctx.Id_simd, ctx.getIdentifier(#BASENAME "4"), \
158+
{simd4##BASENAME, simd4##BASENAME, simd4##BASENAME, false});
111159
#include "swift/ClangImporter/SIMDMappedTypes.def"
112160
static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4,
113161
"must add or remove special name mappings if max number of "
@@ -119,9 +167,11 @@ PrimitiveTypeMapping::getMappedTypeInfoOrNull(const TypeDecl *typeDecl) {
119167
if (mappedTypeNames.empty())
120168
initialize(typeDecl->getASTContext());
121169

122-
Identifier moduleName = typeDecl->getModuleContext()->getName();
123-
Identifier name = typeDecl->getName();
124-
auto iter = mappedTypeNames.find({moduleName, name});
170+
auto nominal = dyn_cast<TypeDecl>(typeDecl);
171+
if (!nominal)
172+
return nullptr;
173+
174+
auto iter = mappedTypeNames.find(nominal);
125175
if (iter == mappedTypeNames.end())
126176
return nullptr;
127177
return &iter->second;

lib/PrintAsClang/PrimitiveTypeMapping.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#ifndef SWIFT_PRINTASCLANG_PRIMITIVETYPEMAPPING_H
1414
#define SWIFT_PRINTASCLANG_PRIMITIVETYPEMAPPING_H
1515

16-
#include "swift/AST/Identifier.h"
16+
#include "swift/AST/Decl.h"
1717
#include "swift/Basic/LLVM.h"
1818
#include "llvm/ADT/DenseMap.h"
1919

@@ -62,14 +62,14 @@ class PrimitiveTypeMapping {
6262

6363
FullClangTypeInfo *getMappedTypeInfoOrNull(const TypeDecl *typeDecl);
6464

65-
/// A map from {Module, TypeName} pairs to {C name, C nullability} pairs.
65+
/// Associate Swift types to their {name, nullability} in foreign languages.
6666
///
6767
/// This is populated on first use with a list of known Swift types that are
6868
/// translated directly by the ObjC printer instead of structurally, allowing
6969
/// it to do things like map 'Int' to 'NSInteger' and 'Float' to 'float'.
7070
/// In some sense it's the reverse of the ClangImporter's MappedTypes.def.
71-
llvm::DenseMap<std::pair<Identifier, Identifier>, FullClangTypeInfo>
72-
mappedTypeNames;
71+
/// Must be kept aligned with BuiltinMappedTypes.def.
72+
llvm::DenseMap<TypeDecl*, FullClangTypeInfo> mappedTypeNames;
7373
};
7474

7575
} // end namespace swift

test/PrintAsObjC/unicode-scalar-reference.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@
1111

1212
@_cdecl("referencesScalar")
1313
func referencesScalar() -> Unicode.Scalar { fatalError() }
14-
// CHECK: SWIFT_EXTERN Scalar referencesScalar(void)
14+
// CHECK: SWIFT_EXTERN wchar_t referencesScalar(void)
15+
16+
@_cdecl("referencesRelated")
17+
func x_referencesRelated(a: CChar32, b: CWideChar) { }
18+
// CHECK: SWIFT_EXTERN void referencesRelated(char32_t a, wchar_t b)

0 commit comments

Comments
 (0)