mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user