Skip to content

[5.7] Runtime Casting for Extended Existentials #59511

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 7 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
1 change: 1 addition & 0 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,7 @@ class ExtendedExistentialTypeShapeFlags {
SpecialKind getSpecialKind() const {
return SpecialKind((Data & SpecialKindMask) >> SpecialKindShift);
}
bool isOpaque() const { return getSpecialKind() == SpecialKind::None; }
bool isClassConstrained() const {
return getSpecialKind() == SpecialKind::Class;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,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
7 changes: 7 additions & 0 deletions stdlib/public/runtime/Demangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,13 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type,
// Just a simple composition of protocols.
return proto_list;
}
case MetadataKind::ExtendedExistential: {
// 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);
auto instance = _swift_buildDemanglingForMetadata(metatype->InstanceType,
Expand Down
193 changes: 189 additions & 4 deletions stdlib/public/runtime/DynamicCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1695,10 +1695,56 @@ tryCastUnwrappingExistentialSource(
return tryCast(destLocation, destType,
srcInnerValue, srcInnerType,
destFailureType, srcFailureType,
takeOnSuccess & (srcInnerValue == srcValue),
takeOnSuccess && (srcInnerValue == srcValue),
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch!

mayDeferChecks);
}

static DynamicCastResult tryCastUnwrappingExtendedExistentialSource(
OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue,
const Metadata *srcType, const Metadata *&destFailureType,
const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks) {
assert(srcType != destType);
assert(srcType->getKind() == MetadataKind::ExtendedExistential);

auto srcExistentialType = cast<ExtendedExistentialTypeMetadata>(srcType);

// Unpack the existential content
const Metadata *srcInnerType = nullptr;
OpaqueValue *srcInnerValue = nullptr;
switch (srcExistentialType->Shape->Flags.getSpecialKind()) {
case ExtendedExistentialTypeShape::SpecialKind::None: {
auto opaqueContainer =
reinterpret_cast<OpaqueExistentialContainer *>(srcValue);
srcInnerType = opaqueContainer->Type;
srcInnerValue = const_cast<OpaqueValue *>(opaqueContainer->projectValue());
break;
}
case ExtendedExistentialTypeShape::SpecialKind::Class: {
auto classContainer =
reinterpret_cast<ClassExistentialContainer *>(srcValue);
srcInnerType = swift_getObjectType((HeapObject *)classContainer->Value);
srcInnerValue = reinterpret_cast<OpaqueValue *>(&classContainer->Value);
break;
}
case ExtendedExistentialTypeShape::SpecialKind::Metatype: {
auto srcExistentialContainer =
reinterpret_cast<ExistentialMetatypeContainer *>(srcValue);
srcInnerType = swift_getMetatypeMetadata(srcExistentialContainer->Value);
srcInnerValue = reinterpret_cast<OpaqueValue *>(&srcExistentialContainer->Value);
break;
}
case ExtendedExistentialTypeShape::SpecialKind::ExplicitLayout: {
swift_unreachable("Explicit layout not yet implemented");
break;
}
}

srcFailureType = srcInnerType;
return tryCast(destLocation, destType, srcInnerValue, srcInnerType,
destFailureType, srcFailureType,
takeOnSuccess & (srcInnerValue == srcValue), mayDeferChecks);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be && here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should be! @rjmccall caught this the first time around. I'll patch this out on main.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

}

static DynamicCastResult
tryCastUnwrappingExistentialMetatypeSource(
OpaqueValue *destLocation, const Metadata *destType,
Expand All @@ -1722,6 +1768,134 @@ tryCastUnwrappingExistentialMetatypeSource(
mayDeferChecks);
}


static DynamicCastResult tryCastToExtendedExistential(
OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue,
const Metadata *srcType, const Metadata *&destFailureType,
const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks) {
assert(srcType != destType);
assert(destType->getKind() == MetadataKind::ExtendedExistential);

auto destExistentialType = cast<ExtendedExistentialTypeMetadata>(destType);
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;
{
// Line up the arguments to the requirement signature.
auto genArgs = destExistentialType->getGeneralizationArguments();
allGenericArgsVec.append(genArgs, genArgs + shapeArgumentCount);
// Tack on the `Self` argument.
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.
witnessesMark = allGenericArgsVec.size();

SubstGenericParametersFromMetadata substitutions(destExistentialShape,
allGenericArgsVec.data());
// Verify the requirements in the requirement signature against the
// arguments from the source value.
auto error = swift::_checkGenericRequirements(
destExistentialShape->getRequirementSignature().getRequirements(),
allGenericArgsVec,
[&substitutions](unsigned depth, unsigned index) {
return substitutions.getMetadata(depth, index);
},
[](const Metadata *type, unsigned index) -> const WitnessTable * {
swift_unreachable("Resolution of witness tables is not supported");
});
if (error)
return DynamicCastResult::Failure;
}

OpaqueValue *destBox = nullptr;
const WitnessTable **destWitnesses = nullptr;
switch (destExistentialShape->Flags.getSpecialKind()) {
case ExtendedExistentialTypeShape::SpecialKind::None: {
auto destExistential =
reinterpret_cast<OpaqueExistentialContainer *>(destLocation);

// Allocate a box and fill in the type information.
destExistential->Type = srcType;
destBox = srcType->allocateBoxForExistentialIn(&destExistential->Buffer);
destWitnesses = destExistential->getWitnessTables();
break;
}
case ExtendedExistentialTypeShape::SpecialKind::Class: {
auto destExistential =
reinterpret_cast<ClassExistentialContainer *>(destLocation);
destBox = reinterpret_cast<OpaqueValue *>(&destExistential->Value);
destWitnesses = destExistential->getWitnessTables();
break;
}
case ExtendedExistentialTypeShape::SpecialKind::Metatype: {
auto destExistential =
reinterpret_cast<ExistentialMetatypeContainer *>(destLocation);
destBox = reinterpret_cast<OpaqueValue *>(&destExistential->Value);
destWitnesses = destExistential->getWitnessTables();
break;
}
case ExtendedExistentialTypeShape::SpecialKind::ExplicitLayout:
swift_unreachable("Witnesses for explicit layout not yet implemented");
}

// Fill in the trailing set of witness tables.
const unsigned numWitnessTables = allGenericArgsVec.size() - witnessesMark;
assert(numWitnessTables ==
llvm::count_if(destExistentialShape->getRequirementSignature().getRequirements(),
[](const auto &req) -> bool {
return req.getKind() ==
GenericRequirementKind::Protocol;
}));
for (unsigned i = 0; i < numWitnessTables; ++i) {
const auto witness = i + witnessesMark;
destWitnesses[i] =
reinterpret_cast<const WitnessTable *>(allGenericArgsVec[witness]);
}

if (takeOnSuccess) {
srcType->vw_initializeWithTake(destBox, srcValue);
return DynamicCastResult::SuccessViaTake;
} else {
srcType->vw_initializeWithCopy(destBox, srcValue);
return DynamicCastResult::SuccessViaCopy;
}
}

/******************************************************************************/
/**************************** Opaque Destination ******************************/
/******************************************************************************/
Expand Down Expand Up @@ -2006,12 +2180,14 @@ static tryCastFunctionType *selectCasterForDest(const Metadata *destType) {
swift_unreachable(
"Unknown existential type representation in dynamic cast dispatch");
}
case MetadataKind::ExtendedExistential:
return tryCastToExtendedExistential;
case MetadataKind::Metatype:
return tryCastToMetatype;
case MetadataKind::ObjCClassWrapper:
return tryCastToMetatype;
case MetadataKind::ObjCClassWrapper:
return tryCastToObjectiveCClass;
case MetadataKind::ExistentialMetatype:
return tryCastToExistentialMetatype;
return tryCastToExistentialMetatype;
Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for fixing whitespace.

case MetadataKind::HeapLocalVariable:
case MetadataKind::HeapGenericLocalVariable:
case MetadataKind::ErrorObject:
Expand Down Expand Up @@ -2169,6 +2345,15 @@ tryCast(
break;
}

case MetadataKind::ExtendedExistential: {
auto subcastResult = tryCastUnwrappingExtendedExistentialSource(
destLocation, destType, srcValue, srcType, destFailureType,
srcFailureType, takeOnSuccess, mayDeferChecks);
if (isSuccess(subcastResult)) {
return subcastResult;
}
break;
}
default:
break;
}
Expand Down
47 changes: 43 additions & 4 deletions stdlib/public/runtime/MetadataLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2269,7 +2269,7 @@ static void installGetClassHook() {
unsigned SubstGenericParametersFromMetadata::
buildDescriptorPath(const ContextDescriptor *context,
Demangler &borrowFrom) const {
assert(sourceIsMetadata);
assert(sourceKind == SourceKind::Metadata);

// Terminating condition: we don't have a context.
if (!context)
Expand Down Expand Up @@ -2360,20 +2360,59 @@ buildEnvironmentPath(
return totalKeyParamCount;
}

unsigned SubstGenericParametersFromMetadata::buildShapePath(
const TargetExtendedExistentialTypeShape<InProcess> *shape) const {
unsigned totalParamCount = 0;

auto genSig = shape->getGeneralizationSignature();
if (!genSig.getParams().empty()) {
totalParamCount += genSig.getParams().size();
descriptorPath.push_back(PathElement{genSig.getParams(),
totalParamCount,
/*numKeyGenericParamsInParent*/ 0,
(unsigned)genSig.getParams().size(),
/*hasNonKeyGenericParams*/ false});
}

const unsigned genSigParamCount = genSig.getParams().size();
auto reqSig = shape->getRequirementSignature();
assert(reqSig.getParams().size() > genSig.getParams().size());
{
auto remainingParams = reqSig.getParams().drop_front(genSig.getParams().size());
totalParamCount += remainingParams.size();
descriptorPath.push_back(PathElement{remainingParams,
totalParamCount,
genSigParamCount,
(unsigned)remainingParams.size(),
/*hasNonKeyGenericParams*/ false});
}

// All parameters in this signature are key parameters.
return totalParamCount;
}

void SubstGenericParametersFromMetadata::setup() const {
if (!descriptorPath.empty())
return;

if (sourceIsMetadata && baseContext) {
switch (sourceKind) {
case SourceKind::Metadata: {
assert(baseContext);
DemanglerForRuntimeTypeResolution<StackAllocatedDemangler<2048>> demangler;
numKeyGenericParameters = buildDescriptorPath(baseContext, demangler);
return;
}

if (!sourceIsMetadata && environment) {
case SourceKind::Environment: {
assert(environment);
numKeyGenericParameters = buildEnvironmentPath(environment);
return;
}
case SourceKind::Shape: {
assert(shape);
numKeyGenericParameters = buildShapePath(shape);
return;
}
}
}

const Metadata *
Expand Down
Loading