Implement Casting For Extended Existentials

Implement casting to and from extended existentials. This is done by slightly generalizing the conditional conformances checking infrastructure.

Unfortunately, casts for reference types and metatypes are unsound because IRGen is peepholing all non-opaque existential conversions with a helper. I’ll disable that in a follow-up.

rdar://92197049
This commit is contained in:
Robert Widmann
2022-06-14 21:29:18 -06:00
parent 14edd57e3e
commit 9e4670a605
7 changed files with 334 additions and 155 deletions

View File

@@ -2125,16 +2125,6 @@ public:
return this->template getTrailingObjects<ConstTargetPointer<Runtime, void>>();
}
public:
/// Project the value pointer from an extended existential container of the
/// type described by this metadata.
const OpaqueValue *projectValue(const OpaqueValue *container) const;
OpaqueValue *projectValue(OpaqueValue *container) const {
return const_cast<OpaqueValue *>(
projectValue((const OpaqueValue *)container));
}
public:
static bool classof(const TargetMetadata<Runtime> *metadata) {
return metadata->getKind() == MetadataKind::ExtendedExistential;

View File

@@ -316,48 +316,6 @@ _buildDemanglingForNominalType(const Metadata *type, Demangle::Demangler &Dem) {
return _buildDemanglingForContext(description, demangledGenerics, Dem);
}
static Demangle::NodePointer
_buildDemanglingForProtocolDescriptor(ProtocolDescriptorRef protocol,
Demangle::Demangler &Dem) {
#if SWIFT_OBJC_INTEROP
if (protocol.isObjC()) {
// The protocol name is mangled as a type symbol, with the _Tt prefix.
StringRef ProtoName(protocol.getName());
NodePointer protocolNode = Dem.demangleSymbol(ProtoName);
// ObjC protocol names aren't mangled.
if (!protocolNode) {
auto module = Dem.createNode(Node::Kind::Module, MANGLING_MODULE_OBJC);
auto node = Dem.createNode(Node::Kind::Protocol);
node->addChild(module, Dem);
node->addChild(Dem.createNode(Node::Kind::Identifier, ProtoName), Dem);
auto typeNode = Dem.createNode(Node::Kind::Type);
typeNode->addChild(node, Dem);
return typeNode;
}
// Dig out the protocol node.
// Global -> (Protocol|TypeMangling)
protocolNode = protocolNode->getChild(0);
if (protocolNode->getKind() == Node::Kind::TypeMangling) {
protocolNode = protocolNode->getChild(0); // TypeMangling -> Type
protocolNode = protocolNode->getChild(0); // Type -> ProtocolList
protocolNode = protocolNode->getChild(0); // ProtocolList -> TypeList
protocolNode = protocolNode->getChild(0); // TypeList -> Type
assert(protocolNode->getKind() == Node::Kind::Type);
assert(protocolNode->getChild(0)->getKind() == Node::Kind::Protocol);
} else {
assert(protocolNode->getKind() == Node::Kind::Protocol);
}
return protocolNode;
}
#endif
return _buildDemanglingForContext(protocol.getSwiftProtocol(), {}, Dem);
}
// Build a demangled type tree for a type.
//
// FIXME: This should use MetadataReader.h.
@@ -403,7 +361,48 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type,
// its canonical ordering of protocols.
for (auto protocol : protocols) {
auto protocolNode = _buildDemanglingForProtocolDescriptor(protocol, Dem);
#if SWIFT_OBJC_INTEROP
if (protocol.isObjC()) {
// The protocol name is mangled as a type symbol, with the _Tt prefix.
StringRef ProtoName(protocol.getName());
NodePointer protocolNode = Dem.demangleSymbol(ProtoName);
// ObjC protocol names aren't mangled.
if (!protocolNode) {
auto module = Dem.createNode(Node::Kind::Module,
MANGLING_MODULE_OBJC);
auto node = Dem.createNode(Node::Kind::Protocol);
node->addChild(module, Dem);
node->addChild(Dem.createNode(Node::Kind::Identifier, ProtoName),
Dem);
auto typeNode = Dem.createNode(Node::Kind::Type);
typeNode->addChild(node, Dem);
type_list->addChild(typeNode, Dem);
continue;
}
// Dig out the protocol node.
// Global -> (Protocol|TypeMangling)
protocolNode = protocolNode->getChild(0);
if (protocolNode->getKind() == Node::Kind::TypeMangling) {
protocolNode = protocolNode->getChild(0); // TypeMangling -> Type
protocolNode = protocolNode->getChild(0); // Type -> ProtocolList
protocolNode = protocolNode->getChild(0); // ProtocolList -> TypeList
protocolNode = protocolNode->getChild(0); // TypeList -> Type
assert(protocolNode->getKind() == Node::Kind::Type);
assert(protocolNode->getChild(0)->getKind() == Node::Kind::Protocol);
} else {
assert(protocolNode->getKind() == Node::Kind::Protocol);
}
type_list->addChild(protocolNode, Dem);
continue;
}
#endif
auto protocolNode =
_buildDemanglingForContext(protocol.getSwiftProtocol(), { }, Dem);
if (!protocolNode)
return nullptr;
@@ -445,51 +444,7 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type,
return proto_list;
}
case MetadataKind::ExtendedExistential: {
auto exis = static_cast<const ExtendedExistentialTypeMetadata *>(type);
auto genSig = exis->Shape->getGeneralizationSignature();
const unsigned selfParamIdx = genSig.getParams().size();
auto node = Dem.createNode(Node::Kind::ParameterizedProtocol);
for (const auto &reqt :
exis->Shape->getRequirementSignature().getRequirements()) {
if (reqt.getKind() != GenericRequirementKind::Protocol) {
continue;
}
if (!reqt.Flags.hasKeyArgument()) {
continue;
}
auto lhsTypeNode = Dem.demangleType(reqt.getParam());
if (!lhsTypeNode || lhsTypeNode->getKind() != Node::Kind::Type ||
!lhsTypeNode->hasChildren() ||
lhsTypeNode->getChild(0)->getKind() !=
Node::Kind::DependentGenericParamType ||
lhsTypeNode->getChild(0)->getNumChildren() != 2) {
continue;
}
auto index = lhsTypeNode->getChild(0)->getChild(1)->getIndex();
if (index + 1 != selfParamIdx)
continue;
auto *protocolNode =
_buildDemanglingForProtocolDescriptor(reqt.getProtocol(), Dem);
if (!protocolNode)
continue;
node->addChild(protocolNode, Dem);
}
const unsigned shapeArgumentCount =
exis->Shape->getGenSigArgumentLayoutSizeInWords();
auto type_list = Dem.createNode(Node::Kind::TypeList);
for (unsigned i = 0; i < shapeArgumentCount; ++i) {
auto genArg = exis->getGeneralizationArguments()[i];
auto eltType =
_swift_buildDemanglingForMetadata((const Metadata *)genArg, Dem);
type_list->addChild(eltType, Dem);
}
node->addChild(type_list, Dem);
return node;
swift_unreachable("Extended existentials not supported");
}
case MetadataKind::ExistentialMetatype: {
auto metatype = static_cast<const ExistentialMetatypeMetadata *>(type);

View File

@@ -1696,10 +1696,56 @@ tryCastUnwrappingExistentialSource(
return tryCast(destLocation, destType,
srcInnerValue, srcInnerType,
destFailureType, srcFailureType,
takeOnSuccess & (srcInnerValue == srcValue),
takeOnSuccess && (srcInnerValue == srcValue),
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);
}
static DynamicCastResult
tryCastUnwrappingExistentialMetatypeSource(
OpaqueValue *destLocation, const Metadata *destType,
@@ -1723,6 +1769,103 @@ 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();
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 *)srcType);
// 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 ******************************/
/******************************************************************************/
@@ -2007,6 +2150,8 @@ 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:
@@ -2170,6 +2315,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;
}

