ASTDemangler: Add support for constrained existential compositions

This commit is contained in:
Slava Pestov
2025-05-22 18:10:33 -04:00
parent ca0e984c94
commit 4338a55154
2 changed files with 99 additions and 40 deletions

View File

@@ -767,65 +767,88 @@ Type ASTBuilder::createExistentialMetatypeType(
Type ASTBuilder::createConstrainedExistentialType( Type ASTBuilder::createConstrainedExistentialType(
Type base, ArrayRef<BuiltRequirement> constraints, Type base, ArrayRef<BuiltRequirement> constraints,
ArrayRef<BuiltInverseRequirement> inverseRequirements) { ArrayRef<BuiltInverseRequirement> inverseRequirements) {
Type constrainedBase; llvm::SmallDenseMap<Identifier, Type> primaryAssociatedTypes;
llvm::SmallDenseSet<Identifier> claimed;
if (auto baseTy = base->getAs<ProtocolType>()) {
auto baseDecl = baseTy->getDecl();
llvm::SmallDenseMap<Identifier, Type> cmap;
for (const auto &req : constraints) { for (const auto &req : constraints) {
switch (req.getKind()) { switch (req.getKind()) {
case RequirementKind::SameShape: case RequirementKind::SameShape:
llvm_unreachable("Same-shape requirement not supported here");
case RequirementKind::Conformance: case RequirementKind::Conformance:
case RequirementKind::Superclass: case RequirementKind::Superclass:
case RequirementKind::Layout: case RequirementKind::Layout:
continue; break;
case RequirementKind::SameType: case RequirementKind::SameType: {
if (auto *DMT = req.getFirstType()->getAs<DependentMemberType>()) if (auto *memberTy = req.getFirstType()->getAs<DependentMemberType>()) {
cmap[DMT->getName()] = req.getSecondType(); if (memberTy->getBase()->is<GenericTypeParamType>()) {
// This is the only case we understand so far.
primaryAssociatedTypes[memberTy->getName()] = req.getSecondType();
continue;
} }
} }
llvm::SmallVector<Type, 4> args; break;
for (auto *assocTy : baseDecl->getPrimaryAssociatedTypes()) { }
auto argTy = cmap.find(assocTy->getName()); }
if (argTy == cmap.end()) {
// If we end here, we didn't recognize this requirement.
return Type(); return Type();
} }
args.push_back(argTy->getSecond());
auto maybeFormParameterizedProtocolType = [&](ProtocolType *protoTy) -> Type {
auto *proto = protoTy->getDecl();
llvm::SmallVector<Type, 4> args;
for (auto *assocTy : proto->getPrimaryAssociatedTypes()) {
auto found = primaryAssociatedTypes.find(assocTy->getName());
if (found == primaryAssociatedTypes.end())
return protoTy;
args.push_back(found->second);
claimed.insert(found->first);
} }
// We may not have any arguments because the constrained existential is a // We may not have any arguments because the constrained existential is a
// plain protocol with an inverse requirement. // plain protocol with an inverse requirement.
if (args.empty()) { if (args.empty())
constrainedBase = return protoTy;
ProtocolType::get(baseDecl, baseTy, base->getASTContext());
return ParameterizedProtocolType::get(Ctx, protoTy, args);
};
SmallVector<Type, 2> members;
bool hasExplicitAnyObject = false;
InvertibleProtocolSet inverses;
// We're given either a single protocol type, or a composition of protocol
// types. Transform each protocol type to add arguments, if necessary.
if (auto protoTy = base->getAs<ProtocolType>()) {
members.push_back(maybeFormParameterizedProtocolType(protoTy));
} else { } else {
constrainedBase = auto compositionTy = base->castTo<ProtocolCompositionType>();
ParameterizedProtocolType::get(base->getASTContext(), baseTy, args); hasExplicitAnyObject = compositionTy->hasExplicitAnyObject();
ASSERT(compositionTy->getInverses().empty());
for (auto member : compositionTy->getMembers()) {
if (auto *protoTy = member->getAs<ProtocolType>()) {
members.push_back(maybeFormParameterizedProtocolType(protoTy));
continue;
}
ASSERT(member->getClassOrBoundGenericClass());
members.push_back(member);
} }
} else if (base->isAny()) {
// The only other case should be that we got an empty PCT, which is equal to
// the Any type. The other constraints should have been encoded in the
// existential's generic signature (and arrive as BuiltInverseRequirement).
constrainedBase = base;
} else {
return Type();
} }
assert(constrainedBase); // Make sure that all arguments were actually used.
ASSERT(claimed.size() == primaryAssociatedTypes.size());
// Handle inverse requirements. // Handle inverse requirements.
if (!inverseRequirements.empty()) { if (!inverseRequirements.empty()) {
InvertibleProtocolSet inverseSet;
for (const auto &inverseReq : inverseRequirements) { for (const auto &inverseReq : inverseRequirements) {
inverseSet.insert(inverseReq.getKind()); inverses.insert(inverseReq.getKind());
} }
constrainedBase = ProtocolCompositionType::get(
Ctx, { constrainedBase }, inverseSet, /*hasExplicitAnyObject=*/false);
} }
return ExistentialType::get(constrainedBase); return ExistentialType::get(ProtocolCompositionType::get(
Ctx, members, inverses, hasExplicitAnyObject));
} }
Type ASTBuilder::createSymbolicExtendedExistentialType(NodePointer shapeNode, Type ASTBuilder::createSymbolicExtendedExistentialType(NodePointer shapeNode,

View File

@@ -0,0 +1,36 @@
// RUN: %target-swift-frontend -emit-ir %s -g -target %target-swift-5.9-abi-triple
public protocol P<A, B> {
associatedtype A
associatedtype B
}
public protocol Q<C> {
associatedtype C
}
public protocol R {}
public class C<T: Equatable> {}
public func foo(_ a: any P<Int, Float> & R) {}
public func foo(_ a: any P<Int, Float> & Q<String>) {}
public func foo(_ a: any P<Int, Float> & Q<String> & R) {}
public func foo(_ a: any P<Int, Float> & Q<String> & R & C<Bool>) {}
public func foo(_ a: any P<Int, Float> & Q<String> & R & AnyObject) {}
public func foo(_ a: any (P<Int, Float> & R).Type) {}
public func foo(_ a: any (P<Int, Float> & Q<String>).Type) {}
public func foo(_ a: any (P<Int, Float> & Q<String> & R).Type) {}
public func foo(_ a: any (P<Int, Float> & Q<String> & R & C<Bool>).Type) {}
public func foo(_ a: any (P<Int, Float> & Q<String> & R & AnyObject).Type) {}
public func foo(_ a: (any P<Int, Float> & R).Type) {}
public func foo(_ a: (any P<Int, Float> & Q<String>).Type) {}
public func foo(_ a: (any P<Int, Float> & Q<String> & R).Type) {}
public func foo(_ a: (any P<Int, Float> & Q<String> & R & C<Bool>).Type) {}
public func foo(_ a: (any P<Int, Float> & Q<String> & R & AnyObject).Type) {}
public struct Foo<each T, U> {
public var a1: (repeat any P<each T, U> & R)
}