Fix exponential type checking of tuple literals.

This fixes two easy cases where we would go exponential in type
checking tuple literals.

Instead of generating a conversion to a single type variable (which
results in one large constraint system), we generate a conversion ot
the same type that appears in the initializer expression (which for
tuples is a tuple type, which naturally splits the constraint system).

I experimented with trying to generalize this further, but ran into
problems getting it working, so for now this will have to do.

Fixes rdar://problem/20233198.
This commit is contained in:
Mark Lacey
2018-03-21 22:53:17 -07:00
parent 08c73914b1
commit 9385dbb3fb
8 changed files with 60 additions and 33 deletions

View File

@@ -1984,40 +1984,52 @@ namespace {
// Var doesn't affect the type.
return getTypeForPattern(cast<VarPattern>(pattern)->getSubPattern(),
locator);
case PatternKind::Any:
// For a pattern of unknown type, create a new type variable.
case PatternKind::Any: {
// If we have a type from an initializer expression, and that
// expression does not produce an InOut type, use it. This
// will avoid exponential typecheck behavior in the case of
// tuples, nested arrays, and dictionary literals.
//
// Otherwise, create a new type variable.
auto boundExpr = locator.trySimplifyToExpr();
if (boundExpr) {
auto boundExprTy = CS.getType(boundExpr);
if (!boundExprTy->is<InOutType>())
return boundExprTy->getRValueType();
}
return CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToInOut);
}
case PatternKind::Named: {
auto var = cast<NamedPattern>(pattern)->getDecl();
auto boundExpr = locator.trySimplifyToExpr();
auto haveBoundCollectionLiteral = boundExpr &&
!var->hasNonPatternBindingInit() &&
(isa<ArrayExpr>(boundExpr) ||
isa<DictionaryExpr>(boundExpr));
auto isWeak = false;
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
isWeak = OA->get() == ReferenceOwnership::Weak;
// For a named pattern without a type, create a new type variable
// and use it as the type of the variable.
auto boundExpr = locator.trySimplifyToExpr();
auto haveBoundExpr = boundExpr && !var->hasNonPatternBindingInit();
// If we have a type from an initializer expression, and that
// expression does not produce an InOut type, use it. This
// will avoid exponential typecheck behavior in the case of
// tuples, nested arrays, and dictionary literals.
//
// FIXME: For now, substitute in the bound type for literal collection
// exprs that would otherwise result in a simple conversion constraint
// being placed between two type variables. (The bound type and the
// collection type, which will always be the same in this case.)
// This will avoid exponential typecheck behavior in the case of nested
// array and dictionary literals.
Type ty = haveBoundCollectionLiteral ?
CS.getType(boundExpr) :
CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToInOut);
// Otherwise, create a new type variable.
if (!isWeak && haveBoundExpr) {
auto boundExprTy = CS.getType(boundExpr);
if (!boundExprTy->is<InOutType>())
return boundExprTy->getRValueType();
}
Type ty = CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToInOut);
// For weak variables, use Optional<T>.
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
if (OA->get() == ReferenceOwnership::Weak) {
ty = CS.getTypeChecker().getOptionalType(var->getLoc(), ty);
if (!ty) return Type();
}
if (isWeak)
return CS.getTypeChecker().getOptionalType(var->getLoc(), ty);
return ty;
}

View File

@@ -2131,7 +2131,9 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
Expr *foundSolution(Solution &solution, Expr *expr) override {
// Figure out what type the constraints decided on.
InitTypeAndInOut.setPointer(solution.simplifyType(InitTypeAndInOut.getPointer()));
auto ty = solution.simplifyType(InitTypeAndInOut.getPointer());
InitTypeAndInOut.setPointer(
ty->getRValueType()->reconstituteSugar(/*recursive =*/false));
InitTypeAndInOut.setInt(expr->isSemanticallyInOutExpr());
// Just keep going.

View File

@@ -118,11 +118,11 @@ func testAKA(structValue: ImportantCStruct, aliasValue: ImportantCAlias) {
let optStructValue: Optional = structValue
let _: Int = optStructValue
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'Optional<ImportantCStruct>' to specified type 'Int'
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'ImportantCStruct?' to specified type 'Int'
let optAliasValue: Optional = aliasValue
let _: Int = optAliasValue
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'Optional<ImportantCAlias>' (aka 'Optional<Int32>') to specified type 'Int'
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'ImportantCAlias?' (aka 'Optional<Int32>') to specified type 'Int'
}
func testRenamedEnumConstants() {

View File

@@ -129,7 +129,7 @@ func defaultToAny(i: Int, s: String) {
let a2: Array = [1, "a", 3.5]
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}}
let _: Int = a2 // expected-error{{value of type 'Array<Any>'}}
let _: Int = a2 // expected-error{{value of type '[Any]'}}
let a3 = [1, "a", nil, 3.5]
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
@@ -137,7 +137,7 @@ func defaultToAny(i: Int, s: String) {
let a4: Array = [1, "a", nil, 3.5]
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
let _: Int = a4 // expected-error{{value of type 'Array<Any?>'}}
let _: Int = a4 // expected-error{{value of type '[Any?]'}}
let a5 = []
// expected-error@-1{{empty collection literal requires an explicit type}}

View File

@@ -302,7 +302,7 @@ func rdar20029786(_ ns: NSString?) {
// <rdar://problem/19813772> QoI: Using as! instead of as in this case produces really bad diagnostic
func rdar19813772(_ nsma: NSMutableArray) {
var a1 = nsma as! Array // expected-error{{generic parameter 'Element' could not be inferred in cast to 'Array<_>'}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{26-26=<Any>}}
var a1 = nsma as! Array // expected-error{{generic parameter 'Element' could not be inferred in cast to 'Array'}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{26-26=<Any>}}
var a2 = nsma as! Array<AnyObject> // expected-warning{{forced cast from 'NSMutableArray' to 'Array<AnyObject>' always succeeds; did you mean to use 'as'?}} {{17-20=as}}
var a3 = nsma as Array<AnyObject>
}

View File

@@ -351,3 +351,16 @@ func testOne() {
if case One.E.SomeError = error {} // expected-error{{generic enum type 'One.E' is ambiguous without explicit generic parameters when matching value of type 'Error'}}
}
}
func overload(_ x: Int) -> Int { return x }
func overload(_ x: Float) -> Float { return x }
func rdar20233198() {
let typed: (Int, Int, Int, Int, Int, Int, Int, Int) = (Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1))
_ = typed
let _ = (Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1))
let named = (Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1))
_ = named;
let _ = (overload(1), overload(1), overload(1), overload(1), overload(1), overload(1), overload(1), overload(1))
let _ = (overload(1.0), overload(1.0), overload(1.0), overload(1.0), overload(1.0), overload(1.0), overload(1.0), overload(1.0))
}

View File

@@ -82,7 +82,7 @@ func tuplePatternDestructuring(_ x : Int, y : Int) {
_ = i+j
// <rdar://problem/20395243> QoI: type variable reconstruction failing for tuple types
let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple '(x: _, a: _)'}}
let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple '(x: Int, a: Int)'}}
}
// <rdar://problem/21057425> Crash while compiling attached test-app.

View File

@@ -1,4 +1,4 @@
// RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select incrementScopeCounter %s
// RUN: %scale-test --begin 1 --end 5 --step 1 --select incrementScopeCounter %s
// REQUIRES: OS=macosx
// REQUIRES: asserts