View File

@@ -3980,32 +3980,6 @@ ExistentialTypeMetadata::projectValue(const OpaqueValue *container) const {
"Unhandled ExistentialTypeRepresentation in switch.");
}
template <>
const OpaqueValue *ExtendedExistentialTypeMetadata::projectValue(
const OpaqueValue *container) const {
switch (Shape->Flags.getSpecialKind()) {
case ExtendedExistentialTypeShape::SpecialKind::None: {
auto *opaqueContainer =
reinterpret_cast<const OpaqueExistentialContainer *>(container);
return opaqueContainer->projectValue();
}
case ExtendedExistentialTypeShape::SpecialKind::Class: {
auto classContainer =
reinterpret_cast<const ClassExistentialContainer *>(container);
return reinterpret_cast<const OpaqueValue *>(&classContainer->Value);
}
case ExtendedExistentialTypeShape::SpecialKind::Metatype: {
auto *metatypeContainer =
reinterpret_cast<const ExistentialMetatypeContainer *>(container);
return reinterpret_cast<const OpaqueValue *>(&metatypeContainer->Value);
}
case ExtendedExistentialTypeShape::SpecialKind::ExplicitLayout:
swift_unreachable("ExplicitLayout not yet handled.");
}
swift_unreachable("Unhandled ExistentialTypeRepresentation in switch.");
}
template<> const Metadata *
ExistentialTypeMetadata::getDynamicType(const OpaqueValue *container) const {
switch (getRepresentation()) {

View File

@@ -2374,49 +2374,32 @@ buildEnvironmentPath(
unsigned SubstGenericParametersFromMetadata::buildShapePath(
const TargetExtendedExistentialTypeShape<InProcess> *shape) const {
unsigned totalParamCount = 0;
unsigned totalKeyParamCount = 0;
auto genSig = shape->getGeneralizationSignature();
if (!genSig.getParams().empty()) {
bool hasNonKeyGenericParams = false;
unsigned numKeyGenericParamsHere = 0;
for (const auto &gp : genSig.getParams()) {
if (gp.hasKeyArgument())
numKeyGenericParamsHere++;
else
hasNonKeyGenericParams = true;
}
totalParamCount += numKeyGenericParamsHere;
totalKeyParamCount += numKeyGenericParamsHere;
descriptorPath.push_back(PathElement{genSig.getParams(), totalParamCount,
totalParamCount += genSig.getParams().size();
descriptorPath.push_back(PathElement{genSig.getParams(),
totalParamCount,
/*numKeyGenericParamsInParent*/ 0,
numKeyGenericParamsHere,
hasNonKeyGenericParams});
(unsigned)genSig.getParams().size(),
/*hasNonKeyGenericParams*/ false});
}
const unsigned genSigParamCount = genSig.getParams().size();
auto reqSig = shape->getRequirementSignature();
assert(reqSig.getParams().size() > genSig.getParams().size());
{
bool hasNonKeyGenericParams = false;
unsigned numKeyGenericParamsHere = 0;
auto remainingParams = reqSig.getParams().drop_front(genSig.getParams().size());
for (const auto &gp : remainingParams) {
if (gp.hasKeyArgument())
numKeyGenericParamsHere++;
else
hasNonKeyGenericParams = true;
}
totalParamCount += numKeyGenericParamsHere;
totalKeyParamCount += numKeyGenericParamsHere;
totalParamCount += remainingParams.size();
descriptorPath.push_back(PathElement{remainingParams,
totalParamCount,
genSigParamCount,
numKeyGenericParamsHere,
hasNonKeyGenericParams});
(unsigned)remainingParams.size(),
/*hasNonKeyGenericParams*/ false});
}
return totalKeyParamCount;
// All parameters in this signature are key parameters.
return totalParamCount;
}
void SubstGenericParametersFromMetadata::setup() const {

View File

@@ -0,0 +1,116 @@
// ParameterizedExistentials.swift - Tests for class constant casts w/ Obj-C
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
// -----------------------------------------------------------------------------
///
/// Tests for parameterized existential type conversions.
///
// -----------------------------------------------------------------------------
// RUN: %empty-directory(%t)
//
// RUN: %target-build-swift -swift-version 5 -g -Onone -Xfrontend -disable-availability-checking -module-name a -c %s -o %t/ParameterizedExistentials.swift.Onone.o
// RUN: %target-swiftc_driver %t/ParameterizedExistentials.swift.Onone.o -o %t/a.swift5.Onone.out
// RUN: %target-codesign %t/a.swift5.Onone.out
// RUN: %target-run %t/a.swift5.Onone.out
//
// RUN: %target-build-swift -swift-version 5 -g -O -Xfrontend -disable-availability-checking -module-name a -c %s -o %t/ParameterizedExistentials.swift.O.o
// RUN: %target-swiftc_driver %t/ParameterizedExistentials.swift.O.o -o %t/a.swift5.O.out
// RUN: %target-codesign %t/a.swift5.O.out
// RUN: %target-run %t/a.swift5.O.out
//
// REQUIRES: executable_test
// This test requires the new existential shape metadata accessors which are
// not available in on-device runtimes, or in the back-deployment runtime.
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime
import Swift
import StdlibUnittest
protocol Holder<T> {
associatedtype T
var value: T { get }
}
struct GenericHolder<T>: Holder {
var value: T
init(value: T) { self.value = value}
}
protocol PairType<T, U> {
associatedtype T
associatedtype U
var first: T { get }
var second: U { get }
}
struct Pair<T, U>: PairType {
var value: (T, U)
var first: T { self.value.0 }
var second: U { self.value.1 }
init(value: (T, U)) { self.value = value}
}
final class ReferencePair<T, U>: PairType {
var first: T
var second: U
init(value: (T, U)) { (self.first, self.second) = value }
}
let tests = TestSuite("ParameterizedExistentials")
tests.test("Parameterized existential casting basics work") {
let a = GenericHolder(value: 5) as any Holder<Int>
let b = GenericHolder(value: 5) as! any Holder<Int>
expectEqual(a.value, b.value)
let c = GenericHolder(value: 5) as? any Holder<Int>
expectNotNil(c)
let d = GenericHolder(value: 5) as? any Holder<String>
expectNil(d)
}
tests.test("Metatype existential casting basics work")
.xfail(.custom({ true }, reason: "IRGen peepholes these casts"))
.code {
let a = GenericHolder<Int>.self as any Holder<Int>.Type
let b = GenericHolder<Int>.self as! any Holder<Int>.Type
expectTrue(a == b)
let c = GenericHolder<Int>.self as? any Holder<Int>.Type
expectNotNil(c)
let d = GenericHolder<Int>.self as? any Holder<String>.Type
expectNil(d)
}
tests.test("Existential box should maintain identity") {
let a = Pair(value: ("Hello", 42))
var b/*ox*/ = a as? any PairType<String, Int>
expectNotNil(b)
expectEqual(a.value, (b!.first, b!.second))
let c = ReferencePair(value: ("Goodbye", 24))
let d = c as? any PairType<String, Int>
expectNotNil(d)
b = d!
expectEqual(b!.first, d!.first)
expectEqual(b!.second, d!.second)
let e = b as? ReferencePair<String, Int>
expectNotNil(e)
expectTrue(e! === c)
}
runAllTests()

View File

@@ -43,4 +43,11 @@ ParameterizedProtocolsTestSuite.test("metadataEquality") {
expectEqual(typeOne, typeTwo)
}
ParameterizedProtocolsTestSuite.test("casting") {
let a = GenericHolder(value: 5) as any Holder<Int>
let b = GenericHolder(value: 5) as! any Holder<Int>
expectEqual(a.value, b.value)
}
runAllTests()