@@ -2471,7 +2471,30 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
24712471 TypeMention getReturnType ( ) ;
24722472 }
24732473
2474- /** A special pseudo type representing a particular closure parameter. */
2474+ /**
2475+ * A special pseudo type representing a particular closure parameter.
2476+ *
2477+ * This is needed in cases where the type of a closure parameter must be
2478+ * inferred from the inferred _return type_ of the closure. For example,
2479+ * in
2480+ *
2481+ * ```rust
2482+ * let c = |x| x;
2483+ * let r: i32 = c(42);
2484+ * ```
2485+ *
2486+ * by assigning `x` the pseudo type `T_x`, we can
2487+ *
2488+ * 1. bottom-up infer that `c` has type `Fn(_) -> T_x`,
2489+ * 2. this enables us to detect that contextual inference is needed, so we also
2490+ * assign `c` the type `Fn(_) -> UnknownType`,
2491+ * 3. using bottom-up inference allows us to infer that `c(42)` must have
2492+ * `UnknownType`,
2493+ * 4. using contextual inference allows us to infer that `c` has type `Fn(_) -> i32`, and
2494+ * 5. finally since `c` also has type `Fn(_) -> T_x`, we conclude that `x` has type `i32`.
2495+ *
2496+ * . Then, when we propagate type information downwards from the
2497+ */
24752498 class ClosureParameterPseudoType extends PseudoType ;
24762499
24772500 /** Gets the pseudo type corresponding to closure parameter `p`. */
@@ -2554,6 +2577,15 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
25542577 )
25552578 }
25562579
2580+ private predicate closureStep ( AstNode n1 , TypePath path1 , Closure n2 , TypePath path2 ) {
2581+ exists ( int index , Parameter p |
2582+ n1 = p .getPattern ( ) and
2583+ p = n2 .getParameter ( index ) and
2584+ path1 .isEmpty ( ) and
2585+ path2 = getClosureParameterTypePath ( p )
2586+ )
2587+ }
2588+
25572589 /** Provides logic for inferring certain type information. */
25582590 private module Certain {
25592591 predicate stepCertain ( AstNode n1 , TypePath path1 , AstNode n2 , TypePath path2 ) {
@@ -2572,27 +2604,17 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
25722604 or
25732605 n1 = n2 .( ParenExpr ) .getExpr ( )
25742606 )
2575- or
2576- exists ( Closure c , int index , Parameter p |
2577- path1 .isEmpty ( ) and
2578- p = c .getParameter ( index )
2579- |
2580- n1 = p and
2581- n2 = c and
2582- path2 = getClosureParameterTypePath ( n1 )
2583- or
2584- n1 = p .getPattern ( ) and
2585- n2 = p and
2586- path2 .isEmpty ( )
2587- )
25882607 }
25892608
25902609 pragma [ nomagic]
25912610 private Type inferTypeFromStepCertain ( AstNode n , TypePath path ) {
25922611 exists ( TypePath path1 , AstNode n2 , TypePath path2 , TypePath suffix |
25932612 result = inferTypeCertain ( n2 , path2 .appendInverse ( suffix ) ) and
2594- path = path1 .append ( suffix ) and
2613+ path = path1 .append ( suffix )
2614+ |
25952615 stepCertain ( n2 , path2 , n , path1 )
2616+ or
2617+ closureStep ( n2 , path2 , n , path1 )
25962618 )
25972619 }
25982620
@@ -2716,8 +2738,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
27162738 private Type inferTypeFromStep ( AstNode n , TypePath path ) {
27172739 exists ( TypePath path1 , AstNode n2 , TypePath path2 , TypePath suffix |
27182740 result = inferType ( n2 , path2 .appendInverse ( suffix ) ) and
2719- path = path1 .append ( suffix ) and
2741+ path = path1 .append ( suffix )
2742+ |
27202743 step ( n2 , path2 , n , path1 )
2744+ or
2745+ closureStep ( n2 , path2 , n , path1 ) and
2746+ not result = getClosureParameterPseudoType ( n .( Closure ) .getParameter ( _) )
27212747 )
27222748 }
27232749
@@ -2752,8 +2778,11 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
27522778 or
27532779 exists ( TypePath path1 , AstNode n2 , TypePath path2 , TypePath suffix |
27542780 result = inferType ( n2 , path2 .appendInverse ( suffix ) ) and
2755- path = path1 .append ( suffix ) and
2781+ path = path1 .append ( suffix )
2782+ |
27562783 step ( n , path1 , n2 , path2 )
2784+ or
2785+ closureStep ( n , path1 , n2 , path2 )
27572786 )
27582787 }
27592788
@@ -2765,31 +2794,43 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
27652794 prefix = path .getAPrefix ( )
27662795 }
27672796
2768- private Type inferClosureType ( AstNode n , TypePath path ) {
2797+ pragma [ nomagic]
2798+ private predicate hasClosureParameterPseudoType ( AstNode n , Parameter p , TypePath path ) {
2799+ inferType0 ( n , path ) = getClosureParameterPseudoType ( p )
2800+ }
2801+
2802+ /**
2803+ * If the type of a parameter occurs in the inferred return type of the closure,
2804+ * allow for contextual inference based on the inferred return type to propagate
2805+ * type information into the parameter
2806+ */
2807+ private Type inferClosureParameterType ( AstNode n , TypePath path ) {
27692808 exists ( Closure c , Parameter p | p = c .getParameter ( _) |
2770- n = p .getPattern ( ) and
2771- path .isEmpty ( ) and
2772- result = getClosureParameterPseudoType ( p )
2773- or
2774- exists ( TypePath ret | inferType ( c , ret ) = getClosureParameterPseudoType ( p ) |
2809+ not exists ( p .getType ( ) ) and
2810+ (
2811+ n = p .getPattern ( ) and
2812+ path .isEmpty ( ) and
2813+ result = getClosureParameterPseudoType ( p )
2814+ or
27752815 n = c and
2776- path = ret and
2816+ path = getClosureParameterTypePath ( p ) and
27772817 result instanceof UnknownType
27782818 or
2779- inferType ( c , ret .appendInverse ( path ) ) = result and
27802819 n = p .getPattern ( ) and
2820+ result = inferType ( c , getClosureParameterTypePath ( p ) .appendInverse ( path ) ) and
27812821 not result instanceof UnknownType
27822822 )
27832823 or
2824+ hasClosureParameterPseudoType ( c , p , path ) and
2825+ n = c and
2826+ result instanceof UnknownType
2827+ or
27842828 exists ( AstNode n0 , TypePath prefix |
2785- inferType ( n0 , prefix ) = getClosureParameterPseudoType ( p ) and
2786- result = inferTypeCertain ( n0 , prefix .appendInverse ( path ) ) and
2787- n = p .getPattern ( )
2829+ hasClosureParameterPseudoType ( n0 , p , prefix ) and
2830+ result = inferType ( n0 , prefix .appendInverse ( path ) ) and
2831+ n = p .getPattern ( ) and
2832+ not result instanceof UnknownType
27882833 )
2789- // or
2790- // n = p.getPattern() and
2791- // result = inferType(p, path) and
2792- // not result instanceof UnknownType
27932834 )
27942835 }
27952836
@@ -2815,7 +2856,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
28152856 or
28162857 result = inferConstructionReturnType ( n , path )
28172858 or
2818- result = inferClosureType ( n , path )
2859+ result = inferClosureParameterType ( n , path )
28192860 or
28202861 // contextual typing: only propagate type information from surrounding context
28212862 // into a node which has an explicitly unknown type
@@ -2842,7 +2883,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
28422883 |
28432884 not Certain:: certainTypeConflict ( n , prefix , path , result )
28442885 or
2845- result instanceof PseudoType and not result instanceof UnknownType // todo
2886+ // propagate closure parameter pseudo types even when there is certain information,
2887+ // but prevent propagation outside of the closure
2888+ exists ( Parameter p |
2889+ result = getClosureParameterPseudoType ( p ) and
2890+ not p = n .( Closure ) .getParameter ( _)
2891+ )
28462892 )
28472893 or
28482894 // If `n` has an explicitly unknown type at `prefix` and at the same time a certain
0 commit comments