Skip to content

Commit 0f716e6

Browse files
committed
[QoI] Fix inference of inout closure parameters in generic contexts
Convert subtype constraints with inout on one side and type variable on the other into fixed binding. Constriants like `inout T0 subtype T1` where (T0 must be materializable) are created when closures are part of the generic function parameters e.g. `func foo<T>(_ t: T, (inout T) -> Void) {}` so when such function gets called e.g. ``` var x = 42 foo(x) { $0 = 0 } ``` it's going to try and map closure parameters type (inout T0) - where is opened generic parameter T - to argument type (T1), which can be 'inout' but it's uncertain at this stage, but since closure 'declaration' `{ $0 = 0 }` is wrapped inside of a function call, it has to 'map' parameters to arguments instead of converting them, see `ConstraintSystem::matchFunctionTypes`. Resolves: SR-3520, SR-1976, SR-3073, SR-3479.
1 parent dd7360e commit 0f716e6

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,40 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
14481448
SWIFT_FALLTHROUGH;
14491449

14501450
case ConstraintKind::Subtype:
1451+
// Subtype constraints are subject for edge contraction,
1452+
// which is inappropriate in this case, because it's going to
1453+
// erase/lose 'inout' modifier after merging equivalence classes
1454+
// (if inout containts type var, see ConstraintGraph::contractEdges()),
1455+
// since right-hand side type variable must not be materializable
1456+
// it can simply get left-hand side as a fixed binding, otherwise fail.
1457+
if (type1->is<InOutType>() &&
1458+
type1->getInOutObjectType()->isTypeVariableOrMember() && typeVar2) {
1459+
// Left-hand side type is not materializable, so we need to
1460+
// check if it's even appropriate to have such a constraint
1461+
// between these two types, or fail early otherwise if right-hand
1462+
// side must be materializable.
1463+
if (typeVar2->getImpl().mustBeMaterializable())
1464+
return SolutionKind::Error;
1465+
1466+
// Constriants like `inout T0 subtype T1` where (T0 must be
1467+
// materializable) are created when closures are part of the generic
1468+
// function parameters e.g. `func foo<T>(_ t: T, (inout T) -> Void) {}`
1469+
// so when such function gets called e.g.
1470+
// ```
1471+
// var x = 42
1472+
// foo(x) { $0 = 0 }
1473+
// ```
1474+
// it's going to try and map closure parameters type (inout T0), where
1475+
// T0 is opened generic parameter T, to argument type (T1), which can
1476+
// be 'inout' but it's uncertain at this stage, but since closure
1477+
// 'declaration' `{ $0 = 0 }` is wrapped inside of a function call,
1478+
// it has to 'map' parameters to arguments instead of converting them,
1479+
// see `ConstraintSystem::matchFunctionTypes`.
1480+
assignFixedType(typeVar2, type1);
1481+
return SolutionKind::Solved;
1482+
}
1483+
SWIFT_FALLTHROUGH;
1484+
14511485
case ConstraintKind::ArgumentConversion:
14521486
case ConstraintKind::OperatorArgumentTupleConversion:
14531487
case ConstraintKind::OperatorArgumentConversion:

test/Constraints/closures.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,3 +399,41 @@ let mismatchInClosureResultType : (String) -> ((Int) -> Void) = {
399399
// SR-3520: Generic function taking closure with inout parameter can result in a variety of compiler errors or EXC_BAD_ACCESS
400400
func sr3520_1<T>(_ g: (inout T) -> Int) {}
401401
sr3520_1 { $0 = 1 } // expected-error {{cannot convert value of type '()' to closure result type 'Int'}}
402+
403+
func sr3520_2<T>(_ item: T, _ update: (inout T) -> Void) {
404+
var x = item
405+
update(&x)
406+
}
407+
var sr3250_arg = 42
408+
sr3520_2(sr3250_arg) { $0 += 3 } // ok
409+
410+
// This test makes sure that having closure with inout argument doesn't crash with member lookup
411+
struct S_3520 {
412+
var number1: Int
413+
}
414+
func sr3520_set_via_closure<S, T>(_ closure: (inout S, T) -> ()) {}
415+
sr3520_set_via_closure({ $0.number1 = $1 }) // expected-error {{type of expression is ambiguous without more context}}
416+
417+
// SR-1976/SR-3073: Inference of inout
418+
func sr1976<T>(_ closure: (inout T) -> Void) {}
419+
sr1976({ $0 += 2 }) // ok
420+
421+
// SR-3073: UnresolvedDotExpr in single expression closure
422+
423+
struct SR3073Lense<Whole, Part> {
424+
let set: (inout Whole, Part) -> ()
425+
}
426+
struct SR3073 {
427+
var number1: Int
428+
func lenses() {
429+
let _: SR3073Lense<SR3073, Int> = SR3073Lense(
430+
set: { $0.number1 = $1 } // ok
431+
)
432+
}
433+
}
434+
435+
// SR-3479: Segmentation fault and other error for closure with inout parameter
436+
func sr3497_unfold<A, B>(_ a0: A, next: (inout A) -> B) {}
437+
func sr3497() {
438+
let _ = sr3497_unfold((0, 0)) { s in 0 } // ok
439+
}

0 commit comments

Comments
 (0)