Skip to content

Fix exponential type checking of tuple literals. #15419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 36 additions & 24 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 isWeak = false;
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
isWeak = OA->get() == ReferenceOwnership::Weak;

auto boundExpr = locator.trySimplifyToExpr();
auto haveBoundCollectionLiteral = boundExpr &&
!var->hasNonPatternBindingInit() &&
(isa<ArrayExpr>(boundExpr) ||
isa<DictionaryExpr>(boundExpr));
auto haveBoundExpr = boundExpr && !var->hasNonPatternBindingInit();

// For a named pattern without a type, create a new type variable
// and use it as the type of the variable.
// 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;
}
Expand Down
4 changes: 3 additions & 1 deletion lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions test/APINotes/versioned.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
4 changes: 2 additions & 2 deletions test/Constraints/array_literal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,15 @@ 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}}
let _: Int = a3 // expected-error{{value of type '[Any?]'}}

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}}
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/bridging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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>
}
Expand Down
13 changes: 13 additions & 0 deletions test/Constraints/patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
2 changes: 1 addition & 1 deletion test/decl/var/variables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down