Skip to content

Commit 46e7822

Browse files
committed
Sema: More complex PackExpansionType matching
1 parent 12d21e4 commit 46e7822

File tree

2 files changed

+290
-26
lines changed

2 files changed

+290
-26
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 216 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2466,21 +2466,215 @@ ConstraintSystem::matchPackTypes(PackType *pack1, PackType *pack2,
24662466
return getTypeMatchSuccess();
24672467
}
24682468

2469+
namespace {
2470+
2471+
/// Collects all unique pack type variables referenced from the pattern type,
2472+
/// skipping those captured by nested pack expansion types.
2473+
///
2474+
/// FIXME: This could probably be a common utility method somewhere.
2475+
struct PackTypeVariableCollector: TypeWalker {
2476+
llvm::SetVector<TypeVariableType *> typeVars;
2477+
2478+
Action walkToTypePre(Type t) override {
2479+
if (t->is<PackExpansionType>())
2480+
return Action::SkipChildren;
2481+
2482+
if (auto *typeVar = t->getAs<TypeVariableType>()) {
2483+
if (typeVar->getImpl().canBindToPack())
2484+
typeVars.insert(typeVar);
2485+
}
2486+
2487+
return Action::Continue;
2488+
}
2489+
};
2490+
2491+
}
2492+
2493+
/// Utility function used when matching a pack expansion type against a
2494+
/// pack type.
2495+
///
2496+
/// Takes a pattern type and an original pack type, and returns an instantiated
2497+
/// pack type. The original pack type is then matched against the instantiated
2498+
/// pack type.
2499+
///
2500+
/// As a side effect, it binds each pack type variable occuring in the pattern
2501+
/// type to a new pack with the same shape as the original pack, but where the
2502+
/// elements are fresh type variables.
2503+
///
2504+
/// The instantiated pack has the same shape as the original pack, where the
2505+
/// ith element is the pattern type with each pack type variable replaced by the
2506+
/// ith element of its binding.
2507+
///
2508+
/// For example, given the pattern Foo<$T0> and the original pack
2509+
/// {Foo<Int>, Foo<String>...}, we're going to bind
2510+
///
2511+
/// $T0 := {$T1, $T2}
2512+
///
2513+
/// And return the new pack {Foo<$T1>, Foo<$T2>...}.
2514+
///
2515+
/// The caller will then match the original pack type against the instantiated
2516+
/// pack type, which will recover the bindings:
2517+
///
2518+
/// $T1 := Int
2519+
/// $T2 := String
2520+
///
2521+
static PackType *replaceTypeVariablesWithFreshPacks(ConstraintSystem &cs,
2522+
Type pattern,
2523+
PackType *pack,
2524+
ConstraintLocatorBuilder locator) {
2525+
SmallVector<Type, 2> elts;
2526+
2527+
llvm::MapVector<TypeVariableType *, SmallVector<Type, 2>> typeVars;
2528+
2529+
PackTypeVariableCollector collector;
2530+
pattern.walk(collector);
2531+
2532+
auto *loc = cs.getConstraintLocator(locator);
2533+
2534+
// For each pack type variable occurring in the pattern type, compute a
2535+
// binding pack type comprised of fresh type variables.
2536+
for (auto *typeVar : collector.typeVars) {
2537+
auto &freshTypeVars = typeVars[typeVar];
2538+
for (unsigned i = 0, e = pack->getNumElements(); i < e; ++i) {
2539+
// Preserve the pack expansion structure of the original pack. If the ith
2540+
// element was a pack expansion type, create a new pack expansion type
2541+
// wrapping a pack type variable. Otherwise, create a new scalar
2542+
// type variable.
2543+
//
2544+
// FIXME: Locator for diagnostics
2545+
// FIXME: Other TVO_* flags for type variables?
2546+
if (pack->getElementType(i)->is<PackExpansionType>()) {
2547+
auto *freshTypeVar = cs.createTypeVariable(loc, TVO_CanBindToPack);
2548+
freshTypeVars.push_back(PackExpansionType::get(freshTypeVar, freshTypeVar));
2549+
} else {
2550+
freshTypeVars.push_back(cs.createTypeVariable(loc, /*options=*/0));
2551+
}
2552+
}
2553+
}
2554+
2555+
// For each element of the original pack type, instantiate the pattern type by
2556+
// replacing each pack type variable with the corresponding element of the
2557+
// pack type variable's binding pack.
2558+
for (unsigned i = 0, e = pack->getNumElements(); i < e; ++i) {
2559+
auto *packExpansionElt = pack->getElementType(i)->getAs<PackExpansionType>();
2560+
2561+
auto instantiatedPattern = pattern.transformRec([&](Type t) -> Optional<Type> {
2562+
if (t->is<PackExpansionType>())
2563+
return t;
2564+
2565+
if (auto *typeVar = t->getAs<TypeVariableType>()) {
2566+
if (typeVar->getImpl().canBindToPack()) {
2567+
auto found = typeVars.find(typeVar);
2568+
assert(found != typeVars.end());
2569+
2570+
// The ith element of the binding pack is either a scalar type variable
2571+
// or a pack expansion type wrapping a pack type variable.
2572+
auto projectedType = (found->second)[i];
2573+
if (packExpansionElt != nullptr) {
2574+
projectedType = projectedType->castTo<PackExpansionType>()
2575+
->getPatternType();
2576+
assert(projectedType->castTo<TypeVariableType>()
2577+
->getImpl().canBindToPack());
2578+
} else {
2579+
assert(!projectedType->castTo<TypeVariableType>()
2580+
->getImpl().canBindToPack());
2581+
}
2582+
2583+
return projectedType;
2584+
}
2585+
}
2586+
2587+
return None;
2588+
});
2589+
2590+
if (packExpansionElt != nullptr) {
2591+
elts.push_back(PackExpansionType::get(instantiatedPattern,
2592+
packExpansionElt->getCountType()));
2593+
} else {
2594+
elts.push_back(instantiatedPattern);
2595+
}
2596+
}
2597+
2598+
auto &ctx = cs.getASTContext();
2599+
2600+
// Bind each pack type variable occurring in the pattern type to its
2601+
// binding pack that was constructed above.
2602+
for (const auto &pair : typeVars) {
2603+
// FIXME: Locator for diagnostics
2604+
cs.addConstraint(ConstraintKind::Bind,
2605+
pair.first, PackType::get(ctx, pair.second), locator);
2606+
}
2607+
2608+
// Construct the instantiated pack type.
2609+
return PackType::get(cs.getASTContext(), elts);
2610+
}
2611+
24692612
ConstraintSystem::TypeMatchResult
24702613
ConstraintSystem::matchPackExpansionTypes(PackExpansionType *expansion1,
24712614
PackExpansionType *expansion2,
24722615
ConstraintKind kind, TypeMatchOptions flags,
24732616
ConstraintLocatorBuilder locator) {
2474-
// FIXME: Should we downgrade kind to Bind or something here?
2475-
auto result = matchTypes(expansion1->getCountType(),
2476-
expansion2->getCountType(),
2477-
kind, flags, locator);
2478-
if (result.isFailure())
2479-
return result;
2617+
// The count types of two pack expansion types must have the same shape.
2618+
//
2619+
// FIXME: Locator for diagnostics.
2620+
auto *loc = getConstraintLocator(locator);
2621+
auto *shapeTypeVar = createTypeVariable(loc, TVO_CanBindToPack);
2622+
addConstraint(ConstraintKind::ShapeOf,
2623+
expansion1->getCountType(), shapeTypeVar, locator);
2624+
addConstraint(ConstraintKind::ShapeOf,
2625+
expansion2->getCountType(), shapeTypeVar, locator);
2626+
2627+
auto pattern1 = expansion1->getPatternType();
2628+
auto pattern2 = expansion2->getPatternType();
2629+
2630+
// A pattern is 'expanded' if it is a pack type, pack archetype or
2631+
// pack type variable. Otherwise, it is a concrete type which can be
2632+
// instantiated by replacing any pack type variables that occur within.
2633+
auto isFullyExpanded = [](Type t) -> bool {
2634+
return (t->is<PackType>() ||
2635+
t->is<PackArchetypeType>() ||
2636+
(t->is<TypeVariableType>() &&
2637+
t->castTo<TypeVariableType>()->getImpl().canBindToPack()));
2638+
};
2639+
2640+
bool isExpanded1 = isFullyExpanded(pattern1);
2641+
bool isExpanded2 = isFullyExpanded(pattern2);
2642+
2643+
// If both sides are expanded or neither side is, just match them
2644+
// directly.
2645+
if ((isExpanded1 && isExpanded2) ||
2646+
(!isExpanded1 && !isExpanded2)) {
2647+
return matchTypes(pattern1, pattern2, kind, flags, locator);
2648+
2649+
// If the right hand side is expanded, we have something like
2650+
// Foo<$T0>... vs Pack{Foo<Int>, Foo<String>}...; We're going to
2651+
// bind $T0 to Pack{Int, String}.
2652+
} else if (!isExpanded1 && isExpanded2) {
2653+
if (auto *pack2 = pattern2->getAs<PackType>()) {
2654+
auto *pack1 = replaceTypeVariablesWithFreshPacks(
2655+
*this, pattern1, pack2, locator);
2656+
// FIXME: Locator for diagnostics.
2657+
addConstraint(kind, pack1, pack2, locator);
2658+
return getTypeMatchSuccess();
2659+
}
2660+
2661+
return getTypeMatchFailure(locator);
24802662

2481-
return matchTypes(expansion1->getPatternType(),
2482-
expansion2->getPatternType(),
2483-
kind, flags, locator);
2663+
// If the left hand side is expanded, we have something like
2664+
// Pack{Foo<Int>, Foo<String>}... vs Foo<$T0>...; We're going to
2665+
// bind $T0 to Pack{Int, String}.
2666+
} else {
2667+
assert(isExpanded1 && !isExpanded2);
2668+
if (auto *pack1 = pattern1->getAs<PackType>()) {
2669+
auto *pack2 = replaceTypeVariablesWithFreshPacks(
2670+
*this, pattern2, pack1, locator);
2671+
// FIXME: Locator for diagnostics.
2672+
addConstraint(kind, pack1, pack2, locator);
2673+
return getTypeMatchSuccess();
2674+
}
2675+
2676+
return getTypeMatchFailure(locator);
2677+
}
24842678
}
24852679

