swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.58k stars 10.36k forks source link

[SR-14401] [Compiler Type Check] Unexpected inference type with single expression closure #56758

Open swift-ci opened 3 years ago

swift-ci commented 3 years ago
Previous ID SR-14401
Radar rdar://problem/75839791
Original Reporter shanrongxin@bytedance.com (JIRA User)
Type Bug
Status Reopened
Resolution

Attachment: Download

Environment Xcode Version 12.4 (12D4e) Max OS X: 10.15.7 (19H524) Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28) Target: x86_64-apple-darwin19.6.0
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug | |Assignee | shanrongxin@bytedance.com (JIRA) | |Priority | Medium | md5: a0eff88e2eaf2eda0d7c1ced779797dc

Issue Description:

//代码占位符

let instance = Transform(element: 3)
let result1 = instance.foo{$0}.foo{ true }

struct Transform<E> {

    let element: E

    func foo<T>(transform: (E) -> T) -> TransformB<T> {
        // mappedElement is expected to be 3, but is '()'
        // because type checker infer the first closure {$0} to be "Int -> ()"(not Int -> Int) on Type Checker Stage
        let mappedElement = transform(element) // mappedElement: ()
        return TransformB<T>(element: mappedElement)
    }
}

struct TransformB<E> {
    let element: E
    func foo<T>(transform: (E) -> T) -> T {
        let transformResult = transform(element)
        return transformResult
    }
}

{$0} (which explicit argument is Int) is inferred to type (Int) -> () instead of (Int) -> (Int) on a specific situation . (actually there is a warning, but it should to be an error )

when use swiftc -dump ast

//代码占位符
---Constraint solving at [/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15 - line:4:42]---
  (overload set choice binding $T0 := Transform<Int>)
  (overload set choice binding $T1 := (($T2) -> $T3) -> TransformB<$T3>)
  (overload set choice binding $T8 := (($T9) -> $T10) -> $T10)
---Initial constraints for the given expression---
(call_expr type='$T10' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15 - line:4:42] arg_labels=_:
  (unresolved_dot_expr type='(($T3) -> $T10) -> $T10' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15 - line:4:32] field 'foo' function_ref=single
    (call_expr type='TransformB<$T3>' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15 - line:4:30] arg_labels=_:
      (unresolved_dot_expr type='((Int) -> $T3) -> TransformB<$T3>' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15 - line:4:24] field 'foo' function_ref=single
        (declref_expr type='Transform<Int>' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15 - line:4:15] decl=Contents.(file).instance@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:2:5 function_ref=unapplied))
      (paren_expr type='($T4)' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 - line:4:30] trailing-closure
        (closure_expr type='$T4' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 - line:4:30] discriminator=0 single-expression
          (parameter_list
            (parameter "$0") range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 - line:4:27])
          (declref_expr type='<null>' decl=Contents.(file).top-level code.explicit closure discriminator=0.$0@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 function_ref=unapplied)))))
  (paren_expr type='($T11)' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35 - line:4:42] trailing-closure
    (closure_expr type='$T11' location=/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35 range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35 - line:4:42] discriminator=1 single-expression
      (parameter_list range=[/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35 - line:4:35])
      (boolean_literal_expr type='<null>' value=true builtin_initializer=**NULL** initializer=**NULL**))))
