Skip to content

Commit 0471e2c

Browse files
committed
Implement Type Sequence Parameter Opening
The heart of this patchset: An inference scheme for variadic generic functions and associated pack expansions. A traditional variadic function looks like func foo<T>(_ xs: T...) {} Along with the corresponding function type <T>(T [variadic]) -> Void which the constraint system only has to resolve one two for. Hence it opens <T> as a type variable and uses each argument to the function to try to solve for <T>. This approach cannot work for variadic generics as each argument would try to bind to the same <T> and the constraint system would lose coherency in the one case we need it: when the arguments all have different types. Instead, notice when we encounter expansion types: func print<T...>(_ xs: T...) print("Macs say Hello in", 42, "different languages") We open this type as print : ($t0...) -> ($t0...) Now for the brand new stuff: We need to create and bind a pack to $t0, which will trigger the expansion we need for CSApply to see a coherent view of the world. This means we need to examine the argument list and construct the pack type <$t1, $t2, $t3, ...> - one type variable per argument - and bind it to $t0. There's also the catch that each argument that references the opened type $t0 needs to have the same parallel structure, including its arity. The algorithm is thus: For input type F<... ($t0...), ..., ($tn..) ...> and an apply site F(a0, ..., an) we walk the type `F` and record an entry in a mapping for each opened variadic generic parameter. Now, for each argument ai in (a0, ..., an), we create a fresh type variable corresponding to the argument ai, and record an additional entry in the parameter pack type elements corresponding to that argument. Concretely, suppose we have func print2<T..., U...>(first: T..., second: U...) {} print2(first: "", 42, (), second: [42]) We open print2 as print2 : ($t0..., $t1...) -> Void And construct a mapping $t0 => <$t2, $t3, $t4> // eventually <String, Int, Void> $t1 => <$t5> // eventually [Int] We then yield the entries of this map back to the solver, which constructs bind constraints where each => exists in the diagram above. The pack type thus is immediately substituted into the corresponding pack expansion type which produces a fully-expanded pack type of the correct arity that the solver can actually use to make forward progress. Applying the solution is as simple as pulling out the pack type and coercing arguments element-wise into a pack expression.
1 parent 8ac8633 commit 0471e2c

File tree

3 files changed

+243
-6
lines changed

3 files changed

+243
-6
lines changed

lib/Sema/CSApply.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3404,6 +3404,10 @@ namespace {
34043404
return simplifyExprType(expr);
34053405
}
34063406

3407+
Expr *visitPackExpr(PackExpr *expr) {
3408+
return simplifyExprType(expr);
3409+
}
3410+
34073411
Expr *visitSubscriptExpr(SubscriptExpr *expr) {
34083412
auto *memberLocator =
34093413
cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
@@ -5667,7 +5671,53 @@ ArgumentList *ExprRewriter::coerceCallArguments(
56675671
const auto &param = params[paramIdx];
56685672
auto paramLabel = param.getLabel();
56695673

5670-
// Handle variadic parameters.
5674+
// Handle variadic generic parameters.
5675+
if (paramInfo.isVariadicGenericParameter(paramIdx)) {
5676+
assert(param.isVariadic());
5677+
assert(!param.isInOut());
5678+
5679+
SmallVector<Expr *, 4> variadicArgs;
5680+
5681+
// The first argument of this vararg parameter may have had a label;
5682+
// save its location.
5683+
auto &varargIndices = parameterBindings[paramIdx];
5684+
SourceLoc labelLoc;
5685+
if (!varargIndices.empty())
5686+
labelLoc = args->getLabelLoc(varargIndices[0]);
5687+
5688+
// Convert the arguments.
5689+
auto paramTuple = param.getPlainType()->castTo<PackType>();
5690+
for (auto varargIdx : indices(varargIndices)) {
5691+
auto argIdx = varargIndices[varargIdx];
5692+
auto *arg = args->getExpr(argIdx);
5693+
auto argType = cs.getType(arg);
5694+
5695+
// If the argument type exactly matches, this just works.
5696+
auto paramTy = paramTuple->getElementType(varargIdx);
5697+
if (argType->isEqual(paramTy)) {
5698+
variadicArgs.push_back(arg);
5699+
continue;
5700+
}
5701+
5702+
// Convert the argument.
5703+
auto convertedArg = coerceToType(
5704+
arg, paramTy,
5705+
getArgLocator(argIdx, paramIdx, param.getParameterFlags()));
5706+
if (!convertedArg)
5707+
return nullptr;
5708+
5709+
// Add the converted argument.
5710+
variadicArgs.push_back(convertedArg);
5711+
}
5712+
5713+
// Collect them into a PackExpr.
5714+
auto *packExpr = PackExpr::create(ctx, variadicArgs, paramTuple);
5715+
packExpr->setType(paramTuple);
5716+
cs.cacheType(packExpr);
5717+
5718+
newArgs.push_back(Argument(labelLoc, paramLabel, packExpr));
5719+
continue;
5720+
}
56715721
if (param.isVariadic()) {
56725722
assert(!param.isInOut());
56735723

@@ -6667,6 +6717,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
66676717
/*isImplicit*/ true));
66686718
}
66696719