24862680
/// Check where a representation is a subtype of another.
@@ -12519,34 +12713,38 @@ ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint(
1251912713
return SolutionKind::Solved;
1252012714
}
1252112715

12522-
static Type getReducedShape(Type type) {
12716+
/// FIXME: Move this elsewhere if it is broadly useful
12717+
static Type getReducedShape(Type type, ASTContext &ctx) {
1252312718
// Pack archetypes know their reduced shape
1252412719
if (auto *packArchetype = type->getAs<PackArchetypeType>())
1252512720
return packArchetype->getShape();
1252612721

1252712722
// Reduced shape of pack is computed recursively
1252812723
if (auto *packType = type->getAs<PackType>()) {
12529-
auto &ctx = type->getASTContext();
1253012724
SmallVector<Type, 2> elts;
1253112725

1253212726
for (auto elt : packType->getElementTypes()) {
1253312727
// T... => shape(T)...
1253412728
if (auto *packExpansionType = elt->getAs<PackExpansionType>()) {
12535-
auto shapeType = getReducedShape(packExpansionType->getCountType());
12536-
assert(shapeType && "Should not end up here if pack type's shape "
12537-
"is still potentially unknown");
12729+
if (packExpansionType->getCountType()->is<PlaceholderType>()) {
12730+
elts.push_back(ctx.TheEmptyTupleType);
12731+
continue;
12732+
}
12733+
auto shapeType = getReducedShape(packExpansionType->getCountType(), ctx);
1253812734
elts.push_back(PackExpansionType::get(shapeType, shapeType));
1253912735
}
1254012736

12541-
// Use () as a placeholder for scalar shape
12737+
// Use () as a placeholder for scalar shape.
1254212738
elts.push_back(ctx.TheEmptyTupleType);
1254312739
}
1254412740

1254512741
return PackType::get(ctx, elts);
1254612742
}
1254712743

12548-
// Getting the shape of any other type is an error.
12549-
return Type();
12744+
assert(!type->isTypeVariableOrMember());
12745+
12746+
// Use () as a placeholder for scalar shape.
12747+
return ctx.TheEmptyTupleType;
1255012748
}
1255112749

1255212750
ConstraintSystem::SolutionKind ConstraintSystem::simplifyShapeOfConstraint(
@@ -12579,7 +12777,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyShapeOfConstraint(
1257912777
return formUnsolved();
1258012778
}
1258112779

12582-
if (Type shape = getReducedShape(type1)) {
12780+
if (Type shape = getReducedShape(type1, getASTContext())) {
1258312781
addConstraint(ConstraintKind::Bind, shape, type2, locator);
1258412782
return SolutionKind::Solved;
1258512783
}

0 commit comments

Comments
 (0)