Skip to content

Commit a5cff5a

Browse files
committed
[CSGen] Always record a type of named pattern declaration
This is going to be used by multi-statement closure inference because patterns could be declarated and referenced in the body via the associated declaration.
1 parent 966f58f commit a5cff5a

File tree

1 file changed

+64
-2
lines changed

1 file changed

+64
-2
lines changed

lib/Sema/CSGen.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)