6720+
case TypeKind::Pack:
6721+
case TypeKind::PackExpansion: {
6722+
llvm_unreachable("Unimplemented!");
6723+
}
6724+
66706725
// Coerce from a tuple to a tuple.
66716726
case TypeKind::Tuple: {
66726727
auto fromTuple = cast<TupleType>(desugaredFromType);
@@ -7018,6 +7073,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
70187073
case TypeKind::GenericFunction:
70197074
case TypeKind::LValue:
70207075
case TypeKind::InOut:
7076+
case TypeKind::Pack:
7077+
case TypeKind::PackExpansion:
70217078
break;
70227079
}
70237080

lib/Sema/CSSimplify.cpp

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,85 @@ class AllowLabelMismatches : public MatchCallArgumentListener {
12391239
}
12401240
};
12411241

1242+
namespace {
1243+
class OpenTypeSequenceElements {
1244+
ConstraintSystem &CS;
1245+
int64_t argIdx;
1246+
Type patternTy;
1247+
// A map that relates a type variable opened for a reference to a type
1248+
// sequence to an array of parallel type variables, one for each argument.
1249+
llvm::MapVector<TypeVariableType *, SmallVector<TypeVariableType *, 2>> SequenceElementCache;
1250+
1251+
public:
1252+
OpenTypeSequenceElements(ConstraintSystem &CS, PackExpansionType *PET)
1253+
: CS(CS), argIdx(-1), patternTy(PET->getPatternType()) {}
1254+
1255+
public:
1256+
Type expandParameter() {
1257+
argIdx += 1;
1258+
return patternTy.transform(*this);
1259+
}
1260+
1261+
void intoPackTypes(llvm::function_ref<void(TypeVariableType *, Type)> fn) && {
1262+
if (argIdx == -1) {
1263+
(void)patternTy.transform(*this);
1264+
for (auto &entry : SequenceElementCache) {
1265+
entry.second.clear();
1266+
}
1267+
}
1268+
1269+
for (const auto &entry : SequenceElementCache) {
1270+
SmallVector<Type, 8> elements;
1271+
llvm::transform(entry.second, std::back_inserter(elements), [](Type t) {
1272+
return t;
1273+
});
1274+
auto packType = PackType::get(CS.getASTContext(), elements);
1275+
fn(entry.first, packType);
1276+
}
1277+
}
1278+
1279+
Type operator()(Type Ty) {
1280+
if (!Ty || !Ty->isTypeVariableOrMember())
1281+
return Ty;
1282+
1283+
auto *TV = Ty->getAs<TypeVariableType>();
1284+
if (!TV)
1285+
return Ty;
1286+
1287+
// Leave plain opened type variables alone.
1288+
if (!TV->getImpl().getGenericParameter()->isTypeSequence())
1289+
return TV;
1290+
1291+
// Create a clone of the type sequence type variable.
1292+
auto *clonedTV = CS.createTypeVariable(TV->getImpl().getLocator(),
1293+
TV->getImpl().getRawOptions());
1294+
1295+
// Is there an entry for this type sequence?
1296+
const auto &entries = SequenceElementCache.find(TV);
1297+
if (entries == SequenceElementCache.end()) {
1298+
// We're just seeing this type sequence fresh, so enter a new type
1299+
// variable in its place and cache down the fact we just saw it.
1300+
SequenceElementCache[TV] = {clonedTV};
1301+
} else if ((int64_t)entries->second.size() <= argIdx) {
1302+
// We've seen this type sequence before, but we have a brand new element.
1303+
// Expand the cache entry.
1304+
SequenceElementCache[TV].push_back(clonedTV);
1305+
} else {
1306+
// Not only have we seen this type sequence before, we've seen this
1307+
// argument index before. Extract the old cache entry, emplace the new
1308+
// one, and bind them together.
1309+
auto *oldEntry = SequenceElementCache[TV][argIdx];
1310+
SequenceElementCache[TV][argIdx] = clonedTV;
1311+
1312+
CS.addConstraint(ConstraintKind::Bind, oldEntry, clonedTV,
1313+
TV->getImpl().getLocator());
1314+
}
1315+
1316+
return clonedTV;
1317+
}
1318+
};
1319+
}
1320+
12421321
// Match the argument of a call to the parameter.
12431322
ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
12441323
ConstraintSystem &cs, FunctionType *contextualType,
@@ -1376,14 +1455,47 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
13761455

