Skip to content

Commit 9385dbb

Browse files
committed
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.
1 parent 08c7391 commit 9385dbb

File tree

8 files changed

+59
-32
lines changed

8 files changed

+59
-32
lines changed

lib/Sema/CSGen.cpp

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1984,40 +1984,52 @@ namespace {
19841984
// Var doesn't affect the type.
19851985
return getTypeForPattern(cast<VarPattern>(pattern)->getSubPattern(),
19861986
locator);
1987-
case PatternKind::Any:
1988-
// For a pattern of unknown type, create a new type variable.
1987+
case PatternKind::Any: {
1988+
// If we have a type from an initializer expression, and that
1989+
// expression does not produce an InOut type, use it. This
1990+
// will avoid exponential typecheck behavior in the case of
1991+
// tuples, nested arrays, and dictionary literals.
1992+
//
1993+
// Otherwise, create a new type variable.
1994+
auto boundExpr = locator.trySimplifyToExpr();
1995+
1996+
if (boundExpr) {
1997+
auto boundExprTy = CS.getType(boundExpr);
1998+
if (!boundExprTy->is<InOutType>())
1999+
return boundExprTy->getRValueType();
2000+
}
2001+
19892002
return CS.createTypeVariable(CS.getConstraintLocator(locator),
19902003
TVO_CanBindToInOut);
2004+
}
19912005

19922006
case PatternKind::Named: {
19932007
auto var = cast<NamedPattern>(pattern)->getDecl();
1994-
2008+
auto isWeak = false;
2009+
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
2010+
isWeak = OA->get() == ReferenceOwnership::Weak;
2011+
19952012
auto boundExpr = locator.trySimplifyToExpr();
1996-
auto haveBoundCollectionLiteral = boundExpr &&
1997-
!var->hasNonPatternBindingInit() &&
1998-
(isa<ArrayExpr>(boundExpr) ||
1999-
isa<DictionaryExpr>(boundExpr));
2013+
auto haveBoundExpr = boundExpr && !var->hasNonPatternBindingInit();
20002014

2001-
// For a named pattern without a type, create a new type variable
2002-
// and use it as the type of the variable.
2015+
// If we have a type from an initializer expression, and that
2016+
// expression does not produce an InOut type, use it. This
2017+
// will avoid exponential typecheck behavior in the case of
2018+
// tuples, nested arrays, and dictionary literals.
20032019
//
2004-
// FIXME: For now, substitute in the bound type for literal collection
2005-
// exprs that would otherwise result in a simple conversion constraint
2006-
// being placed between two type variables. (The bound type and the
2007-
// collection type, which will always be the same in this case.)
2008-
// This will avoid exponential typecheck behavior in the case of nested
2009-
// array and dictionary literals.
2010-
Type ty = haveBoundCollectionLiteral ?
2011-
CS.getType(boundExpr) :
2012-
CS.createTypeVariable(CS.getConstraintLocator(locator),
2013-
TVO_CanBindToInOut);
2020+
// Otherwise, create a new type variable.
2021+
if (!isWeak && haveBoundExpr) {
2022+
auto boundExprTy = CS.getType(boundExpr);
2023+
if (!boundExprTy->is<InOutType>())
2024+
return boundExprTy->getRValueType();
2025+
}
2026+
2027+
Type ty = CS.createTypeVariable(CS.getConstraintLocator(locator),
2028+
TVO_CanBindToInOut);
20142029

20152030
// For weak variables, use Optional<T>.
2016-
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
2017-
if (OA->get() == ReferenceOwnership::Weak) {
2018-
ty = CS.getTypeChecker().getOptionalType(var->getLoc(), ty);
2019-
if (!ty) return Type();
2020-
}
2031+
if (isWeak)
2032+
return CS.getTypeChecker().getOptionalType(var->getLoc(), ty);
20212033

20222034
return ty;
20232035
}

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2131,7 +2131,9 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
21312131

21322132
Expr *foundSolution(Solution &solution, Expr *expr) override {
21332133
// Figure out what type the constraints decided on.
2134-
InitTypeAndInOut.setPointer(solution.simplifyType(InitTypeAndInOut.getPointer()));
2134+
auto ty = solution.simplifyType(InitTypeAndInOut.getPointer());
2135+
InitTypeAndInOut.setPointer(
2136+
ty->getRValueType()->reconstituteSugar(/*recursive =*/false));
21352137
InitTypeAndInOut.setInt(expr->isSemanticallyInOutExpr());
21362138

21372139
// Just keep going.

test/APINotes/versioned.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ func testAKA(structValue: ImportantCStruct, aliasValue: ImportantCAlias) {
118118

119119
let optStructValue: Optional = structValue
120120
let _: Int = optStructValue
121-
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'Optional<ImportantCStruct>' to specified type 'Int'
121+
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'ImportantCStruct?' to specified type 'Int'
122122

123123
let optAliasValue: Optional = aliasValue
124124
let _: Int = optAliasValue
125-
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'Optional<ImportantCAlias>' (aka 'Optional<Int32>') to specified type 'Int'
125+
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:16: error: cannot convert value of type 'ImportantCAlias?' (aka 'Optional<Int32>') to specified type 'Int'
126126
}
127127

128128
func testRenamedEnumConstants() {

test/Constraints/array_literal.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,15 @@ func defaultToAny(i: Int, s: String) {
129129

130130
let a2: Array = [1, "a", 3.5]
131131
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}}
132-
let _: Int = a2 // expected-error{{value of type 'Array<Any>'}}
132+
let _: Int = a2 // expected-error{{value of type '[Any]'}}
133133

134134
let a3 = [1, "a", nil, 3.5]
135135
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
136136
let _: Int = a3 // expected-error{{value of type '[Any?]'}}
137137

138138
let a4: Array = [1, "a", nil, 3.5]
139139
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
140-
let _: Int = a4 // expected-error{{value of type 'Array<Any?>'}}
140+
let _: Int = a4 // expected-error{{value of type '[Any?]'}}
141141

142142
let a5 = []
143143
// expected-error@-1{{empty collection literal requires an explicit type}}

test/Constraints/bridging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ func rdar20029786(_ ns: NSString?) {
302302

303303
// <rdar://problem/19813772> QoI: Using as! instead of as in this case produces really bad diagnostic
304304
func rdar19813772(_ nsma: NSMutableArray) {
305-
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>}}
305+
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>}}
306306
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}}
307307
var a3 = nsma as Array<AnyObject>
308308
}