Score: 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Contextual Type: <null>
Type Variables:
  $T0 [lvalue allowed] [noescape allowed] as Transform<Int> @ locator@0x7fc75e05f200 [DeclRef@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15]
  $T1 [lvalue allowed] [noescape allowed] as ((Int) -> $T3) -> TransformB<$T3> @ locator@0x7fc75e05f250 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member]
  $T2 as Int @ locator@0x7fc75e05f310 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member -> generic parameter 'E']
  $T3 potentially_incomplete fully_bound involves_type_vars #defaultable_bindings=1 bindings={<<unresolvedtype>>} @ locator@0x7fc75e05f370 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member -> generic parameter 'T']
  $T4 [noescape allowed] involves_type_vars bindings={(subtypes of) (Int) -> $T3} @ locator@0x7fc75e05f590 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27]
  $T5 [inout allowed] [noescape allowed] #defaultable_bindings=1 bindings={<<unresolvedtype>>} @ locator@0x7fc75e05f5e0 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 -> tuple element #&#8203;0]
  $T6 subtype_of_existential bindings={} @ locator@0x7fc75e05f630 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 -> closure result]
  $T7 [noescape allowed] as TransformB<$T3> @ locator@0x7fc75e05f778 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> function result]
  $T8 [lvalue allowed] [noescape allowed] as (($T3) -> $T10) -> $T10 @ locator@0x7fc75e05f8e0 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member]
  $T9 equivalent to $T3 @ locator@0x7fc75e05f9b0 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member -> generic parameter 'E']
  $T10 potentially_incomplete fully_bound involves_type_vars #defaultable_bindings=1 bindings={<<unresolvedtype>>} @ locator@0x7fc75e05fa10 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member -> generic parameter 'T']
  $T11 [noescape allowed] involves_type_vars bindings={(subtypes of) ($T3) -> $T10} @ locator@0x7fc75e05fc00 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35]
  $T12 subtype_of_existential bindings={} @ locator@0x7fc75e05fc50 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35 -> closure result]
  $T13 [noescape allowed] equivalent to $T10 @ locator@0x7fc75e05fd78 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> function result]Active Constraints:
  $T4 arg conv (Int) -> $T3 [[locator@0x7fc75e05f848 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> apply argument -> comparing call argument #&#8203;0 to parameter #&#8203;0]]];
  $T11 arg conv ($T3) -> $T10 [[locator@0x7fc75e05fe48 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> apply argument -> comparing call argument #&#8203;0 to parameter #&#8203;0]]];Inactive Constraints:
  $T4 closure can default to ($T5) -> $T6 [[locator@0x7fc75e05f590 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27]]];
  $T11 closure can default to () -> $T12 [[locator@0x7fc75e05fc00 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35]]];
Resolved overloads:
  selected overload set choice instance: $T0 == Transform<Int>
  selected overload set choice Transform<Int>.foo: $T1 == (($T2) -> $T3) -> TransformB<$T3>
  selected overload set choice TransformB<$T3>.foo: $T8 == (($T9) -> $T10) -> $T10
Opened types:
  locator@0x7fc75e05f250 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member] opens τ_0_0 -> $T2, τ_1_0 -> $T3
  locator@0x7fc75e05f8e0 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member] opens τ_0_0 -> $T9, τ_1_0 -> $T10
  ($T4 involves_type_vars bindings={(subtypes of) (Int) -> $T3})
  ($T11 involves_type_vars bindings={(subtypes of) ($T3) -> $T10})
  Initial bindings: $T4 := (Int) -> $T3
  (attempting type variable $T4 := (Int) -> $T3
    (overload set choice binding $T15 := $T14)
    Contracting constraint $T5 bind param $T14 [[locator@0x7fc75e05f5e0 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 -> tuple element #&#8203;0]]];
    ($T5 involves_type_vars bindings={(supertypes of) Int})
    ($T11 involves_type_vars bindings={(subtypes of) ($T3) -> $T10})
    Initial bindings: $T5 := Int
    (attempting type variable $T5 := Int
      ($T6 involves_type_vars bindings={(supertypes of) Int; (supertypes of) ()})
      ($T11 involves_type_vars bindings={(subtypes of) ($T3) -> $T10})
      Initial bindings: $T6 := Int, $T6 := ()
      (attempting type variable $T6 := Int
        ($T3 potentially_incomplete fully_bound involves_type_vars bindings={(supertypes of) Int})
        ($T11 involves_type_vars bindings={(subtypes of) ($T3) -> $T10})
        Initial bindings: $T11 := ($T3) -> $T10
        (attempting type variable $T11 := ($T3) -> $T10
          (increasing score due to function conversion)
          ($T3 potentially_incomplete bindings={(supertypes of) Int; (subtypes of) ()})
          ($T16 literal=3 involves_type_vars bindings={(subtypes of) (default from ExpressibleByBooleanLiteral) Bool})
          Initial bindings: $T3 := Int, $T3 := ()
          (attempting type variable $T3 := Int
            (failed constraint $T3 subtype () [[locator@0x7fc74e809b28 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> apply argument -> comparing call argument #&#8203;0 to parameter #&#8203;0 -> function argument]]];)
          )
          (attempting type variable $T3 := ()
            (failed constraint $T6 subtype $T3 [[locator@0x7fc75e060128 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> apply argument -> comparing call argument #&#8203;0 to parameter #&#8203;0 -> function result]]];)
          )
        )
      )
      (attempting type variable $T6 := ()
        (increasing score due to function conversion)
        ($T3 potentially_incomplete fully_bound involves_type_vars bindings={(supertypes of) ()})
        ($T11 involves_type_vars bindings={(subtypes of) ($T3) -> $T10})
        Initial bindings: $T11 := ($T3) -> $T10
        (attempting type variable $T11 := ($T3) -> $T10
          (increasing score due to function conversion)
          ($T3 potentially_incomplete bindings={(supertypes of) ()})
          ($T17 literal=3 involves_type_vars bindings={(subtypes of) (default from ExpressibleByBooleanLiteral) Bool})
          Initial bindings: $T3 := ()
          (attempting type variable $T3 := ()
            ($T17 literal=3 involves_type_vars bindings={(subtypes of) (default from ExpressibleByBooleanLiteral) Bool})
            Initial bindings: $T17 := Bool
            (attempting type variable $T17 := Bool
              ($T12 involves_type_vars bindings={(supertypes of) Bool; (supertypes of) ()})
              Initial bindings: $T12 := Bool, $T12 := ()
              (attempting type variable $T12 := Bool
                ($T10 potentially_incomplete bindings={(supertypes of) Bool})
                Initial bindings: $T10 := Bool
                (attempting type variable $T10 := Bool
                  (found solution 0 0 0 0 0 0 0 2 0 0 0 0 0 0)
                )
              )
              (attempting type variable $T12 := ()
                (increasing score due to function conversion)
                (solution is worse than the best solution)
              )
            )
          )
        )
      )
    )
  )
---Solver statistics---
Total number of scopes explored: 14
Maximum depth reached while exploring solutions: 9
Time: 9.084000e+00ms
---Solution---
Fixed score: 0 0 0 0 0 0 0 2 0 0 0 0 0 0
Type variables:
  $T7 as TransformB<()> @ locator@0x7fc75e05f778 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> function result]
  $T15 as Int @ locator@0x7fc75e05ffb0 [DeclRef@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:28]
  $T3 as () @ locator@0x7fc75e05f370 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member -> generic parameter 'T']
  $T17 as Bool @ locator@0x7fc75e0601c8 [BooleanLiteral@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:37]
  $T2 as Int @ locator@0x7fc75e05f310 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member -> generic parameter 'E']
  $T14 as Int @ locator@0x7fc75e05f5e0 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 -> tuple element #&#8203;0]
  $T10 as Bool @ locator@0x7fc75e05fa10 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member -> generic parameter 'T']
  $T0 as Transform<Int> @ locator@0x7fc75e05f200 [DeclRef@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15]
  $T5 as Int @ locator@0x7fc75e05f5e0 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 -> tuple element #&#8203;0]
  $T1 as ((Int) -> ()) -> TransformB<()> @ locator@0x7fc75e05f250 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member]
  $T6 as () @ locator@0x7fc75e05f630 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 -> closure result]
  $T4 as (Int) -> () @ locator@0x7fc75e05f590 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27]
  $T9 as () @ locator@0x7fc75e05f9b0 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member -> generic parameter 'E']
  $T13 as Bool @ locator@0x7fc75e05fd78 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> function result]
  $T8 as ((()) -> Bool) -> Bool @ locator@0x7fc75e05f8e0 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member]
  $T12 as Bool @ locator@0x7fc75e05fc50 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35 -> closure result]
  $T11 as () -> Bool @ locator@0x7fc75e05fc00 [Closure@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:35]Overload choices:
  locator@0x7fc75e05ffb0 [DeclRef@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:28] with Contents.(file).top-level code.explicit closure discriminator=0.$0@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:27 as $0: $T14  locator@0x7fc75e05f200 [DeclRef@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:15] with Contents.(file).instance@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:2:5 as instance: Transform<Int>  locator@0x7fc75e05f250 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member] with Contents.(file).Transform.foo(transform:)@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:11:10 as Transform<Int>.foo: (($T2) -> $T3) -> TransformB<$T3>  locator@0x7fc75e05f8e0 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member] with Contents.(file).TransformB.foo(transform:)@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:23:10 as TransformB<$T3>.foo: (($T9) -> $T10) -> $T10
