Skip to content

Fixup Extended Existential Metatype Casts #59507

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 3 commits into from
Jun 17, 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: 1 addition & 1 deletion lib/IRGen/GenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
/// that the actual value isn't changed in any way, thus preserving its
/// reference identity.
///
/// These restrictions are set by canUseScalarCheckedCastInstructions.
/// These restrictions are set by \c canSILUseScalarCheckedCastInstructions.
/// Essentially, both the source and target types must be one of:
/// - a (possibly generic) concrete class type,
/// - a class-bounded archetype,
Expand Down
8 changes: 8 additions & 0 deletions lib/SIL/Utils/DynamicCasts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,14 @@ bool swift::canSILUseScalarCheckedCastInstructions(SILModule &M,
bool swift::canIRGenUseScalarCheckedCastInstructions(SILModule &M,
CanType sourceFormalType,
CanType targetFormalType) {
// If the cast involves any kind of generalized existential we
// need to use the indirect-cast path to handle checking the extra
// constraints there as the scalar path does not (yet) know how to do it.
if (sourceFormalType->hasParameterizedExistential() ||
targetFormalType->hasParameterizedExistential()) {
return false;
}

// Look through one level of optionality on the source.
auto objectType = sourceFormalType;
if (auto type = objectType.getOptionalObjectType())
Expand Down
6 changes: 5 additions & 1 deletion stdlib/public/runtime/Demangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,11 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type,
return proto_list;
}
case MetadataKind::ExtendedExistential: {
swift_unreachable("Extended existentials not supported");
// FIXME: Implement this by demangling the extended existential and
// substituting the generalization arguments into the demangle tree.
// For now, unconditional casts will report '<<< invalid type >>>' when
// they fail.
return nullptr;
}
case MetadataKind::ExistentialMetatype: {
auto metatype = static_cast<const ExistentialMetatypeMetadata *>(type);
Expand Down
33 changes: 32 additions & 1 deletion stdlib/public/runtime/DynamicCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,37 @@ static DynamicCastResult tryCastToExtendedExistential(
auto *destExistentialShape = destExistentialType->Shape;
const unsigned shapeArgumentCount =
destExistentialShape->getGenSigArgumentLayoutSizeInWords();
const Metadata *selfType = srcType;

// If we have a type expression to look into, unwrap as much metatype
// structure as possible so we can reach the type metadata for the 'Self'
// parameter.
if (destExistentialShape->Flags.hasTypeExpression()) {
Demangler dem;
auto *node = dem.demangleType(destExistentialShape->getTypeExpression()->name.get());
if (!node)
return DynamicCastResult::Failure;

while (node->getKind() == Demangle::Node::Kind::Type &&
node->getNumChildren() &&
node->getChild(0)->getKind() == Demangle::Node::Kind::Metatype &&
node->getChild(0)->getNumChildren()) {
auto *metatypeMetadata = dyn_cast<MetatypeMetadata>(selfType);
if (!metatypeMetadata)
return DynamicCastResult::Failure;

selfType = metatypeMetadata->InstanceType;
node = node->getChild(0)->getChild(0);
}

// Make sure the thing we've pulled out at the end is a dependent
// generic parameter.
if (!(node->getKind() == Demangle::Node::Kind::Type &&
node->getNumChildren() &&
node->getChild(0)->getKind() ==
Demangle::Node::Kind::DependentGenericParamType))
return DynamicCastResult::Failure;
}

llvm::SmallVector<const void *, 8> allGenericArgsVec;
unsigned witnessesMark = 0;
Expand All @@ -1789,7 +1820,7 @@ static DynamicCastResult tryCastToExtendedExistential(
auto genArgs = destExistentialType->getGeneralizationArguments();
allGenericArgsVec.append(genArgs, genArgs + shapeArgumentCount);
// Tack on the `Self` argument.
allGenericArgsVec.push_back((const void *)srcType);
allGenericArgsVec.push_back((const void *)selfType);
// Mark the point where the generic arguments end.
// _checkGenericRequirements is going to fill in a set of witness tables
// after that.
Expand Down
4 changes: 1 addition & 3 deletions test/Casting/ParameterizedExistentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ tests.test("Parameterized existential casting basics work") {
expectNil(d)
}

tests.test("Metatype existential casting basics work")
.xfail(.custom({ true }, reason: "IRGen peepholes these casts"))
.code {
tests.test("Metatype existential casting basics work") {
let a = GenericHolder<Int>.self as any Holder<Int>.Type
let b = GenericHolder<Int>.self as! any Holder<Int>.Type
expectTrue(a == b)
Expand Down