Skip to content

Commit f15d600

Browse files
authored
Merge pull request swiftlang#83953 from Azoy/62-extended-existentials
[6.2] [AST & Runtime] Correctly mangle extended existentials with inverse requirements
2 parents 5f20a5b + 0877b04 commit f15d600

File tree

8 files changed

+154
-58
lines changed

8 files changed

+154
-58
lines changed

include/swift/ABI/Metadata.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,12 +2352,7 @@ struct TargetExtendedExistentialTypeShape
23522352
}
23532353

23542354
bool isCopyable() const {
2355-
if (!hasGeneralizationSignature()) {
2356-
return true;
2357-
}
2358-
auto *reqts = getGenSigRequirements();
2359-
for (unsigned i = 0, e = getNumGenSigRequirements(); i < e; ++i) {
2360-
auto &reqt = reqts[i];
2355+
for (auto &reqt : getRequirementSignature().getRequirements()) {
23612356
if (reqt.getKind() != GenericRequirementKind::InvertedProtocols) {
23622357
continue;
23632358
}

include/swift/AST/ExistentialLayout.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,24 @@ struct ExistentialLayout {
117117

118118
LayoutConstraint getLayoutConstraint() const;
119119

120+
/// Whether this layout has any inverses within its signature.
121+
bool hasInverses() const {
122+
return !inverses.empty();
123+
}
124+
125+
/// Whether this existential needs to have an extended existential shape. This
126+
/// is relevant for the mangler to mangle as a symbolic link where possible
127+
/// and for IRGen directly emitting some existentials.
128+
///
129+
/// If 'allowInverses' is false, then regardless of if this existential layout
130+
/// has inverse requirements those will not influence the need for having a
131+
/// shape.
132+
bool needsExtendedShape(bool allowInverses = true) const;
133+
120134
private:
121135
SmallVector<ProtocolDecl *, 4> protocols;
122136
SmallVector<ParameterizedProtocolType *, 4> parameterized;
137+
InvertibleProtocolSet inverses;
123138
};
124139

125140
}

lib/AST/ASTMangler.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,7 +1620,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
16201620
// ExtendedExistentialTypeShapes consider existential metatypes to
16211621
// be part of the existential, so if we're symbolically referencing
16221622
// shapes, we need to handle that at this level.
1623-
if (EMT->hasParameterizedExistential()) {
1623+
if (EMT->getExistentialLayout().needsExtendedShape(AllowInverses)) {
16241624
auto referent = SymbolicReferent::forExtendedExistentialTypeShape(EMT);
16251625
if (canSymbolicReference(referent)) {
16261626
appendSymbolicExtendedExistentialType(referent, EMT, sig, forDecl);
@@ -1629,7 +1629,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
16291629
}
16301630

16311631
if (EMT->getInstanceType()->isExistentialType() &&
1632-
EMT->hasParameterizedExistential())
1632+
EMT->getExistentialLayout().needsExtendedShape(AllowInverses))
16331633
appendConstrainedExistential(EMT->getInstanceType(), sig, forDecl);
16341634
else
16351635
appendType(EMT->getInstanceType(), sig, forDecl);
@@ -1685,8 +1685,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
16851685
return appendType(strippedTy, sig, forDecl);
16861686
}
16871687

1688-
if (PCT->hasParameterizedExistential()
1689-
|| (PCT->hasInverse() && AllowInverses))
1688+
if (PCT->getExistentialLayout().needsExtendedShape(AllowInverses))
16901689
return appendConstrainedExistential(PCT, sig, forDecl);
16911690

16921691
// We mangle ProtocolType and ProtocolCompositionType using the
@@ -1700,7 +1699,8 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
17001699

17011700
case TypeKind::Existential: {
17021701
auto *ET = cast<ExistentialType>(tybase);
1703-
if (ET->hasParameterizedExistential()) {
1702+
1703+
if (ET->getExistentialLayout().needsExtendedShape(AllowInverses)) {
17041704
auto referent = SymbolicReferent::forExtendedExistentialTypeShape(ET);
17051705
if (canSymbolicReference(referent)) {
17061706
appendSymbolicExtendedExistentialType(referent, ET, sig, forDecl);
@@ -1710,6 +1710,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
17101710
return appendConstrainedExistential(ET->getConstraintType(), sig,
17111711
forDecl);
17121712
}
1713+
17131714
return appendType(ET->getConstraintType(), sig, forDecl);
17141715
}
17151716

lib/AST/Type.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ ExistentialLayout::ExistentialLayout(CanProtocolType type) {
320320
!protoDecl->isMarkerProtocol());
321321
representsAnyObject = false;
322322

323+
inverses = InvertibleProtocolSet();
324+
323325
protocols.push_back(protoDecl);
324326
expandDefaults(protocols, InvertibleProtocolSet(), type->getASTContext());
325327
}
@@ -353,7 +355,7 @@ ExistentialLayout::ExistentialLayout(CanProtocolCompositionType type) {
353355
protocols.push_back(protoDecl);
354356
}
355357

356-
auto inverses = type->getInverses();
358+
inverses = type->getInverses();
357359
expandDefaults(protocols, inverses, type->getASTContext());
358360

359361
representsAnyObject = [&]() {
@@ -433,6 +435,16 @@ Type ExistentialLayout::getSuperclass() const {
433435
return Type();
434436
}
435437

438+
bool ExistentialLayout::needsExtendedShape(bool allowInverses) const {
439+
if (!getParameterizedProtocols().empty())
440+
return true;
441+
442+
if (allowInverses && hasInverses())
443+
return true;
444+
445+
return false;
446+
}
447+
436448
bool TypeBase::isObjCExistentialType() {
437449
return getCanonicalType().isObjCExistentialType();
438450
}

lib/IRGen/GenMeta.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7857,8 +7857,10 @@ irgen::emitExtendedExistentialTypeShape(IRGenModule &IGM,
78577857

78587858
// You can have a superclass with a generic parameter pack in a composition,
78597859
// like `C<each T> & P<Int>`
7860-
assert(genHeader->GenericPackArguments.empty() &&
7860+
if (genSig) {
7861+
assert(genHeader->GenericPackArguments.empty() &&
78617862
"Generic parameter packs not supported here yet");
7863+
}
78627864

78637865
return b.finishAndCreateFuture();
78647866
}, [&](llvm::GlobalVariable *var) {

lib/IRGen/MetadataRequest.cpp

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -264,41 +264,6 @@ MetadataDependency MetadataDependencyCollector::finish(IRGenFunction &IGF) {
264264
return result;
265265
}
266266

267-
static bool usesExtendedExistentialMetadata(CanType type) {
268-
auto layout = type.getExistentialLayout();
269-
// If there are parameterized protocol types that we want to
270-
// treat as equal to unparameterized protocol types (maybe
271-
// something like `P<some Any>`?), then AST type canonicalization
272-
// should turn them into unparameterized protocol types. If the
273-
// structure makes it to IRGen, we have to honor that decision that
274-
// they represent different types.
275-
return !layout.getParameterizedProtocols().empty();
276-
}
277-
278-
static std::optional<std::pair<CanExistentialType, /*depth*/ unsigned>>
279-
usesExtendedExistentialMetadata(CanExistentialMetatypeType type) {
280-
unsigned depth = 1;
281-
auto cur = type.getInstanceType();
282-
while (auto metatype = dyn_cast<ExistentialMetatypeType>(cur)) {
283-
cur = metatype.getInstanceType();
284-
depth++;
285-
}
286-
287-
// The only existential types that don't currently use ExistentialType
288-
// are Any and AnyObject, which don't use extended metadata.
289-
if (usesExtendedExistentialMetadata(cur)) {
290-
// HACK: The AST for an existential metatype of a (parameterized) protocol
291-
// still directly wraps the existential type as its instance, which means
292-
// we need to reconstitute the enclosing ExistentialType.
293-
assert(cur->isExistentialType());
294-
if (!cur->is<ExistentialType>()) {
295-
cur = ExistentialType::get(cur)->getCanonicalType();
296-
}
297-
return std::make_pair(cast<ExistentialType>(cur), depth);
298-
}
299-
return std::nullopt;
300-
}
301-
302267
llvm::Constant *IRGenModule::getAddrOfStringForMetadataRef(
303268
StringRef symbolName,
304269
unsigned alignment,
@@ -1998,7 +1963,7 @@ namespace {
19981963

19991964
// Existential metatypes for extended existentials don't use
20001965
// ExistentialMetatypeMetadata.
2001-
if (usesExtendedExistentialMetadata(type)) {
1966+
if (type->getExistentialLayout().needsExtendedShape()) {
20021967
auto metadata = emitExtendedExistentialTypeMetadata(type);
20031968
return setLocal(type, MetadataResponse::forComplete(metadata));
20041969
}
@@ -3110,8 +3075,8 @@ static bool shouldAccessByMangledName(IRGenModule &IGM, CanType type) {
31103075
void visitExistentialMetatypeType(CanExistentialMetatypeType meta) {
31113076
// Extended existential metatypes just emit a different shape
31123077
// and don't do any wrapping.
3113-
if (auto typeAndDepth = usesExtendedExistentialMetadata(meta)) {
3114-
return visit(typeAndDepth.first);
3078+
if (meta->getExistentialLayout().needsExtendedShape()) {
3079+
// return visit(unwrapExistentialMetatype(meta));
31153080
}
31163081

31173082
// The number of accesses turns out the same as the instance type,

stdlib/public/runtime/Demangle.cpp

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,69 @@ _buildDemanglingForNominalType(const Metadata *type, Demangle::Demangler &Dem) {
465465
return _buildDemanglingForContext(description, demangledGenerics, Dem);
466466
}
467467

468+
static Demangle::NodePointer
469+
_replaceGeneralizationArg(Demangle::NodePointer type,
470+
SubstGenericParametersFromMetadata substitutions,
471+
Demangle::Demangler &Dem) {
472+
assert(type->getKind() == Node::Kind::Type);
473+
auto genericParam = type->getChild(0);
474+
475+
if (genericParam->getKind() != Node::Kind::DependentGenericParamType)
476+
return type;
477+
478+
auto depth = genericParam->getChild(0)->getIndex();
479+
auto index = genericParam->getChild(1)->getIndex();
480+
481+
auto arg = substitutions.getMetadata(depth, index);
482+
assert(arg.isMetadata());
483+
return _swift_buildDemanglingForMetadata(arg.getMetadata(), Dem);
484+
}
485+
486+
static Demangle::NodePointer
487+
_buildDemanglingForExtendedExistential(const Metadata *type,
488+
Demangle::Demangler &Dem) {
489+
auto ee = cast<ExtendedExistentialTypeMetadata>(type);
490+
491+
auto demangledExistential = Dem.demangleType(ee->Shape->ExistentialType.get(),
492+
ResolveToDemanglingForContext(Dem));
493+
494+
if (!ee->Shape->hasGeneralizationSignature())
495+
return demangledExistential;
496+
497+
SubstGenericParametersFromMetadata substitutions(ee->Shape,
498+
ee->getGeneralizationArguments());
499+
500+
// Dig out the requirement list.
501+
auto constrainedExistential = demangledExistential->getChild(0);
502+
assert(constrainedExistential->getKind() == Node::Kind::ConstrainedExistential);
503+
auto reqList = constrainedExistential->getChild(1);
504+
assert(reqList->getKind() == Node::Kind::ConstrainedExistentialRequirementList);
505+
506+
auto newReqList = Dem.createNode(Node::Kind::ConstrainedExistentialRequirementList);
507+
508+
for (auto req : *reqList) {
509+
// Currently, the only requirements that can create generalization arguments
510+
// are same types.
511+
if (req->getKind() != Node::Kind::DependentGenericSameTypeRequirement) {
512+
newReqList->addChild(req, Dem);
513+
continue;
514+
}
515+
516+
auto lhs = _replaceGeneralizationArg(req->getChild(0), substitutions, Dem);
517+
auto rhs = _replaceGeneralizationArg(req->getChild(1), substitutions, Dem);
518+
519+
auto newReq = Dem.createNode(Node::Kind::DependentGenericSameTypeRequirement);
520+
newReq->addChild(lhs, Dem);
521+
newReq->addChild(rhs, Dem);
522+
523+
newReqList->addChild(newReq, Dem);
524+
}
525+
526+
constrainedExistential->replaceChild(1, newReqList);
527+
528+
return demangledExistential;
529+
}
530+
468531
// Build a demangled type tree for a type.
469532
//
470533
// FIXME: This should use MetadataReader.h.
@@ -596,13 +659,7 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type,
596659
return proto_list;
597660
}
598661
case MetadataKind::ExtendedExistential: {
599-
// FIXME: Implement this by demangling the extended existential and
600-
// substituting the generalization arguments into the demangle tree.
601-
// For now, unconditional casts will report '<<< invalid type >>>' when
602-
// they fail.
603-
// TODO: for clients that need to guarantee round-tripping, demangle
604-
// to a SymbolicExtendedExistentialType.
605-
return nullptr;
662+
return _buildDemanglingForExtendedExistential(type, Dem);
606663
}
607664
case MetadataKind::ExistentialMetatype: {
608665
auto metatype = static_cast<const ExistentialMetatypeMetadata *>(type);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -O -target %target-cpu-apple-macos15.0 %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out | %FileCheck %s
5+
6+
// REQUIRES: OS=macosx
7+
// REQUIRES: executable_test
8+
9+
protocol A<B>: ~Copyable {
10+
associatedtype B
11+
}
12+
13+
protocol B: ~Copyable {}
14+
15+
let a: Any = (any ~Copyable).self
16+
// CHECK: any Any<Self: ~Swift.Copyable>
17+
print(a)
18+
19+
let b: Any = (any ~Escapable).self
20+
// CHECK: any Any<Self: ~Swift.Escapable>
21+
print(b)
22+
23+
let c: Any = (any ~Copyable & ~Escapable).self
24+
// CHECK: any Any<Self: ~Swift.Copyable, Self: ~Swift.Escapable>
25+
print(c)
26+
27+
let d: Any = (any A).self
28+
// CHECK: A
29+
print(d)
30+
31+
let e: Any = (any B).self
32+
// CHECK: B
33+
print(e)
34+
35+
let f: Any = (any A & B).self
36+
// CHECK: A & B
37+
print(f)
38+
39+
let g: Any = (any A & ~Copyable).self
40+
// CHECK: any A<Self: ~Swift.Copyable>
41+
print(g)
42+
43+
let h: Any = (any B & ~Copyable).self
44+
// CHECK: any B<Self: ~Swift.Copyable>
45+
print(h)
46+
47+
let i: Any = (any A & B & ~Copyable).self
48+
// CHECK: any A & B<Self: ~Swift.Copyable>
49+
print(i)

0 commit comments

Comments
 (0)