Skip to content

Commit f163022

Browse files
authored
Merge pull request #64820 from xedin/variadics-matching-improvements-5.9
[5.9][ConstraintSystem] Improvements to variadic generics inference
2 parents 0fa9054 + a7a8702 commit f163022

File tree

4 files changed

+152
-10
lines changed

4 files changed

+152
-10
lines changed

lib/Sema/CSApply.cpp

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5490,7 +5490,7 @@ Solution::resolveLocatorToDecl(ConstraintLocator *locator) const {
54905490
/// index. This looks through inheritance for inherited default args.
54915491
static ConcreteDeclRef getDefaultArgOwner(ConcreteDeclRef owner,
54925492
unsigned index) {
5493-
auto *param = getParameterAt(owner.getDecl(), index);
5493+
auto *param = getParameterAt(owner, index);
54945494
assert(param);
54955495
if (param->getDefaultArgumentKind() == DefaultArgumentKind::Inherited) {
54965496
return getDefaultArgOwner(owner.getOverriddenDecl(), index);
@@ -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,
@@ -5904,9 +5963,18 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59045963
assert(solution.argumentMatchingChoices.count(locatorPtr) == 1);
59055964
auto parameterBindings = solution.argumentMatchingChoices.find(locatorPtr)
59065965
->second.parameterBindings;
5966+
bool shouldSubstituteBindings = shouldSubstituteParameterBindings(callee);
5967+
5968+
SmallVector<ParamBinding, 4> substitutedBindings;
5969+
if (shouldSubstituteBindings) {
5970+
computeParameterBindingsSubstitutions(callee, params, parameterBindings,
5971+
substitutedBindings);
5972+
} else {
5973+
substitutedBindings = parameterBindings;
5974+
}
59075975

59085976
SmallVector<Argument, 4> newArgs;
5909-
for (unsigned paramIdx = 0, numParams = parameterBindings.size();
5977+
for (unsigned paramIdx = 0, numParams = substitutedBindings.size();
59105978
paramIdx != numParams; ++paramIdx) {
59115979
// Extract the parameter.
59125980
const auto &param = params[paramIdx];
@@ -5920,7 +5988,7 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59205988

59215989
// The first argument of this vararg parameter may have had a label;
59225990
// save its location.
5923-
auto &varargIndices = parameterBindings[paramIdx];
5991+
auto &varargIndices = substitutedBindings[paramIdx];
59245992
SourceLoc labelLoc;
59255993
if (!varargIndices.empty())
59265994
labelLoc = args->getLabelLoc(varargIndices[0]);
@@ -5969,11 +6037,22 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59696037
}
59706038

59716039
// Handle default arguments.
5972-
if (parameterBindings[paramIdx].empty()) {
6040+
if (substitutedBindings[paramIdx].empty()) {
6041+
auto paramIdxForDefault = paramIdx;
6042+
// If bindings were substituted we need to find "original"
6043+
// (or contextless) parameter index for the default argument.
6044+
if (shouldSubstituteBindings) {
6045+
auto *paramList = getParameterList(callee.getDecl());
6046+
assert(paramList);
6047+
paramIdxForDefault =
6048+
paramList->getOrigParamIndex(callee.getSubstitutions(), paramIdx);
6049+
}
6050+
59736051
auto owner = getDefaultArgOwner(callee, paramIdx);
59746052
auto paramTy = param.getParameterType();
59756053
auto *defArg = new (ctx) DefaultArgumentExpr(
5976-
owner, paramIdx, args->getStartLoc(), paramTy, dc);
6054+
owner, paramIdxForDefault, args->getStartLoc(), paramTy, dc);
6055+
59776056
cs.cacheType(defArg);
59786057
newArgs.emplace_back(SourceLoc(), param.getLabel(), defArg);
59796058
continue;
@@ -5982,8 +6061,8 @@ ArgumentList *ExprRewriter::coerceCallArguments(
59826061
// Otherwise, we have a plain old ordinary argument.
59836062

59846063
// Extract the argument used to initialize this parameter.
5985-
assert(parameterBindings[paramIdx].size() == 1);
5986-
unsigned argIdx = parameterBindings[paramIdx].front();
6064+
assert(substitutedBindings[paramIdx].size() == 1);
6065+
unsigned argIdx = substitutedBindings[paramIdx].front();
59876066
auto arg = args->get(argIdx);
59886067
auto *argExpr = arg.getExpr();
59896068
auto argType = cs.getType(argExpr);
@@ -6027,7 +6106,7 @@ ArgumentList *ExprRewriter::coerceCallArguments(
60276106
};
60286107

60296108
if (paramInfo.hasExternalPropertyWrapper(paramIdx)) {
6030-
auto *paramDecl = getParameterAt(callee.getDecl(), paramIdx);
6109+
auto *paramDecl = getParameterAt(callee, paramIdx);
60316110
assert(paramDecl);
60326111

60336112
auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++];

lib/Sema/CSSimplify.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,22 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
16351635
SmallVector<SynthesizedArg, 4> synthesizedArgs;
16361636
for (unsigned i = 0, n = argTuple->getNumElements(); i != n; ++i) {
16371637
const auto &elt = argTuple->getElement(i);
1638-
AnyFunctionType::Param argument(elt.getType(), elt.getName());
1638+
1639+
// If tuple doesn't have a label for its first element
1640+
// and parameter does, let's assume parameter's label
1641+
// to aid argument matching. For example:
1642+
//
1643+
// \code
1644+
// func test(val: Int, _: String) {}
1645+
//
1646+
// test(val: (42, "")) // expands into `(val: 42, "")`
1647+
// \endcode
1648+
Identifier label = elt.getName();
1649+
if (i == 0 && !elt.hasName() && params[0].hasLabel()) {
1650+
label = params[0].getLabel();
1651+
}
1652+
1653+
AnyFunctionType::Param argument(elt.getType(), label);
16391654
synthesizedArgs.push_back(SynthesizedArg{i, argument});
16401655
argsWithLabels.push_back(argument);
16411656
}

test/Constraints/pack-expansion-expressions.swift

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,11 +331,53 @@ func test_pack_expansions_with_closures() {
331331
// rdar://107151854 - crash on invalid due to specialized pack expansion
332332
func test_pack_expansion_specialization() {
333333
struct Data<each T> {
334-
init(_: repeat each T) {} // expected-note {{'init(_:)' declared here}}
334+
init(_: repeat each T) {} // expected-note 2 {{'init(_:)' declared here}}
335+
init(vals: repeat each T) {} // expected-note 2 {{'init(vals:)' declared here}}
335336
}
336337

337338
_ = Data<Int>() // expected-error {{missing argument for parameter #1 in call}}
338339
_ = Data<Int>(0) // Ok
339340
_ = Data<Int, String>(42, "") // Ok
340341
_ = Data<Int>(42, "") // expected-error {{extra argument in call}}
342+
_ = Data<Int, String>((42, ""))
343+
// expected-error@-1 {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}}
344+
_ = Data<Int, String, Float>(vals: (42, "", 0))
345+
// expected-error@-1 {{initializer expects 3 separate arguments; remove extra parentheses to change tuple into separate arguments}}
346+
_ = Data<Int, String, Float>((vals: 42, "", 0))
347+
// expected-error@-1 {{initializer expects 3 separate arguments; remove extra parentheses to change tuple into separate arguments}}
348+
}
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+
375+
struct Defaulted<each T> {
376+
init(t: repeat each T, extra: Int? = nil) {}
377+
init<each U>(t: repeat each T, u: repeat each U, other: Int? = nil) {}
378+
}
379+
380+
_ = Defaulted(t: "a", 0, 1.0) // Ok
381+
_ = Defaulted(t: "b", 0) // Ok
382+
_ = Defaulted(t: "c", 1.0, u: "d", 0) // Ok
341383
}

test/Constraints/tuple_arguments.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,3 +1799,9 @@ func variadicSplat() {
17991799
_ = y.count
18001800
}
18011801
}
1802+
1803+
func tuple_splat_with_a_label() {
1804+
func test(vals: Int, _: String, _: Float) {} // expected-note 2 {{'test(vals:_:_:)' declared here}}
1805+
test(vals: (23, "hello", 3.14)) // expected-error {{local function 'test' expects 3 separate arguments; remove extra parentheses to change tuple into separate arguments}}
1806+
test((vals: 23, "hello", 3.14)) // expected-error {{local function 'test' expects 3 separate arguments; remove extra parentheses to change tuple into separate arguments}}
1807+
}

0 commit comments

Comments
 (0)