Skip to content

Commit 43b7b7e

Browse files
committed
Runtime: Handle synthesized decl "related entity" tags.
Clang-importer-synthesized declarations get an extra tag character included in their mangling, which was not being preserved in type context descriptors. This caused runtime lookup for these synthesized types to fail. Fix this by adding the tag information to type context descriptors and teaching the runtime to match it up when fetching metadata by mangled name. Fixes rdar://problem/40878715.
1 parent cc0983e commit 43b7b7e

File tree

11 files changed

+168
-33
lines changed

11 files changed

+168
-33
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,12 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
11761176
///
11771177
/// Meaningful for all type-descriptor kinds.
11781178
IsReflectable = 2,
1179+
1180+
/// Set if the type is a Clang-importer-synthesized related entity. After
1181+
/// the null terminator for the type name is another null-terminated string
1182+
/// containing the tag that discriminates the entity from other synthesized
1183+
/// declarations associated with the same declaration.
1184+
IsSynthesizedRelatedEntity = 3,
11791185

11801186
/// Set if the context descriptor is includes metadata for dynamically
11811187
/// constructing a class's vtables at metadata instantiation time.
@@ -1208,6 +1214,10 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
12081214
FLAGSET_DEFINE_FLAG_ACCESSORS(IsCTypedef, isCTypedef, setIsCTypedef)
12091215
FLAGSET_DEFINE_FLAG_ACCESSORS(IsReflectable, isReflectable, setIsReflectable)
12101216

1217+
FLAGSET_DEFINE_FLAG_ACCESSORS(IsSynthesizedRelatedEntity,
1218+
isSynthesizedRelatedEntity,
1219+
setIsSynthesizedRelatedEntity)
1220+
12111221
FLAGSET_DEFINE_FLAG_ACCESSORS(Class_HasVTable,
12121222
class_hasVTable,
12131223
class_setHasVTable)

