Skip to content

Fix nested generic typerefs applying generic params at the wrong level #59262

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 1 commit into from
Jun 30, 2022
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
2 changes: 2 additions & 0 deletions include/swift/AST/ASTDemangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class ASTBuilder {
using BuiltRequirement = swift::Requirement;
using BuiltSubstitutionMap = swift::SubstitutionMap;

static constexpr bool needsToPrecomputeParentGenericContextShapes = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we Doxygen-document what this parameter means?


explicit ASTBuilder(ASTContext &ctx) : Ctx(ctx) {}

ASTContext &getASTContext() { return Ctx; }
Expand Down
128 changes: 110 additions & 18 deletions include/swift/Reflection/TypeRefBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,24 @@ struct FieldTypeCollectionResult {
std::vector<std::string> Errors;
};

struct TypeRefDecl {
std::string mangledName;

// Only used when building a bound generic typeref, and when the
// generic params for all the levels are stored as a flat array.
llvm::Optional<std::vector<size_t>> genericParamsPerLevel;

TypeRefDecl(std::string mangledName,
std::vector<size_t> genericParamsPerLevel)
: mangledName(mangledName),
genericParamsPerLevel(genericParamsPerLevel) {}

TypeRefDecl(std::string mangledName)
: mangledName(mangledName),
genericParamsPerLevel(llvm::None) {}

};

/// An implementation of MetadataReader's BuilderType concept for
/// building TypeRefs, and parsing field metadata from any images
/// it has been made aware of.
Expand All @@ -366,7 +384,7 @@ class TypeRefBuilder {

public:
using BuiltType = const TypeRef *;
using BuiltTypeDecl = llvm::Optional<std::string>;
using BuiltTypeDecl = llvm::Optional<TypeRefDecl>;
using BuiltProtocolDecl =
llvm::Optional<std::pair<std::string, bool /*isObjC*/>>;
using BuiltSubstitution = std::pair<const TypeRef *, const TypeRef *>;
Expand All @@ -376,6 +394,8 @@ class TypeRefBuilder {
using BuiltGenericSignature = const GenericSignatureRef *;
using BuiltSubstitutionMap = llvm::DenseMap<DepthAndIndex, const TypeRef *>;

static constexpr bool needsToPrecomputeParentGenericContextShapes = true;

TypeRefBuilder(const TypeRefBuilder &other) = delete;
TypeRefBuilder &operator=(const TypeRefBuilder &other) = delete;

Expand Down Expand Up @@ -433,12 +453,30 @@ class TypeRefBuilder {
return BuiltinTypeRef::create(*this, mangledName);
}

llvm::Optional<std::string> createTypeDecl(Node *node, bool &typeAlias) {
BuiltTypeDecl createTypeDecl(Node *node, std::vector<size_t> paramsPerLevel) {
auto mangling = Demangle::mangleNode(node);
if (!mangling.isSuccess()) {
return llvm::None;
}
return {{mangling.result(), paramsPerLevel}};
}

BuiltTypeDecl createTypeDecl(std::string &&mangledName,
std::vector<size_t> paramsPerLevel) {
return {{std::move(mangledName), {paramsPerLevel}}};
}

BuiltTypeDecl createTypeDecl(Node *node, bool &typeAlias) {
auto mangling = Demangle::mangleNode(node);
if (!mangling.isSuccess()) {
return llvm::None;
}
return mangling.result();
return {{mangling.result()}};
}

BuiltTypeDecl createTypeDecl(std::string &&mangledName,
bool &typeAlias) {
return {{(mangledName)}};;
}

BuiltProtocolDecl
Expand All @@ -455,24 +493,20 @@ class TypeRefBuilder {
return std::make_pair(name, true);
}

llvm::Optional<std::string> createTypeDecl(std::string &&mangledName,
bool &typeAlias) {
return std::move(mangledName);
}

const NominalTypeRef *
createNominalType(const llvm::Optional<std::string> &mangledName) {
return NominalTypeRef::create(*this, *mangledName, nullptr);
createNominalType(const BuiltTypeDecl &typeRefDecl) {
return NominalTypeRef::create(*this, typeRefDecl->mangledName, nullptr);
}

const NominalTypeRef *
createNominalType(const llvm::Optional<std::string> &mangledName,
createNominalType(const BuiltTypeDecl &typeRefDecl,
const TypeRef *parent) {
return NominalTypeRef::create(*this, *mangledName, parent);
return NominalTypeRef::create(*this, typeRefDecl->mangledName, parent);
}

const TypeRef *
createTypeAliasType(const llvm::Optional<std::string> &mangledName,
createTypeAliasType(const BuiltTypeDecl &typeRefDecl,
const TypeRef *parent) {
// TypeRefs don't contain sugared types
return nullptr;
Expand All @@ -498,17 +532,75 @@ class TypeRefBuilder {
return nullptr;
}

const BoundGenericTypeRef *createBoundGenericTypeReconstructingParent(
const NodePointer node, const TypeRefDecl &decl, size_t shapeIndex,
const llvm::ArrayRef<const TypeRef *> &args, size_t argsIndex) {
if (!node || !node->hasChildren())
return nullptr;

auto maybeGenericParamsPerLevel = decl.genericParamsPerLevel;
if (!maybeGenericParamsPerLevel)
return nullptr;

auto genericParamsPerLevel = *maybeGenericParamsPerLevel;

auto kind = node->getKind();
// Kinds who have a "BoundGeneric..." variant.
if (kind != Node::Kind::Class && kind != Node::Kind::Structure &&
kind != Node::Kind::Enum && kind != Node::Kind::Protocol &&
kind != Node::Kind::OtherNominalType && kind != Node::Kind::TypeAlias &&
kind != Node::Kind::Function)
return nullptr;
auto mangling = Demangle::mangleNode(node);
if (!mangling.isSuccess())
return nullptr;

auto numGenericArgs = genericParamsPerLevel[shapeIndex];

std::vector<const TypeRef *> genericParams(
args.end() - argsIndex - numGenericArgs, args.end() - argsIndex);

const BoundGenericTypeRef *parent = nullptr;
if (node->hasChildren())
parent = createBoundGenericTypeReconstructingParent(
node->getFirstChild(), decl, --shapeIndex, args, argsIndex + numGenericArgs);

return BoundGenericTypeRef::create(*this, mangling.result(), genericParams,
parent);
}

const BoundGenericTypeRef *
createBoundGenericType(const llvm::Optional<std::string> &mangledName,
const std::vector<const TypeRef *> &args) {
return BoundGenericTypeRef::create(*this, *mangledName, args, nullptr);
createBoundGenericType(const BuiltTypeDecl &builtTypeDecl,
const llvm::ArrayRef<const TypeRef *> &args) {
if (!builtTypeDecl)
return nullptr;

if (!builtTypeDecl->genericParamsPerLevel)
return BoundGenericTypeRef::create(*this, builtTypeDecl->mangledName, args, nullptr);


auto node = Dem.demangleType(builtTypeDecl->mangledName);
if (!node || !node->hasChildren() || node->getKind() != Node::Kind::Type)
return nullptr;

auto type = node->getFirstChild();
return createBoundGenericTypeReconstructingParent(
type, *builtTypeDecl, builtTypeDecl->genericParamsPerLevel->size() - 1, args, 0);
}

const BoundGenericTypeRef *
createBoundGenericType(const llvm::Optional<std::string> &mangledName,
createBoundGenericType(const BuiltTypeDecl &builtTypeDecl,
llvm::ArrayRef<const TypeRef *> args,
const TypeRef *parent) {
return BoundGenericTypeRef::create(*this, *mangledName, args, parent);
if (!builtTypeDecl)
return nullptr;

if (!builtTypeDecl->genericParamsPerLevel)
return BoundGenericTypeRef::create(*this, builtTypeDecl->mangledName, args,
parent);
assert(parent == nullptr &&
"Parent is not null but we're reconstructing the parent!");
return createBoundGenericType(builtTypeDecl, args);
}

const TypeRef *
Expand Down Expand Up @@ -627,7 +719,7 @@ class TypeRefBuilder {
if (protocol->second) {
return llvm::cast<TypeRef>(createObjCProtocolType(protocol->first));
} else {
return llvm::cast<TypeRef>(createNominalType(protocol->first));
return llvm::cast<TypeRef>(createNominalType(TypeRefDecl(protocol->first)));
}
}

Expand Down
46 changes: 44 additions & 2 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "swift/Runtime/HeapObject.h"
#include "swift/Basic/Unreachable.h"

#include <type_traits>
#include <vector>
#include <unordered_map>

Expand Down Expand Up @@ -2818,8 +2819,14 @@ class MetadataReader {

/// Given a read nominal type descriptor, attempt to build a
/// nominal type decl from it.
BuiltTypeDecl
buildNominalTypeDecl(ContextDescriptorRef descriptor) {
template <
typename T = BuilderType,
typename std::enable_if_t<
!std::is_same<
bool,
decltype(T::needsToPrecomputeParentGenericContextShapes)>::value,
bool> = true>
BuiltTypeDecl buildNominalTypeDecl(ContextDescriptorRef descriptor) {
// Build the demangling tree from the context tree.
Demangler dem;
auto node = buildContextMangling(descriptor, dem);
Expand All @@ -2830,6 +2837,41 @@ class MetadataReader {
return decl;
}

template <
typename T = BuilderType,
typename std::enable_if_t<
std::is_same<
bool,
decltype(T::needsToPrecomputeParentGenericContextShapes)>::value,
bool> = true>
BuiltTypeDecl buildNominalTypeDecl(ContextDescriptorRef descriptor) {
// Build the demangling tree from the context tree.
Demangler dem;
auto node = buildContextMangling(descriptor, dem);
if (!node || node->getKind() != Node::Kind::Type)
return BuiltTypeDecl();
std::vector<size_t> paramsPerLevel;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llvm::SmallVector<size_t, 4> ?

size_t runningCount = 0;
std::function<void(ContextDescriptorRef current, size_t &)> countLevels =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment what the lambda does?

[&](ContextDescriptorRef current, size_t &runningCount) {
if (auto parentContextRef = readParentContextDescriptor(current))
if (parentContextRef->isResolved())
if (auto parentContext = parentContextRef->getResolved())
countLevels(parentContext, runningCount);

auto genericContext = current->getGenericContext();
if (!genericContext)
return;
auto contextHeader = genericContext->getGenericContextHeader();

paramsPerLevel.emplace_back(contextHeader.NumParams - runningCount);
runningCount += paramsPerLevel.back();
};
countLevels(descriptor, runningCount);
BuiltTypeDecl decl = Builder.createTypeDecl(node, paramsPerLevel);
return decl;
}

#if SWIFT_OBJC_INTEROP
std::string readObjCProtocolName(StoredPointer Address) {
auto Size = sizeof(TargetObjCProtocolPrefix<Runtime>);
Expand Down
31 changes: 29 additions & 2 deletions stdlib/public/Reflection/TypeRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -579,8 +579,35 @@ class DemanglingForTypeRef
genericNode->addChild(unspecializedType, Dem);
genericNode->addChild(genericArgsList, Dem);

if (auto parent = BG->getParent())
assert(false && "not implemented");
auto parent = BG->getParent();
if (!parent)
return genericNode;

auto parentNode = visit(parent);
if (!parentNode || !parentNode->hasChildren() ||
parentNode->getKind() != Node::Kind::Type ||
!unspecializedType->hasChildren())
return genericNode;

// Peel off the "Type" node.
parentNode = parentNode->getFirstChild();

auto nominalNode = unspecializedType->getFirstChild();

if (nominalNode->getNumChildren() != 2)
return genericNode;

// Save identifier for reinsertion later, we have to remove it
// so we can insert the parent node as the first child.
auto identifierNode = nominalNode->getLastChild();

// Remove all children.
nominalNode->removeChildAt(1);
nominalNode->removeChildAt(0);

// Add the parent we just visited back in, followed by the identifier.
nominalNode->addChild(parentNode, Dem);
nominalNode->addChild(identifierNode, Dem);

return genericNode;
}
Expand Down
Loading