13771456
for (unsigned paramIdx = 0, numParams = parameterBindings.size();
13781457
paramIdx != numParams; ++paramIdx){
1379-
// Skip unfulfilled parameters. There's nothing to do for them.
1380-
if (parameterBindings[paramIdx].empty())
1381-
continue;
1382-
13831458
// Determine the parameter type.
13841459
const auto &param = params[paramIdx];
13851460
auto paramTy = param.getOldType();
13861461

1462+
// Type sequences ingest the entire set of argument bindings as
1463+
// a pack type bound to the sequence archetype for the parameter.
1464+
//
1465+
// We pull these out special because variadic parameters ban lots of
1466+
// the more interesting typing constructs called out below like
1467+
// inout and @autoclosure.
1468+
if (paramInfo.isVariadicGenericParameter(paramIdx)) {
1469+
auto *PET = paramTy->castTo<PackExpansionType>();
1470+
OpenTypeSequenceElements openTypeSequence{cs, PET};
1471+
for (auto argIdx : parameterBindings[paramIdx]) {
1472+
const auto &argument = argsWithLabels[argIdx];
1473+
auto argTy = argument.getPlainType();
1474+
1475+
// First, re-open the parameter type so we bind the elements of the type
1476+
// sequence into their proper positions.
1477+
auto substParamTy = openTypeSequence.expandParameter();
1478+
1479+
cs.addConstraint(
1480+
subKind, argTy, substParamTy, loc, /*isFavored=*/false);
1481+
}
1482+
1483+
// Now that we have the pack mappings, bind the raw type sequence to the
1484+
// whole parameter pack. This ensures references to the type sequence in
1485+
// return position refer to the whole pack of elements we just gathered.
1486+
std::move(openTypeSequence)
1487+
.intoPackTypes([&](TypeVariableType *tsParam, Type pack) {
1488+
cs.addConstraint(ConstraintKind::Bind, pack, tsParam, loc,
1489+
/*isFavored=*/false);
1490+
});
1491+
1492+
continue;
1493+
}
1494+
1495+
// Skip unfulfilled parameters. There's nothing to do for them.
1496+
if (parameterBindings[paramIdx].empty())
1497+
continue;
1498+
13871499
// Compare each of the bound arguments for this parameter.
13881500
for (auto argIdx : parameterBindings[paramIdx]) {
13891501
auto loc = locator.withPathElement(LocatorPathElt::ApplyArgToParam(
@@ -1505,6 +1617,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
15051617
}
15061618
}
15071619
}
1620+
15081621
if (!argument.isCompileTimeConst() && param.isCompileTimeConst()) {
15091622
auto *locator = cs.getConstraintLocator(loc);
15101623
SourceRange range;
@@ -1531,6 +1644,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
15311644
return cs.getTypeMatchSuccess();
15321645
}
15331646

1647+
15341648
ConstraintSystem::TypeMatchResult
15351649
ConstraintSystem::matchFunctionResultTypes(Type expectedResult, Type fnResult,
15361650
TypeMatchOptions flags,
@@ -1753,6 +1867,29 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
17531867
return getTypeMatchSuccess();
17541868
}
17551869

1870+
ConstraintSystem::TypeMatchResult
1871+
ConstraintSystem::matchPackTypes(PackType *pack1, PackType *pack2,
1872+
ConstraintKind kind, TypeMatchOptions flags,
1873+
ConstraintLocatorBuilder locator) {
1874+
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
1875+
if (pack1->getNumElements() != pack2->getNumElements())
1876+
return getTypeMatchFailure(locator);
1877+
1878+
for (unsigned i = 0, n = pack1->getNumElements(); i != n; ++i) {
1879+
Type ty1 = pack1->getElementType(i);
1880+
Type ty2 = pack2->getElementType(i);
1881+
1882+
// Compare the element types.
1883+
auto result =
1884+
matchTypes(ty1, ty2, kind, subflags,
1885+
locator.withPathElement(LocatorPathElt::PackElement(i)));
1886+
if (result.isFailure())
1887+
return result;
1888+
}
1889+
1890+
return getTypeMatchSuccess();
1891+
}
1892+
17561893
/// Check where a representation is a subtype of another.
17571894
///
17581895
/// The subtype relationship is defined as:
@@ -5780,6 +5917,18 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
57805917
// completely different decls, then there's nothing else we can do.
57815918
return getTypeMatchFailure(locator);
57825919
}
5920+
case TypeKind::Pack: {
5921+
auto tmpPackLoc = locator.withPathElement(LocatorPathElt::PackType(type1));
5922+
auto packLoc = tmpPackLoc.withPathElement(LocatorPathElt::PackType(type2));
5923+
return matchPackTypes(cast<PackType>(desugar1),
5924+
cast<PackType>(desugar2),
5925+
kind, subflags, packLoc);
5926+
}
5927+
case TypeKind::PackExpansion: {
5928+
return matchTypes(cast<PackExpansionType>(desugar1)->getPatternType(),
5929+
cast<PackExpansionType>(desugar2)->getPatternType(),
5930+
kind, subflags, locator);
5931+
}
57835932
}
57845933
}
57855934