Constraint restrictions:
  TransformB<()> to TransformB<()> is [deep equality]
  Transform<Int> to Transform<Int> is [deep equality]Trailing closure matching:
  locator@0x7fc75e05f828 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> apply argument]: forward
  locator@0x7fc75e05fe28 [Call@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> apply argument]: forwardDisjunction choices:Opened types:
  locator@0x7fc75e05f250 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:24 -> member] opens τ_0_0 -> $T2, τ_1_0 -> $T3
  locator@0x7fc75e05f8e0 [UnresolvedDot@/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:32 -> member] opens τ_0_0 -> $T9, τ_1_0 -> $T10
/Users/Rico/Rico/Program/BugReport.playground/Contents.swift:4:28: warning: expression of type 'Int' is unused
let result1 = instance.foo{$0}.foo{ true }

playground file is in attachment

Thanks !

typesanitizer commented 3 years ago

@swift-ci create

xedin commented 3 years ago

Type-checker would produce expected solution if second closure were to specify a parameter e.g. '.foo { $0 }.foo { _ in true }', otherwise `E` from `TransformB` gets defaulted to `Void` because it's the only valid solution here for second closure to be of type `(()) -> Bool`.

swift-ci commented 3 years ago

Comment by Rongxin Shan (JIRA)

Thanks for your explanation.

But I think the code in second closure make the logic of first closure be unexpected, this may be confusing.

Here could be a common situation: e.g

//代码占位符

struct Data { }

let arr = [1, nil, 2, 6]

let mappedArr = arr.compactMap{ $0 }.map{ Data() }  // result: [Data, Data, Data, Data]

Or maybe compiler can throws a better diagnostic ?

Look forward to your reply. Thanks. @xedin

xedin commented 3 years ago

shanrongxin@bytedance.com (JIRA User) We can't change the behavior (e.g. to fail and produce a diagnostic) without source compatibility impact so our hands are tied here unfortunately.