Skip to content

Commit 3235ae7

Browse files
committed
[CSApply] Teach coerceCallArguments about variadic generics
For variadic generic declarations we need to compute a substituted version of bindings because all of the packs are exploded in the substituted function type. ```swift func fn<each T>(_: repeat each T) {} fn("", 42) ``` The type of `fn` in the call is `(String, Int) -> Void` but bindings have only one parameter at index `0` with two argument positions: 0, 1. (cherry picked from commit 1900813)
1 parent 331a077 commit 3235ae7

File tree

2 files changed

+98
-5
lines changed

2 files changed

+98
-5
lines changed

lib/Sema/CSApply.cpp

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5851,6 +5851,65 @@ static void applyContextualClosureFlags(
58515851
}
58525852
}
58535853

5854+
// For variadic generic declarations we need to compute a substituted
5855+
// version of bindings because all of the packs are exploaded in the
5856+
// substituted function type.
5857+
//
5858+
// \code
5859+
// func fn<each T>(_: repeat each T) {}
5860+
//
5861+
// fn("", 42)
5862+
// \endcode
5863+
//
5864+
// The type of `fn` in the call is `(String, Int) -> Void` but bindings
5865+
// have only one parameter at index `0` with two argument positions: 0, 1.
5866+
static bool shouldSubstituteParameterBindings(ConcreteDeclRef callee) {
5867+
auto subst = callee.getSubstitutions();
5868+
if (subst.empty())
5869+
return false;
5870+
5871+
auto sig = subst.getGenericSignature();
5872+
return llvm::any_of(
5873+
sig.getGenericParams(),
5874+
[&](const GenericTypeParamType *GP) { return GP->isParameterPack(); });
5875+
}
5876+
5877+
/// Compute parameter binding substitutions by exploding pack expansions
5878+
/// into multiple bindings (if they matched more than one argument) and
5879+
/// ignoring empty ones.
5880+
static void computeParameterBindingsSubstitutions(
5881+
ConcreteDeclRef callee, ArrayRef<AnyFunctionType::Param> params,
5882+
ArrayRef<ParamBinding> origBindings,
5883+
SmallVectorImpl<ParamBinding> &substitutedBindings) {
5884+
for (unsigned bindingIdx = 0, numBindings = origBindings.size();
5885+
bindingIdx != numBindings; ++bindingIdx) {
5886+
if (origBindings[bindingIdx].size() > 1) {
5887+
const auto &param = params[substitutedBindings.size()];
5888+
if (!param.isVariadic()) {
5889+
#ifndef NDEBUG
5890+
auto *PD = getParameterAt(callee.getDecl(), bindingIdx);
5891+
assert(PD && PD->getInterfaceType()->is<PackExpansionType>());
5892+
#endif
5893+
// Explode binding set to match substituted function parameters.
5894+
for (auto argIdx : origBindings[bindingIdx])
5895+
substitutedBindings.push_back({argIdx});
5896+
continue;
5897+
}
5898+
}
5899+
5900+
const auto &bindings = origBindings[bindingIdx];
5901+
if (bindings.size() == 0) {
5902+
auto *PD = getParameterAt(callee.getDecl(), bindingIdx);
5903+
// Skip pack expansions with no arguments because they are not
5904+
// present in the substituted function type.
5905+
if (PD->getInterfaceType()->is<PackExpansionType>())
5906+
continue;
5907+
}
5908+
5909+
substitutedBindings.push_back(bindings);
5910+
}
5911+
}
5912+
58545913
ArgumentList *ExprRewriter::coerceCallArguments(
58555914
ArgumentList *args, AnyFunctionType *funcType, ConcreteDeclRef callee,
58565915
ApplyExpr *apply, ConstraintLocatorBuilder locator,
@@ -5905,8 +5964,16 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59055964
auto parameterBindings = solution.argumentMatchingChoices.find(locatorPtr)
59065965
->second.parameterBindings;
59075966

5967+
SmallVector<ParamBinding, 4> substitutedBindings;
5968+
if (shouldSubstituteParameterBindings(callee)) {
5969+
computeParameterBindingsSubstitutions(callee, params, parameterBindings,
5970+
substitutedBindings);
5971+
} else {
5972+
substitutedBindings = parameterBindings;
5973+
}
5974+
59085975
SmallVector<Argument, 4> newArgs;
5909-
for (unsigned paramIdx = 0, numParams = parameterBindings.size();
5976+
for (unsigned paramIdx = 0, numParams = substitutedBindings.size();
59105977
paramIdx != numParams; ++paramIdx) {
59115978
// Extract the parameter.
59125979
const auto &param = params[paramIdx];
@@ -5920,7 +5987,7 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59205987

59215988
// The first argument of this vararg parameter may have had a label;
59225989
// save its location.
5923-
auto &varargIndices = parameterBindings[paramIdx];
5990+
auto &varargIndices = substitutedBindings[paramIdx];
59245991
SourceLoc labelLoc;
59255992
if (!varargIndices.empty())
59265993
labelLoc = args->getLabelLoc(varargIndices[0]);
@@ -5969,7 +6036,7 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59696036
}
59706037

59716038
// Handle default arguments.
5972-
if (parameterBindings[paramIdx].empty()) {
6039+
if (substitutedBindings[paramIdx].empty()) {
59736040
auto owner = getDefaultArgOwner(callee, paramIdx);
59746041
auto paramTy = param.getParameterType();
59756042
auto *defArg = new (ctx) DefaultArgumentExpr(
@@ -5982,8 +6049,8 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59826049
// Otherwise, we have a plain old ordinary argument.
59836050

59846051
// Extract the argument used to initialize this parameter.
5985-
assert(parameterBindings[paramIdx].size() == 1);
5986-
unsigned argIdx = parameterBindings[paramIdx].front();
6052+
assert(substitutedBindings[paramIdx].size() == 1);
6053+
unsigned argIdx = substitutedBindings[paramIdx].front();
59876054
auto arg = args->get(argIdx);
59886055
auto *argExpr = arg.getExpr();
59896056
auto argType = cs.getType(argExpr);

test/Constraints/pack-expansion-expressions.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,29 @@ func test_pack_expansion_specialization() {
346346
_ = Data<Int, String, Float>((vals: 42, "", 0))
347347
// expected-error@-1 {{initializer expects 3 separate arguments; remove extra parentheses to change tuple into separate arguments}}
348348
}
349+
350+
// Make sure that in-exact matches (that require any sort of conversion or load) on arguments are handled correctly.
351+
do {
352+
var v: Float = 42 // expected-warning {{variable 'v' was never mutated; consider changing to 'let' constant}}
353+
354+
func testOpt<each T>(x: Int?, _: repeat each T) {}
355+
testOpt(x: 42, "", v) // Load + Optional promotion
356+
357+
func testLoad<each T, each U>(t: repeat each T, u: repeat each U) {}
358+
testLoad(t: "", v) // Load + default
359+
testLoad(t: "", v, u: v, 0.0) // Two loads
360+
361+
func testDefaultWithExtra<each T, each U>(t: repeat each T, u: repeat each U, extra: Int?) {}
362+
testDefaultWithExtra(t: "", v, extra: 42)
363+
364+
func defaults1<each T>(x: Int? = nil, _: repeat each T) {}
365+
defaults1("", 3.14) // Ok
366+
367+
func defaults2<each T>(_: repeat each T, x: Int? = nil) {}
368+
defaults2("", 3.14) // Ok
369+
370+
func defaults3<each T, each U>(t: repeat each T, u: repeat each U, extra: Int? = nil) {}
371+
defaults3(t: "", 3.14) // Ok
372+
defaults3(t: "", 3.14, u: 0, v) // Ok
373+
defaults3(t: "", 3.14, u: 0, v, extra: 42) // Ok
374+
}

0 commit comments

Comments
 (0)