test/Constraints/patterns.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,16 @@ func testOne() {
351351
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'}}
352352
}
353353
}
354+
355+
func overload(_ x: Int) -> Int { return x }
356+
func overload(_ x: Float) -> Float { return x }
357+
358+
func rdar20233198() {
359+
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))
360+
_ = typed
361+
let _ = (Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1))
362+
let named = (Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1), Int(1))
363+
_ = named;
364+
let _ = (overload(1), overload(1), overload(1), overload(1), overload(1), overload(1), overload(1), overload(1))
365+
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))
366+
}

test/decl/var/variables.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func tuplePatternDestructuring(_ x : Int, y : Int) {
8282
_ = i+j
8383

8484
// <rdar://problem/20395243> QoI: type variable reconstruction failing for tuple types
85-
let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple '(x: _, a: _)'}}
85+
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)'}}
8686
}
8787

8888
// <rdar://problem/21057425> Crash while compiling attached test-app.

validation-test/Sema/type_checker_perf/slow/rdar20233198.swift.gyb renamed to validation-test/Sema/type_checker_perf/fast/rdar20233198.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select incrementScopeCounter %s
1+
// RUN: %scale-test --begin 1 --end 5 --step 1 --select incrementScopeCounter %s
22
// REQUIRES: OS=macosx
33
// REQUIRES: asserts
44

0 commit comments

Comments
 (0)