include/swift/Remote/MetadataReader.h

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,20 +1136,30 @@ class MetadataReader {
11361136
Demangle::NodePointer
11371137
buildNominalTypeMangling(ContextDescriptorRef descriptor,
11381138
Demangle::NodeFactory &nodeFactory) {
1139-
std::vector<std::pair<Demangle::Node::Kind, std::string>>
1139+
std::vector<std::pair<Demangle::Node::Kind, Demangle::NodePointer>>
11401140
nameComponents;
11411141
ContextDescriptorRef parent = descriptor;
11421142

11431143
while (parent) {
11441144
std::string nodeName;
1145+
std::string relatedTag;
11451146
Demangle::Node::Kind nodeKind;
11461147

11471148
auto getTypeName = [&]() -> bool {
11481149
auto typeBuffer =
11491150
reinterpret_cast<const TargetTypeContextDescriptor<Runtime> *>
11501151
(parent.getLocalBuffer());
11511152
auto nameAddress = resolveRelativeField(parent, typeBuffer->Name);
1152-
return Reader->readString(RemoteAddress(nameAddress), nodeName);
1153+
if (!Reader->readString(RemoteAddress(nameAddress), nodeName))
1154+
return false;
1155+
1156+
if (typeBuffer->isSynthesizedRelatedEntity()) {
1157+
nameAddress += nodeName.size() + 1;
1158+
if (!Reader->readString(RemoteAddress(nameAddress), relatedTag))
1159+
return false;
1160+
}
1161+
1162+
return true;
11531163
};
11541164

11551165
bool isTypeContext = false;
@@ -1208,8 +1218,17 @@ class MetadataReader {
12081218
else if (typeFlags.isCTypedef())
12091219
nodeKind = Demangle::Node::Kind::TypeAlias;
12101220
}
1221+
1222+
auto nameNode = nodeFactory.createNode(Node::Kind::Identifier,
1223+
nodeName);
1224+
if (!relatedTag.empty()) {
1225+
auto relatedNode =
1226+
nodeFactory.createNode(Node::Kind::RelatedEntityDeclName, relatedTag);
1227+
relatedNode->addChild(nameNode, nodeFactory);
1228+
nameNode = relatedNode;
1229+
}
12111230

1212-
nameComponents.emplace_back(nodeKind, nodeName);
1231+
nameComponents.emplace_back(nodeKind, nameNode);
12131232

12141233
parent = readParentContextDescriptor(parent);
12151234
}
@@ -1222,13 +1241,11 @@ class MetadataReader {
12221241
auto moduleInfo = std::move(nameComponents.back());
12231242
nameComponents.pop_back();
12241243
auto demangling =
1225-
nodeFactory.createNode(Node::Kind::Module, moduleInfo.second);
1244+
nodeFactory.createNode(Node::Kind::Module, moduleInfo.second->getText());
12261245
for (auto &component : reversed(nameComponents)) {
1227-
auto name = nodeFactory.createNode(Node::Kind::Identifier,
1228-
component.second);
12291246
auto parent = nodeFactory.createNode(component.first);
12301247
parent->addChild(demangling, nodeFactory);
1231-
parent->addChild(name, nodeFactory);
1248+
parent->addChild(component.second, nodeFactory);
12321249
demangling = parent;
12331250
}
12341251

include/swift/Runtime/Metadata.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3511,6 +3511,21 @@ class TargetTypeContextDescriptor
35113511

35123512
llvm::ArrayRef<GenericParamDescriptor> getGenericParams() const;
35133513

3514+
bool isSynthesizedRelatedEntity() const {
3515+
return getTypeContextDescriptorFlags().isSynthesizedRelatedEntity();
3516+
}
3517+
3518+
/// Return the tag used to discriminate declarations synthesized by the
3519+
/// Clang importer and give them stable identities.
3520+
StringRef getSynthesizedDeclRelatedEntityTag() const {
3521+
if (!isSynthesizedRelatedEntity())
3522+
return {};
3523+
// The tag name comes after the null terminator for the name.
3524+
const char *nameBegin = Name.get();
3525+
auto *nameEnd = nameBegin + strlen(nameBegin) + 1;
3526+
return nameEnd;
3527+
}
3528+
35143529
/// Return the offset of the start of generic arguments in the nominal
35153530
/// type's metadata. The returned value is measured in sizeof(void*).
35163531
int32_t getGenericArgumentOffset() const;

lib/IRGen/GenMeta.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,21 @@ namespace {
481481
}
482482

483483
void addName() {
484+
SmallString<32> nameBuf;
484485
StringRef name;
485-
486+
487+
// Use the original name with tag for synthesized decls. The tag comes
488+
// after the null terminator for the name.
489+
if (auto *synthesizedTypeAttr =
490+
Type->getAttrs().getAttribute<ClangImporterSynthesizedTypeAttr>()) {
491+
nameBuf.append(synthesizedTypeAttr->originalTypeName);
492+
nameBuf.push_back('\0');
493+
nameBuf.append(synthesizedTypeAttr->getManglingName());
494+
495+
name = nameBuf;
486496
// Try to use the Clang name if there is one.
487-
if (auto namedClangDecl =
488-
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
497+
} else if (auto namedClangDecl =
498+
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
489499
name = namedClangDecl->getName();
490500
} else {
491501
name = Type->getName().str();
@@ -565,22 +575,18 @@ namespace {
565575
/// Flags to indicate Clang-imported declarations so we mangle them
566576
/// consistently at runtime.
567577
void getClangImportedFlags(TypeContextDescriptorFlags &flags) const {
568-
auto clangDecl = Mangle::ASTMangler::getClangDeclForMangling(Type);
569-
if (!clangDecl)
570-
return;
571-
572-
if (isa<clang::TagDecl>(clangDecl)) {
573-
flags.setIsCTag(true);
574-
return;
578+
if (Type->getAttrs().getAttribute<ClangImporterSynthesizedTypeAttr>()) {
579+
flags.setIsSynthesizedRelatedEntity(true);
575580
}
576581

577-
if (isa<clang::TypedefNameDecl>(clangDecl)
578-
|| isa<clang::ObjCCompatibleAliasDecl>(clangDecl)) {
579-
flags.setIsCTypedef(true);
580-
return;
582+
if (auto clangDecl = Mangle::ASTMangler::getClangDeclForMangling(Type)) {
583+
if (isa<clang::TagDecl>(clangDecl)) {
584+
flags.setIsCTag(true);
585+
} else if (isa<clang::TypedefNameDecl>(clangDecl)
586+
|| isa<clang::ObjCCompatibleAliasDecl>(clangDecl)) {
587+
flags.setIsCTypedef(true);
588+
}
581589
}
582-
583-
return;
584590
}
585591

586592
// Subclasses should provide:

lib/RemoteAST/RemoteAST.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,17 @@ RemoteASTTypeBuilder::findForeignNominalTypeDecl(StringRef name,
753753
if (HadError) return;
754754
if (decl == Result) return;
755755
if (!Result) {
756-
Result = cast<NominalTypeDecl>(decl);
756+
// A synthesized type from the Clang importer may resolve to a
757+
// compatibility alias.
758+
if (auto resultAlias = dyn_cast<TypeAliasDecl>(decl)) {
759+
if (resultAlias->isCompatibilityAlias()) {
760+
Result = resultAlias->getUnderlyingTypeLoc().getType()
761+
->getAnyNominal();
762+
}
763+
} else {
764+
Result = dyn_cast<NominalTypeDecl>(decl);
765+
}
766+
HadError |= !Result;
757767
} else {
758768
HadError = true;
759769
Result = nullptr;

stdlib/public/runtime/Demangle.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,14 @@ swift::_buildDemanglingForContext(const ContextDescriptor *context,
160160

161161
auto typeNode = Dem.createNode(nodeKind);
162162
typeNode->addChild(node, Dem);
163-
auto identifier = Dem.createNode(Node::Kind::Identifier, name);
164-
typeNode->addChild(identifier, Dem);
163+
auto nameNode = Dem.createNode(Node::Kind::Identifier, name);
164+
if (type->isSynthesizedRelatedEntity()) {
165+
auto relatedName = Dem.createNode(Node::Kind::RelatedEntityDeclName,
166+
type->getSynthesizedDeclRelatedEntityTag());
167+
relatedName->addChild(nameNode, Dem);
168+
nameNode = relatedName;
169+
}
170+
typeNode->addChild(nameNode, Dem);
165171
node = typeNode;
166172

167173
// Apply generic arguments if the context is generic.

stdlib/public/runtime/Metadata.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,20 @@ bool swift::equalContexts(const ContextDescriptor *a,
14881488
&& kind <= ContextDescriptorKind::Type_Last) {
14891489
auto typeA = cast<TypeContextDescriptor>(a);
14901490
auto typeB = cast<TypeContextDescriptor>(b);
1491-
return strcmp(typeA->Name.get(), typeB->Name.get()) == 0;
1491+
if (strcmp(typeA->Name.get(), typeB->Name.get()) != 0)
1492+
return false;
1493+
1494+
// A synthesized entity has to match the related entity tag too.
1495+
if (typeA->isSynthesizedRelatedEntity()) {
1496+
if (!typeB->isSynthesizedRelatedEntity())
1497+
return false;
1498+
1499+
if (typeA->getSynthesizedDeclRelatedEntityTag()
1500+
!= typeB->getSynthesizedDeclRelatedEntityTag())
1501+
return false;
1502+
}
1503+
1504+
return true;
14921505
}
14931506

14941507
// Otherwise, this runtime doesn't know anything about this context kind.

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,14 +264,31 @@ swift::_contextDescriptorMatchesMangling(const ContextDescriptor *context,
264264
}
265265

266266
auto nameNode = node->getChild(1);
267-
if (nameNode->getKind() == Demangle::Node::Kind::PrivateDeclName)
268-
return false;
269-
270-
if (nameNode->getText() != type->Name.get())
267+
268+
// Declarations synthesized by the Clang importer get a small tag
269+
// string in addition to their name.
270+
if (nameNode->getKind() == Demangle::Node::Kind::RelatedEntityDeclName){
271+
if (nameNode->getText() != type->getSynthesizedDeclRelatedEntityTag())
272+
return false;
273+
274+
nameNode = nameNode->getChild(0);
275+
} else if (type->isSynthesizedRelatedEntity()) {
271276
return false;
277+
}
272278

273-
node = node->getChild(0);
274-
break;
279+
// We should only match public or internal declarations with stable
280+
// names. The runtime metadata for private declarations would be
281+
// anonymized.
282+
if (nameNode->getKind() == Demangle::Node::Kind::Identifier) {
283+
if (nameNode->getText() != type->Name.get())
284+
return false;
285+
286+
node = node->getChild(0);
287+
break;
288+
}
289+
290+
return false;
291+
275292
}
276293

277294
// We don't know about this kind of context, or it doesn't have a stable
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import CoreLocation
2+
3+
public func getCLError() -> Any.Type {
4+
return CLError.self
5+
}
6+
7+
public func getCLErrorCode() -> Any.Type {
8+
return CLError.Code.self
9+
}

test/Runtime/demangleToMetadataObjC.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import StdlibUnittest
66
import Foundation
77
import CoreFoundation
8+
import CoreLocation
89

910
let DemangleToMetadataTests = TestSuite("DemangleToMetadataObjC")
1011

@@ -74,5 +75,15 @@ DemangleToMetadataTests.test("@objc protocol conformances") {
7475
expectNil(_typeByMangledName("4main3CG4CyAA1DCAA1DCG"))
7576
}
7677

78+
DemangleToMetadataTests.test("synthesized declarations") {
79+
expectEqual(CLError.self, _typeByMangledName("SC7CLErrorLeV")!)
80+
expectNil(_typeByMangledName("SC7CLErrorV"))
81+
expectEqual(CLError.Code.self, _typeByMangledName("So7CLErrorV")!)
82+
83+
let error = NSError(domain: NSCocoaErrorDomain, code: 0)
84+
let reflectionString = String(reflecting: CLError(_nsError: error))
85+
expectTrue(reflectionString.hasPrefix("__C_Synthesized.related decl 'e' for CLError(_nsError:"))
86+
}
87+
7788
runAllTests()
7889

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -parse-as-library -force-single-frontend-invocation %S/Inputs/synthesized_decl_uniqueness.swift -emit-object -o %t/A.o -module-name A -emit-module-path %t/A.swiftmodule
3+
// RUN: %target-build-swift -parse-as-library -force-single-frontend-invocation %S/Inputs/synthesized_decl_uniqueness.swift -emit-object -o %t/B.o -module-name B -emit-module-path %t/B.swiftmodule
4+
// RUN: %target-build-swift -I %t %s %t/A.o %t/B.o -o %t/a.out
5+
// RUN: %target-run %t/a.out
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: objc_interop
9+
10+
import StdlibUnittest
11+
import A
12+
import B
13+
14+
var tests = TestSuite("metadata identity for synthesized types")
15+
16+
tests.test("synthesized type identity across modules") {
17+
expectEqual(A.getCLError(), B.getCLError())
18+
expectEqual(A.getCLErrorCode(), B.getCLErrorCode())
19+
}
20+
21+
runAllTests()

0 commit comments

Comments
 (0)