[CS] Re-order subscript constraint generation

Add the application constraint before the member constraint, which
allows us to get rid of the special-cased delaying logic for dynamic
member subscripts. The diagnostic change here is due to the fact that
we no longer have a simplified type for the result in CSGen, which
would also be the case if we had a disjunction for the member.
This commit is contained in:
Hamish Knight
2025-10-19 13:14:00 +01:00
parent 4403625279
commit f1823786b6
4 changed files with 15 additions and 34 deletions

View File

@@ -1158,6 +1158,18 @@ namespace {
if (addedTypeVars)
addedTypeVars->push_back(memberTy);
SmallVector<AnyFunctionType::Param, 8> params;
getMatchingParams(argList, params);
// Add the constraint that the index expression's type be convertible
// to the input type of the subscript operator.
// We add this as an unsolved constraint before adding the member
// constraint since some member constraints such as key-path dynamic
// member require the applicable function constraint to be available.
CS.addUnsolvedConstraint(Constraint::createApplicableFunction(
CS, FunctionType::get(params, outputTy), memberTy,
/*trailingClosureMatching=*/std::nullopt, CurDC, fnLocator));
// FIXME: synthesizeMaterializeForSet() wants to statically dispatch to
// a known subscript here. This might be cleaner if we split off a new
// UnresolvedSubscriptExpr from SubscriptExpr.
@@ -1173,15 +1185,6 @@ namespace {
/*outerAlternatives=*/{}, memberLocator);
}
SmallVector<AnyFunctionType::Param, 8> params;
getMatchingParams(argList, params);
// Add the constraint that the index expression's type be convertible
// to the input type of the subscript operator.
CS.addApplicationConstraint(FunctionType::get(params, outputTy), memberTy,
/*trailingClosureMatching=*/std::nullopt,
CurDC, fnLocator);
if (CS.performanceHacksEnabled()) {
Type fixedOutputType =
CS.getFixedTypeRecursive(outputTy, /*wantRValue=*/false);

View File

@@ -11465,22 +11465,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
if (result.actualBaseType)
baseTy = result.actualBaseType;
// If only possible choice to refer to member is via keypath
// dynamic member dispatch, let's delay solving this constraint
// until constraint generation phase is complete, because
// subscript dispatch relies on presence of function application.
if (result.ViableCandidates.size() == 1) {
auto &choice = result.ViableCandidates.front();
if (Phase == ConstraintSystemPhase::ConstraintGeneration &&
choice.isKeyPathDynamicMemberLookup() &&
member.getBaseName().isSubscript()) {
// Let's move this constraint to the active
// list so it could be picked up right after
// constraint generation is done.
return formUnsolved(/*activate=*/true);
}
}
generateOverloadConstraints(
candidates, memberTy, result.ViableCandidates, useDC, locator,
result.getFavoredIndex(), /*requiresFix=*/false,

View File

@@ -1798,12 +1798,6 @@ ConstraintSystem::filterDisjunction(
// constraint, so instead let's keep the disjunction, but disable all
// unviable choices.
if (choice->getOverloadChoice().isKeyPathDynamicMemberLookup()) {
// Early simplification of the "keypath dynamic member lookup" choice
// is impossible because it requires constraints associated with
// subscript index expression to be present.
if (Phase == ConstraintSystemPhase::ConstraintGeneration)
return SolutionKind::Unsolved;
for (auto *currentChoice : disjunction->getNestedConstraints()) {
if (currentChoice->isDisabled())
continue;

View File

@@ -115,11 +115,11 @@ func f_45262(block: () -> (), other: () -> Int) {
struct S {
init<T>(_ x: T, _ y: T) {} // expected-note {{generic parameters are always considered '@escaping'}}
subscript<T>() -> (T, T) -> Void { { _, _ in } } // expected-note {{generic parameters are always considered '@escaping'}}
subscript<T>() -> (T, T) -> Void { { _, _ in } }
init(fn: () -> Int) {
init(fn: () -> Int) { // expected-note {{parameter 'fn' is implicitly non-escaping}}
self.init({ 0 }, fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}}
_ = self[]({ 0 }, fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}}
_ = self[]({ 0 }, fn) // expected-error {{passing non-escaping parameter 'fn' to function expecting an '@escaping' closure}}
}
}