@@ -6096,6 +6245,15 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
60966245
}
60976246
}
60986247
}
6248+
6249+
// A Pack of (Ts...) is convertible to a tuple (X, Y, Z) as long as there
6250+
// is an element-wise match.
6251+
if (auto *expansionTy = type1->getAs<PackType>()) {
6252+
if (!type2->isTypeVariableOrMember()) {
6253+
conversionsOrFixes.push_back(
6254+
ConversionRestrictionKind::ReifyPackToType);
6255+
}
6256+
}
60996257
}
61006258

61016259
if (kind >= ConstraintKind::OperatorArgumentConversion) {
@@ -6340,7 +6498,9 @@ ConstraintSystem::simplifyConstructionConstraint(
63406498
case TypeKind::Function:
63416499
case TypeKind::LValue:
63426500
case TypeKind::InOut:
6343-
case TypeKind::Module: {
6501+
case TypeKind::Module:
6502+
case TypeKind::Pack:
6503+
case TypeKind::PackExpansion: {
63446504
// If solver is in the diagnostic mode and this is an invalid base,
63456505
// let's give solver a chance to repair it to produce a good diagnostic.
63466506
if (shouldAttemptFixes())

test/Constraints/type_sequence.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,23 @@ func invalidPacks() {
6868
func monovariadic2<T>() -> (T...) {} // expected-error 2 {{cannot create expansion with non-variadic type 'T'}}
6969
func monovariadic3<T, U>() -> (T, U...) {} // expected-error {{cannot create a variadic tuple}}
7070
}
71+
72+
func call() {
73+
func multipleParameters<@_typeSequence T>(xs: T..., ys: T...) -> (T...) { return xs }
74+
// expected-note@-1 {{in call to function 'multipleParameters(xs:ys:)'}}
75+
_ = multipleParameters()
76+
// expected-error@-1 2 {{generic parameter 'T' could not be inferred}}
77+
let x: (String) = multipleParameters(xs: "", ys: "")
78+
let (one, two) = multipleParameters(xs: "", 5.0, ys: "", 5.0)
79+
multipleParameters(xs: "", 5.0, ys: 5.0, "") // expected-error {{type of expression is ambiguous without more context}}
80+
81+
func multipleSequences<@_typeSequence T, @_typeSequence U>(xs: T..., ys: U...) -> (T...) { return ys }
82+
// expected-note@-1 {{in call to function 'multipleSequences(xs:ys:)'}}
83+
// expected-error@-2 {{cannot convert return expression of type 'U' to return type 'T'}}
84+
85+
_ = multipleSequences()
86+
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
87+
// expected-error@-2 {{generic parameter 'U' could not be inferred}}
88+
_ = multipleSequences(xs: "", ys: "")
89+
_ = multipleSequences(xs: "", 5.0, ys: 5.0, "")
90+
}

0 commit comments

Comments
 (0)