@@ -2299,9 +2299,71 @@ namespace {
22992299 locator);
23002300 }
23012301
2302- // If we have a type to ascribe to the variable, do so now.
2303- if (oneWayVarType)
2302+ // Ascribe a type to the declaration so it's always available to
2303+ // constraint system.
2304+ if (oneWayVarType) {
23042305 CS.setType (var, oneWayVarType);
2306+ } else if (externalPatternType) {
2307+ // If there is an externally imposed type, that's what the
2308+ // declaration is going to be bound to.
2309+ CS.setType (var, externalPatternType);
2310+ } else {
2311+ // Otherwise, let's use the type of the pattern. The type
2312+ // of the declaration has to be r-value, so let's add an
2313+ // equality constraint if pattern type has any type variables
2314+ // that are allowed to be l-value.
2315+ bool foundLValueVars = false ;
2316+
2317+ // Note that it wouldn't be always correct to allocate a single type
2318+ // variable, that disallows l-value types, to use as a declaration
2319+ // type because equality constraint would drop TVO_CanBindToLValue
2320+ // from the right-hand side (which is not the case for `OneWayEqual`)
2321+ // e.g.:
2322+ //
2323+ // sturct S { var x, y: Int }
2324+ //
2325+ // func test(s: S) {
2326+ // let (x, y) = (s.x, s.y)
2327+ // }
2328+ //
2329+ // Single type variable approach results in the following constraint:
2330+ // `$T_x_y = ($T_s_x, $T_s_y)` where both `$T_s_x` and `$T_s_y` have
2331+ // to allow l-value, but `$T_x_y` does not. Early simplication of `=`
2332+ // constraint (due to right-hand side being a "concrete" tuple type)
2333+ // would drop l-value option from `$T_s_x` and `$T_s_y` which leads to
2334+ // a failure during member lookup because `x` and `y` are both
2335+ // `@lvalue Int`. To avoid that, declaration type would mimic pattern
2336+ // type with all l-value options stripped, so the equality constraint
2337+ // becomes `($T_x, $_T_y) = ($T_s_x, $T_s_y)` which doesn't result in
2338+ // stripping of l-value flag from the right-hand side since
2339+ // simplification can only happen when either side is resolved.
2340+ auto declTy = varType.transform ([&](Type type) -> Type {
2341+ if (auto *typeVar = type->getAs <TypeVariableType>()) {
2342+ if (typeVar->getImpl ().canBindToLValue ()) {
2343+ foundLValueVars = true ;
2344+
2345+ // Drop l-value from the options but preserve the rest.
2346+ auto options = typeVar->getImpl ().getRawOptions ();
2347+ options &= ~TVO_CanBindToLValue;
2348+
2349+ return CS.createTypeVariable (typeVar->getImpl ().getLocator (),
2350+ options);
2351+ }
2352+ }
2353+ return type;
2354+ });
2355+
2356+ // If pattern types allows l-value types, let's create an
2357+ // equality constraint between r-value only declaration type
2358+ // and l-value pattern type that would take care of looking
2359+ // through l-values when necessary.
2360+ if (foundLValueVars) {
2361+ CS.addConstraint (ConstraintKind::Equal, declTy, varType,
2362+ CS.getConstraintLocator (locator));
2363+ }
2364+
2365+ CS.setType (var, declTy);
2366+ }
23052367
23062368 return setType (varType);
23072369 }
0 commit comments