Skip to content

[4.2] Runtime: Handle synthesized decl "related entity" tags. #17603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,12 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
///
/// Meaningful for all type-descriptor kinds.
IsReflectable = 2,

/// Set if the type is a Clang-importer-synthesized related entity. After
/// the null terminator for the type name is another null-terminated string
/// containing the tag that discriminates the entity from other synthesized
/// declarations associated with the same declaration.
IsSynthesizedRelatedEntity = 3,

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

FLAGSET_DEFINE_FLAG_ACCESSORS(IsSynthesizedRelatedEntity,
isSynthesizedRelatedEntity,
setIsSynthesizedRelatedEntity)

FLAGSET_DEFINE_FLAG_ACCESSORS(Class_HasVTable,
class_hasVTable,
class_setHasVTable)
Expand Down
31 changes: 24 additions & 7 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -1136,20 +1136,30 @@ class MetadataReader {
Demangle::NodePointer
buildNominalTypeMangling(ContextDescriptorRef descriptor,
Demangle::NodeFactory &nodeFactory) {
std::vector<std::pair<Demangle::Node::Kind, std::string>>
std::vector<std::pair<Demangle::Node::Kind, Demangle::NodePointer>>
nameComponents;
ContextDescriptorRef parent = descriptor;

while (parent) {
std::string nodeName;
std::string relatedTag;
Demangle::Node::Kind nodeKind;

auto getTypeName = [&]() -> bool {
auto typeBuffer =
reinterpret_cast<const TargetTypeContextDescriptor<Runtime> *>
(parent.getLocalBuffer());
auto nameAddress = resolveRelativeField(parent, typeBuffer->Name);
return Reader->readString(RemoteAddress(nameAddress), nodeName);
if (!Reader->readString(RemoteAddress(nameAddress), nodeName))
return false;

if (typeBuffer->isSynthesizedRelatedEntity()) {
nameAddress += nodeName.size() + 1;
if (!Reader->readString(RemoteAddress(nameAddress), relatedTag))
return false;
}

return true;
};

bool isTypeContext = false;
Expand Down Expand Up @@ -1208,8 +1218,17 @@ class MetadataReader {
else if (typeFlags.isCTypedef())
nodeKind = Demangle::Node::Kind::TypeAlias;
}

auto nameNode = nodeFactory.createNode(Node::Kind::Identifier,
nodeName);
if (!relatedTag.empty()) {
auto relatedNode =
nodeFactory.createNode(Node::Kind::RelatedEntityDeclName, relatedTag);
relatedNode->addChild(nameNode, nodeFactory);
nameNode = relatedNode;
}

nameComponents.emplace_back(nodeKind, nodeName);
nameComponents.emplace_back(nodeKind, nameNode);

parent = readParentContextDescriptor(parent);
}
Expand All @@ -1222,13 +1241,11 @@ class MetadataReader {
auto moduleInfo = std::move(nameComponents.back());
nameComponents.pop_back();
auto demangling =
nodeFactory.createNode(Node::Kind::Module, moduleInfo.second);
nodeFactory.createNode(Node::Kind::Module, moduleInfo.second->getText());
for (auto &component : reversed(nameComponents)) {
auto name = nodeFactory.createNode(Node::Kind::Identifier,
component.second);
auto parent = nodeFactory.createNode(component.first);
parent->addChild(demangling, nodeFactory);
parent->addChild(name, nodeFactory);
parent->addChild(component.second, nodeFactory);
demangling = parent;
}

Expand Down
15 changes: 15 additions & 0 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -3511,6 +3511,21 @@ class TargetTypeContextDescriptor

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

bool isSynthesizedRelatedEntity() const {
return getTypeContextDescriptorFlags().isSynthesizedRelatedEntity();
}

/// Return the tag used to discriminate declarations synthesized by the
/// Clang importer and give them stable identities.
StringRef getSynthesizedDeclRelatedEntityTag() const {
if (!isSynthesizedRelatedEntity())
return {};
// The tag name comes after the null terminator for the name.
const char *nameBegin = Name.get();
auto *nameEnd = nameBegin + strlen(nameBegin) + 1;
return nameEnd;
}

/// Return the offset of the start of generic arguments in the nominal
/// type's metadata. The returned value is measured in sizeof(void*).
int32_t getGenericArgumentOffset() const;
Expand Down
38 changes: 22 additions & 16 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,11 +481,21 @@ namespace {
}

void addName() {
SmallString<32> nameBuf;
StringRef name;


// Use the original name with tag for synthesized decls. The tag comes
// after the null terminator for the name.
if (auto *synthesizedTypeAttr =
Type->getAttrs().getAttribute<ClangImporterSynthesizedTypeAttr>()) {
nameBuf.append(synthesizedTypeAttr->originalTypeName);
nameBuf.push_back('\0');
nameBuf.append(synthesizedTypeAttr->getManglingName());

name = nameBuf;
// Try to use the Clang name if there is one.
if (auto namedClangDecl =
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
} else if (auto namedClangDecl =
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
name = namedClangDecl->getName();
} else {
name = Type->getName().str();
Expand Down Expand Up @@ -565,22 +575,18 @@ namespace {
/// Flags to indicate Clang-imported declarations so we mangle them
/// consistently at runtime.
void getClangImportedFlags(TypeContextDescriptorFlags &flags) const {
auto clangDecl = Mangle::ASTMangler::getClangDeclForMangling(Type);
if (!clangDecl)
return;

if (isa<clang::TagDecl>(clangDecl)) {
flags.setIsCTag(true);
return;
if (Type->getAttrs().getAttribute<ClangImporterSynthesizedTypeAttr>()) {
flags.setIsSynthesizedRelatedEntity(true);
}

if (isa<clang::TypedefNameDecl>(clangDecl)
|| isa<clang::ObjCCompatibleAliasDecl>(clangDecl)) {
flags.setIsCTypedef(true);
return;
if (auto clangDecl = Mangle::ASTMangler::getClangDeclForMangling(Type)) {
if (isa<clang::TagDecl>(clangDecl)) {
flags.setIsCTag(true);
} else if (isa<clang::TypedefNameDecl>(clangDecl)
|| isa<clang::ObjCCompatibleAliasDecl>(clangDecl)) {
flags.setIsCTypedef(true);
}
}

return;
}

// Subclasses should provide:
Expand Down
12 changes: 11 additions & 1 deletion lib/RemoteAST/RemoteAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,17 @@ RemoteASTTypeBuilder::findForeignNominalTypeDecl(StringRef name,
if (HadError) return;
if (decl == Result) return;
if (!Result) {
Result = cast<NominalTypeDecl>(decl);
// A synthesized type from the Clang importer may resolve to a
// compatibility alias.
if (auto resultAlias = dyn_cast<TypeAliasDecl>(decl)) {
if (resultAlias->isCompatibilityAlias()) {
Result = resultAlias->getUnderlyingTypeLoc().getType()
->getAnyNominal();
}
} else {
Result = dyn_cast<NominalTypeDecl>(decl);
}
HadError |= !Result;
} else {
HadError = true;
Result = nullptr;
Expand Down
10 changes: 8 additions & 2 deletions stdlib/public/runtime/Demangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,14 @@ swift::_buildDemanglingForContext(const ContextDescriptor *context,

auto typeNode = Dem.createNode(nodeKind);
typeNode->addChild(node, Dem);
auto identifier = Dem.createNode(Node::Kind::Identifier, name);
typeNode->addChild(identifier, Dem);
auto nameNode = Dem.createNode(Node::Kind::Identifier, name);
if (type->isSynthesizedRelatedEntity()) {
auto relatedName = Dem.createNode(Node::Kind::RelatedEntityDeclName,
type->getSynthesizedDeclRelatedEntityTag());
relatedName->addChild(nameNode, Dem);
nameNode = relatedName;
}
typeNode->addChild(nameNode, Dem);
node = typeNode;

// Apply generic arguments if the context is generic.
Expand Down
15 changes: 14 additions & 1 deletion stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,20 @@ bool swift::equalContexts(const ContextDescriptor *a,
&& kind <= ContextDescriptorKind::Type_Last) {
auto typeA = cast<TypeContextDescriptor>(a);
auto typeB = cast<TypeContextDescriptor>(b);
return strcmp(typeA->Name.get(), typeB->Name.get()) == 0;
if (strcmp(typeA->Name.get(), typeB->Name.get()) != 0)
return false;

// A synthesized entity has to match the related entity tag too.
if (typeA->isSynthesizedRelatedEntity()) {
if (!typeB->isSynthesizedRelatedEntity())
return false;

if (typeA->getSynthesizedDeclRelatedEntityTag()
!= typeB->getSynthesizedDeclRelatedEntityTag())
return false;
}

return true;
}

// Otherwise, this runtime doesn't know anything about this context kind.
Expand Down
42 changes: 35 additions & 7 deletions stdlib/public/runtime/MetadataLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,31 @@ swift::_contextDescriptorMatchesMangling(const ContextDescriptor *context,
}

auto nameNode = node->getChild(1);
if (nameNode->getKind() == Demangle::Node::Kind::PrivateDeclName)
return false;

if (nameNode->getText() != type->Name.get())

// Declarations synthesized by the Clang importer get a small tag
// string in addition to their name.
if (nameNode->getKind() == Demangle::Node::Kind::RelatedEntityDeclName){
if (nameNode->getText() != type->getSynthesizedDeclRelatedEntityTag())
return false;

nameNode = nameNode->getChild(0);
} else if (type->isSynthesizedRelatedEntity()) {
return false;
}

// We should only match public or internal declarations with stable
// names. The runtime metadata for private declarations would be
// anonymized.
if (nameNode->getKind() == Demangle::Node::Kind::Identifier) {
if (nameNode->getText() != type->Name.get())
return false;

node = node->getChild(0);
break;
}

node = node->getChild(0);
break;
return false;

}

// We don't know about this kind of context, or it doesn't have a stable
Expand Down Expand Up @@ -1166,7 +1183,7 @@ void swift::_swift_getFieldAt(
if (typeInfo == nullptr) {
typeInfo = TypeInfo(&METADATA_SYM(EMPTY_TUPLE_MANGLING), {});
warning(0, "SWIFT RUNTIME BUG: unable to demangle type of field '%*s'. "
"mangled type name is '%*s'",
"mangled type name is '%*s'\n",
(int)name.size(), name.data(),
(int)typeName.size(), typeName.data());
}
Expand Down Expand Up @@ -1214,6 +1231,17 @@ void swift::_swift_getFieldAt(
return;
}
}

// If we failed to find the field descriptor metadata for the type, fall
// back to returning an empty tuple as a standin.
auto typeName = swift_getTypeName(base, /*qualified*/ true);
warning(0, "SWIFT RUNTIME BUG: unable to find field metadata for type '%*s'\n",
(int)typeName.length, typeName.data);
callback("unknown",
FieldType()
.withType(TypeInfo(&METADATA_SYM(EMPTY_TUPLE_MANGLING), {}))
.withIndirect(false)
.withWeak(false));
}

#define OVERRIDE_METADATALOOKUP COMPATIBILITY_OVERRIDE
Expand Down
9 changes: 9 additions & 0 deletions test/Runtime/Inputs/synthesized_decl_uniqueness.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import CoreLocation

public func getCLError() -> Any.Type {
return CLError.self
}

public func getCLErrorCode() -> Any.Type {
return CLError.Code.self
}
11 changes: 11 additions & 0 deletions test/Runtime/demangleToMetadataObjC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import StdlibUnittest
import Foundation
import CoreFoundation
import CoreLocation

let DemangleToMetadataTests = TestSuite("DemangleToMetadataObjC")

Expand Down Expand Up @@ -74,5 +75,15 @@ DemangleToMetadataTests.test("@objc protocol conformances") {
expectNil(_typeByMangledName("4main3CG4CyAA1DCAA1DCG"))
}

DemangleToMetadataTests.test("synthesized declarations") {
expectEqual(CLError.self, _typeByMangledName("SC7CLErrorLeV")!)
expectNil(_typeByMangledName("SC7CLErrorV"))
expectEqual(CLError.Code.self, _typeByMangledName("So7CLErrorV")!)

let error = NSError(domain: NSCocoaErrorDomain, code: 0)
let reflectionString = String(reflecting: CLError(_nsError: error))
expectTrue(reflectionString.hasPrefix("__C_Synthesized.related decl 'e' for CLError(_nsError:"))
}

runAllTests()

21 changes: 21 additions & 0 deletions test/Runtime/synthesized_decl_uniqueness.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: %empty-directory(%t)
// 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
// 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
// RUN: %target-build-swift -I %t %s %t/A.o %t/B.o -o %t/a.out
// RUN: %target-run %t/a.out

// REQUIRES: executable_test
// REQUIRES: objc_interop

import StdlibUnittest
import A
import B

var tests = TestSuite("metadata identity for synthesized types")

tests.test("synthesized type identity across modules") {
expectEqual(A.getCLError(), B.getCLError())
expectEqual(A.getCLErrorCode(), B.getCLErrorCode())
}

runAllTests()