mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #81365 from Azoy/my-existentials
[AST & Runtime] Correctly mangle extended existentials with inverse requirements
This commit is contained in:
@@ -2352,12 +2352,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isCopyable() const {
|
bool isCopyable() const {
|
||||||
if (!hasGeneralizationSignature()) {
|
for (auto &reqt : getRequirementSignature().getRequirements()) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
auto *reqts = getGenSigRequirements();
|
|
||||||
for (unsigned i = 0, e = getNumGenSigRequirements(); i < e; ++i) {
|
|
||||||
auto &reqt = reqts[i];
|
|
||||||
if (reqt.getKind() != GenericRequirementKind::InvertedProtocols) {
|
if (reqt.getKind() != GenericRequirementKind::InvertedProtocols) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,9 +117,24 @@ struct ExistentialLayout {
|
|||||||
|
|
||||||
LayoutConstraint getLayoutConstraint() const;
|
LayoutConstraint getLayoutConstraint() const;
|
||||||
|
|
||||||
|
/// Whether this layout has any inverses within its signature.
|
||||||
|
bool hasInverses() const {
|
||||||
|
return !inverses.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this existential needs to have an extended existential shape. This
|
||||||
|
/// is relevant for the mangler to mangle as a symbolic link where possible
|
||||||
|
/// and for IRGen directly emitting some existentials.
|
||||||
|
///
|
||||||
|
/// If 'allowInverses' is false, then regardless of if this existential layout
|
||||||
|
/// has inverse requirements those will not influence the need for having a
|
||||||
|
/// shape.
|
||||||
|
bool needsExtendedShape(bool allowInverses = true) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SmallVector<ProtocolDecl *, 4> protocols;
|
SmallVector<ProtocolDecl *, 4> protocols;
|
||||||
SmallVector<ParameterizedProtocolType *, 4> parameterized;
|
SmallVector<ParameterizedProtocolType *, 4> parameterized;
|
||||||
|
InvertibleProtocolSet inverses;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1620,7 +1620,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
|
|||||||
// ExtendedExistentialTypeShapes consider existential metatypes to
|
// ExtendedExistentialTypeShapes consider existential metatypes to
|
||||||
// be part of the existential, so if we're symbolically referencing
|
// be part of the existential, so if we're symbolically referencing
|
||||||
// shapes, we need to handle that at this level.
|
// shapes, we need to handle that at this level.
|
||||||
if (EMT->hasParameterizedExistential()) {
|
if (EMT->getExistentialLayout().needsExtendedShape(AllowInverses)) {
|
||||||
auto referent = SymbolicReferent::forExtendedExistentialTypeShape(EMT);
|
auto referent = SymbolicReferent::forExtendedExistentialTypeShape(EMT);
|
||||||
if (canSymbolicReference(referent)) {
|
if (canSymbolicReference(referent)) {
|
||||||
appendSymbolicExtendedExistentialType(referent, EMT, sig, forDecl);
|
appendSymbolicExtendedExistentialType(referent, EMT, sig, forDecl);
|
||||||
@@ -1629,7 +1629,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (EMT->getInstanceType()->isExistentialType() &&
|
if (EMT->getInstanceType()->isExistentialType() &&
|
||||||
EMT->hasParameterizedExistential())
|
EMT->getExistentialLayout().needsExtendedShape(AllowInverses))
|
||||||
appendConstrainedExistential(EMT->getInstanceType(), sig, forDecl);
|
appendConstrainedExistential(EMT->getInstanceType(), sig, forDecl);
|
||||||
else
|
else
|
||||||
appendType(EMT->getInstanceType(), sig, forDecl);
|
appendType(EMT->getInstanceType(), sig, forDecl);
|
||||||
@@ -1685,8 +1685,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
|
|||||||
return appendType(strippedTy, sig, forDecl);
|
return appendType(strippedTy, sig, forDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PCT->hasParameterizedExistential()
|
if (PCT->getExistentialLayout().needsExtendedShape(AllowInverses))
|
||||||
|| (PCT->hasInverse() && AllowInverses))
|
|
||||||
return appendConstrainedExistential(PCT, sig, forDecl);
|
return appendConstrainedExistential(PCT, sig, forDecl);
|
||||||
|
|
||||||
// We mangle ProtocolType and ProtocolCompositionType using the
|
// We mangle ProtocolType and ProtocolCompositionType using the
|
||||||
@@ -1700,7 +1699,8 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
|
|||||||
|
|
||||||
case TypeKind::Existential: {
|
case TypeKind::Existential: {
|
||||||
auto *ET = cast<ExistentialType>(tybase);
|
auto *ET = cast<ExistentialType>(tybase);
|
||||||
if (ET->hasParameterizedExistential()) {
|
|
||||||
|
if (ET->getExistentialLayout().needsExtendedShape(AllowInverses)) {
|
||||||
auto referent = SymbolicReferent::forExtendedExistentialTypeShape(ET);
|
auto referent = SymbolicReferent::forExtendedExistentialTypeShape(ET);
|
||||||
if (canSymbolicReference(referent)) {
|
if (canSymbolicReference(referent)) {
|
||||||
appendSymbolicExtendedExistentialType(referent, ET, sig, forDecl);
|
appendSymbolicExtendedExistentialType(referent, ET, sig, forDecl);
|
||||||
@@ -1710,6 +1710,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
|
|||||||
return appendConstrainedExistential(ET->getConstraintType(), sig,
|
return appendConstrainedExistential(ET->getConstraintType(), sig,
|
||||||
forDecl);
|
forDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return appendType(ET->getConstraintType(), sig, forDecl);
|
return appendType(ET->getConstraintType(), sig, forDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,8 @@ ExistentialLayout::ExistentialLayout(CanProtocolType type) {
|
|||||||
!protoDecl->isMarkerProtocol());
|
!protoDecl->isMarkerProtocol());
|
||||||
representsAnyObject = false;
|
representsAnyObject = false;
|
||||||
|
|
||||||
|
inverses = InvertibleProtocolSet();
|
||||||
|
|
||||||
protocols.push_back(protoDecl);
|
protocols.push_back(protoDecl);
|
||||||
expandDefaults(protocols, InvertibleProtocolSet(), type->getASTContext());
|
expandDefaults(protocols, InvertibleProtocolSet(), type->getASTContext());
|
||||||
}
|
}
|
||||||
@@ -353,7 +355,7 @@ ExistentialLayout::ExistentialLayout(CanProtocolCompositionType type) {
|
|||||||
protocols.push_back(protoDecl);
|
protocols.push_back(protoDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto inverses = type->getInverses();
|
inverses = type->getInverses();
|
||||||
expandDefaults(protocols, inverses, type->getASTContext());
|
expandDefaults(protocols, inverses, type->getASTContext());
|
||||||
|
|
||||||
representsAnyObject = [&]() {
|
representsAnyObject = [&]() {
|
||||||
@@ -433,6 +435,16 @@ Type ExistentialLayout::getSuperclass() const {
|
|||||||
return Type();
|
return Type();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ExistentialLayout::needsExtendedShape(bool allowInverses) const {
|
||||||
|
if (!getParameterizedProtocols().empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (allowInverses && hasInverses())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeBase::isObjCExistentialType() {
|
bool TypeBase::isObjCExistentialType() {
|
||||||
return getCanonicalType().isObjCExistentialType();
|
return getCanonicalType().isObjCExistentialType();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7857,8 +7857,10 @@ irgen::emitExtendedExistentialTypeShape(IRGenModule &IGM,
|
|||||||
|
|
||||||
// You can have a superclass with a generic parameter pack in a composition,
|
// You can have a superclass with a generic parameter pack in a composition,
|
||||||
// like `C<each T> & P<Int>`
|
// like `C<each T> & P<Int>`
|
||||||
assert(genHeader->GenericPackArguments.empty() &&
|
if (genSig) {
|
||||||
|
assert(genHeader->GenericPackArguments.empty() &&
|
||||||
"Generic parameter packs not supported here yet");
|
"Generic parameter packs not supported here yet");
|
||||||
|
}
|
||||||
|
|
||||||
return b.finishAndCreateFuture();
|
return b.finishAndCreateFuture();
|
||||||
}, [&](llvm::GlobalVariable *var) {
|
}, [&](llvm::GlobalVariable *var) {
|
||||||
|
|||||||
@@ -264,41 +264,6 @@ MetadataDependency MetadataDependencyCollector::finish(IRGenFunction &IGF) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool usesExtendedExistentialMetadata(CanType type) {
|
|
||||||
auto layout = type.getExistentialLayout();
|
|
||||||
// If there are parameterized protocol types that we want to
|
|
||||||
// treat as equal to unparameterized protocol types (maybe
|
|
||||||
// something like `P<some Any>`?), then AST type canonicalization
|
|
||||||
// should turn them into unparameterized protocol types. If the
|
|
||||||
// structure makes it to IRGen, we have to honor that decision that
|
|
||||||
// they represent different types.
|
|
||||||
return !layout.getParameterizedProtocols().empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<std::pair<CanExistentialType, /*depth*/ unsigned>>
|
|
||||||
usesExtendedExistentialMetadata(CanExistentialMetatypeType type) {
|
|
||||||
unsigned depth = 1;
|
|
||||||
auto cur = type.getInstanceType();
|
|
||||||
while (auto metatype = dyn_cast<ExistentialMetatypeType>(cur)) {
|
|
||||||
cur = metatype.getInstanceType();
|
|
||||||
depth++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The only existential types that don't currently use ExistentialType
|
|
||||||
// are Any and AnyObject, which don't use extended metadata.
|
|
||||||
if (usesExtendedExistentialMetadata(cur)) {
|
|
||||||
// HACK: The AST for an existential metatype of a (parameterized) protocol
|
|
||||||
// still directly wraps the existential type as its instance, which means
|
|
||||||
// we need to reconstitute the enclosing ExistentialType.
|
|
||||||
assert(cur->isExistentialType());
|
|
||||||
if (!cur->is<ExistentialType>()) {
|
|
||||||
cur = ExistentialType::get(cur)->getCanonicalType();
|
|
||||||
}
|
|
||||||
return std::make_pair(cast<ExistentialType>(cur), depth);
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Constant *IRGenModule::getAddrOfStringForMetadataRef(
|
llvm::Constant *IRGenModule::getAddrOfStringForMetadataRef(
|
||||||
StringRef symbolName,
|
StringRef symbolName,
|
||||||
unsigned alignment,
|
unsigned alignment,
|
||||||
@@ -1998,7 +1963,7 @@ namespace {
|
|||||||
|
|
||||||
// Existential metatypes for extended existentials don't use
|
// Existential metatypes for extended existentials don't use
|
||||||
// ExistentialMetatypeMetadata.
|
// ExistentialMetatypeMetadata.
|
||||||
if (usesExtendedExistentialMetadata(type)) {
|
if (type->getExistentialLayout().needsExtendedShape()) {
|
||||||
auto metadata = emitExtendedExistentialTypeMetadata(type);
|
auto metadata = emitExtendedExistentialTypeMetadata(type);
|
||||||
return setLocal(type, MetadataResponse::forComplete(metadata));
|
return setLocal(type, MetadataResponse::forComplete(metadata));
|
||||||
}
|
}
|
||||||
@@ -3110,8 +3075,8 @@ static bool shouldAccessByMangledName(IRGenModule &IGM, CanType type) {
|
|||||||
void visitExistentialMetatypeType(CanExistentialMetatypeType meta) {
|
void visitExistentialMetatypeType(CanExistentialMetatypeType meta) {
|
||||||
// Extended existential metatypes just emit a different shape
|
// Extended existential metatypes just emit a different shape
|
||||||
// and don't do any wrapping.
|
// and don't do any wrapping.
|
||||||
if (auto typeAndDepth = usesExtendedExistentialMetadata(meta)) {
|
if (meta->getExistentialLayout().needsExtendedShape()) {
|
||||||
return visit(typeAndDepth.first);
|
// return visit(unwrapExistentialMetatype(meta));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The number of accesses turns out the same as the instance type,
|
// The number of accesses turns out the same as the instance type,
|
||||||
|
|||||||
@@ -465,6 +465,69 @@ _buildDemanglingForNominalType(const Metadata *type, Demangle::Demangler &Dem) {
|
|||||||
return _buildDemanglingForContext(description, demangledGenerics, Dem);
|
return _buildDemanglingForContext(description, demangledGenerics, Dem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Demangle::NodePointer
|
||||||
|
_replaceGeneralizationArg(Demangle::NodePointer type,
|
||||||
|
SubstGenericParametersFromMetadata substitutions,
|
||||||
|
Demangle::Demangler &Dem) {
|
||||||
|
assert(type->getKind() == Node::Kind::Type);
|
||||||
|
auto genericParam = type->getChild(0);
|
||||||
|
|
||||||
|
if (genericParam->getKind() != Node::Kind::DependentGenericParamType)
|
||||||
|
return type;
|
||||||
|
|
||||||
|
auto depth = genericParam->getChild(0)->getIndex();
|
||||||
|
auto index = genericParam->getChild(1)->getIndex();
|
||||||
|
|
||||||
|
auto arg = substitutions.getMetadata(depth, index);
|
||||||
|
assert(arg.isMetadata());
|
||||||
|
return _swift_buildDemanglingForMetadata(arg.getMetadata(), Dem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Demangle::NodePointer
|
||||||
|
_buildDemanglingForExtendedExistential(const Metadata *type,
|
||||||
|
Demangle::Demangler &Dem) {
|
||||||
|
auto ee = cast<ExtendedExistentialTypeMetadata>(type);
|
||||||
|
|
||||||
|
auto demangledExistential = Dem.demangleType(ee->Shape->ExistentialType.get(),
|
||||||
|
ResolveToDemanglingForContext(Dem));
|
||||||
|
|
||||||
|
if (!ee->Shape->hasGeneralizationSignature())
|
||||||
|
return demangledExistential;
|
||||||
|
|
||||||
|
SubstGenericParametersFromMetadata substitutions(ee->Shape,
|
||||||
|
ee->getGeneralizationArguments());
|
||||||
|
|
||||||
|
// Dig out the requirement list.
|
||||||
|
auto constrainedExistential = demangledExistential->getChild(0);
|
||||||
|
assert(constrainedExistential->getKind() == Node::Kind::ConstrainedExistential);
|
||||||
|
auto reqList = constrainedExistential->getChild(1);
|
||||||
|
assert(reqList->getKind() == Node::Kind::ConstrainedExistentialRequirementList);
|
||||||
|
|
||||||
|
auto newReqList = Dem.createNode(Node::Kind::ConstrainedExistentialRequirementList);
|
||||||
|
|
||||||
|
for (auto req : *reqList) {
|
||||||
|
// Currently, the only requirements that can create generalization arguments
|
||||||
|
// are same types.
|
||||||
|
if (req->getKind() != Node::Kind::DependentGenericSameTypeRequirement) {
|
||||||
|
newReqList->addChild(req, Dem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lhs = _replaceGeneralizationArg(req->getChild(0), substitutions, Dem);
|
||||||
|
auto rhs = _replaceGeneralizationArg(req->getChild(1), substitutions, Dem);
|
||||||
|
|
||||||
|
auto newReq = Dem.createNode(Node::Kind::DependentGenericSameTypeRequirement);
|
||||||
|
newReq->addChild(lhs, Dem);
|
||||||
|
newReq->addChild(rhs, Dem);
|
||||||
|
|
||||||
|
newReqList->addChild(newReq, Dem);
|
||||||
|
}
|
||||||
|
|
||||||
|
constrainedExistential->replaceChild(1, newReqList);
|
||||||
|
|
||||||
|
return demangledExistential;
|
||||||
|
}
|
||||||
|
|
||||||
// Build a demangled type tree for a type.
|
// Build a demangled type tree for a type.
|
||||||
//
|
//
|
||||||
// FIXME: This should use MetadataReader.h.
|
// FIXME: This should use MetadataReader.h.
|
||||||
@@ -596,13 +659,7 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type,
|
|||||||
return proto_list;
|
return proto_list;
|
||||||
}
|
}
|
||||||
case MetadataKind::ExtendedExistential: {
|
case MetadataKind::ExtendedExistential: {
|
||||||
// FIXME: Implement this by demangling the extended existential and
|
return _buildDemanglingForExtendedExistential(type, Dem);
|
||||||
// substituting the generalization arguments into the demangle tree.
|
|
||||||
// For now, unconditional casts will report '<<< invalid type >>>' when
|
|
||||||
// they fail.
|
|
||||||
// TODO: for clients that need to guarantee round-tripping, demangle
|
|
||||||
// to a SymbolicExtendedExistentialType.
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
case MetadataKind::ExistentialMetatype: {
|
case MetadataKind::ExistentialMetatype: {
|
||||||
auto metatype = static_cast<const ExistentialMetatypeMetadata *>(type);
|
auto metatype = static_cast<const ExistentialMetatypeMetadata *>(type);
|
||||||
|
|||||||
49
test/Interpreter/extended_existential.swift
Normal file
49
test/Interpreter/extended_existential.swift
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// RUN: %empty-directory(%t)
|
||||||
|
// RUN: %target-build-swift -O -target %target-cpu-apple-macos15.0 %s -o %t/a.out
|
||||||
|
// RUN: %target-codesign %t/a.out
|
||||||
|
// RUN: %target-run %t/a.out | %FileCheck %s
|
||||||
|
|
||||||
|
// REQUIRES: OS=macosx
|
||||||
|
// REQUIRES: executable_test
|
||||||
|
|
||||||
|
protocol A<B>: ~Copyable {
|
||||||
|
associatedtype B
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol B: ~Copyable {}
|
||||||
|
|
||||||
|
let a: Any = (any ~Copyable).self
|
||||||
|
// CHECK: any Any<Self: ~Swift.Copyable>
|
||||||
|
print(a)
|
||||||
|
|
||||||
|
let b: Any = (any ~Escapable).self
|
||||||
|
// CHECK: any Any<Self: ~Swift.Escapable>
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
let c: Any = (any ~Copyable & ~Escapable).self
|
||||||
|
// CHECK: any Any<Self: ~Swift.Copyable, Self: ~Swift.Escapable>
|
||||||
|
print(c)
|
||||||
|
|
||||||
|
let d: Any = (any A).self
|
||||||
|
// CHECK: A
|
||||||
|
print(d)
|
||||||
|
|
||||||
|
let e: Any = (any B).self
|
||||||
|
// CHECK: B
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
let f: Any = (any A & B).self
|
||||||
|
// CHECK: A & B
|
||||||
|
print(f)
|
||||||
|
|
||||||
|
let g: Any = (any A & ~Copyable).self
|
||||||
|
// CHECK: any A<Self: ~Swift.Copyable>
|
||||||
|
print(g)
|
||||||
|
|
||||||
|
let h: Any = (any B & ~Copyable).self
|
||||||
|
// CHECK: any B<Self: ~Swift.Copyable>
|
||||||
|
print(h)
|
||||||
|
|
||||||
|
let i: Any = (any A & B & ~Copyable).self
|
||||||
|
// CHECK: any A & B<Self: ~Swift.Copyable>
|
||||||
|
print(i)
|
||||||
Reference in New Issue
Block a user