@@ -2124,6 +2124,13 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
21242124 */
21252125 default predicate cachedStageRevRef ( ) { none ( ) }
21262126
2127+ /**
2128+ * Point this predicate to the `inferType` predicate in the output of this module.
2129+ *
2130+ * Needed to be able to refer to `inferType` in default signature implementations.
2131+ */
2132+ Type inferType ( AstNode n , TypePath path ) ;
2133+
21272134 /** A boolean type. */
21282135 class BoolType extends Type ;
21292136
@@ -2221,29 +2228,64 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
22212228 AstNode getRightOperand ( ) ;
22222229 }
22232230
2224- class CallTarget {
2231+ /**
2232+ * A position where a callable can have a declared type.
2233+ */
2234+ class TypePosition {
2235+ /** Holds if this position represents the return type of a callable. */
2236+ predicate isReturn ( ) ;
2237+
2238+ /** Gets a textual representation of this position. */
2239+ string toString ( ) ;
2240+ }
2241+
2242+ /** A context needed to resolve calls. */
2243+ bindingset [ this ]
2244+ class CallResolutionContext {
2245+ /** Gets a textual representation of this context. */
2246+ bindingset [ this ]
2247+ string toString ( ) ;
2248+ }
2249+
2250+ /** A callable. */
2251+ class Callable {
22252252 TypeParameter getTypeParameter ( TypeParameterPosition ppos ) ;
22262253
22272254 TypeMention getAdditionalTypeParameterConstraint ( TypeParameter tp ) ;
22282255
2229- Type getReturnType ( TypePath path ) ;
2256+ /* Gets the declared type of this callable at `path` for position `pos`. */
2257+ Type getDeclaredType ( TypePosition pos , TypePath path ) ;
22302258
2231- Type getParameterType ( int index , TypePath path ) ;
2232-
2233- /** Gets a textual representation of this element. */
2259+ /** Gets a textual representation of this callable. */
22342260 string toString ( ) ;
22352261
2236- /** Gets the location of this element . */
2262+ /** Gets the location of this callable . */
22372263 Location getLocation ( ) ;
22382264 }
22392265
22402266 class Call extends Expr {
22412267 Type getTypeArgument ( TypeArgumentPosition apos , TypePath path ) ;
22422268
2269+ AstNode getNodeAt ( TypePosition pos ) ;
2270+
22432271 /** Gets the target of this call. */
2244- CallTarget getTargetCertain ( ) ;
2272+ Callable getTargetCertain ( ) ;
2273+
2274+ /** Gets the target of this call. */
2275+ Callable getTarget ( CallResolutionContext ctx ) ;
22452276 }
22462277
2278+ /** Gets the inferred type `call` at `path` for position `pos` in context `ctx`. */
2279+ bindingset [ ctx]
2280+ default Type inferCallTypeIn (
2281+ Call call , CallResolutionContext ctx , TypePosition pos , TypePath path
2282+ ) {
2283+ result = inferType ( call .getNodeAt ( pos ) , path ) and
2284+ exists ( ctx )
2285+ }
2286+
2287+ Type inferCallTypeOut ( AstNode n , TypePosition pos , TypePath path ) ;
2288+
22472289 /**
22482290 * Holds if the types of `n1` at `path1` and `n2` at `path2` are certainly equal.
22492291 */
@@ -2330,16 +2372,19 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
23302372
23312373 pragma [ nomagic]
23322374 private Type getCertainCallExprType ( Call call , TypePath path ) {
2333- forex ( CallTarget target | target = call .getTargetCertain ( ) |
2334- result = target .getReturnType ( path )
2375+ exists ( TypePosition ret |
2376+ ret .isReturn ( ) and
2377+ forex ( Callable target | target = call .getTargetCertain ( ) |
2378+ result = target .getDeclaredType ( ret , path )
2379+ )
23352380 )
23362381 }
23372382
23382383 pragma [ nomagic]
23392384 private Type inferCertainCallExprType ( Call call , TypePath path ) {
23402385 exists ( Type ty , TypePath prefix | ty = getCertainCallExprType ( call , prefix ) |
23412386 exists (
2342- CallTarget target , TypePath suffix , TypeParameterPosition tppos ,
2387+ Callable target , TypePath suffix , TypeParameterPosition tppos ,
23432388 TypeArgumentPosition tapos
23442389 |
23452390 ty = target .getTypeParameter ( tppos ) and
@@ -2497,15 +2542,104 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
24972542 (
24982543 result = inferTypeEquality ( n , path )
24992544 or
2545+ result = CheckContextTyping< inferCallTypeOut / 3 > :: check ( n , path )
2546+ or
25002547 result = inferTypeInput ( n , path )
25012548 )
25022549 }
25032550
2551+ private module TypePositionMatchingInput {
2552+ class DeclarationPosition = TypePosition ;
2553+
2554+ class AccessPosition = DeclarationPosition ;
2555+
2556+ predicate accessDeclarationPositionMatch ( AccessPosition apos , DeclarationPosition dpos ) {
2557+ apos = dpos
2558+ }
2559+ }
2560+
2561+ /**
2562+ * A matching configuration for resolving types of calls.
2563+ */
2564+ private module CallMatchingInput implements MatchingWithEnvironmentInputSig {
2565+ import TypePositionMatchingInput
2566+
2567+ class Declaration = Callable ;
2568+
2569+ bindingset [ decl]
2570+ TypeMention getATypeParameterConstraint ( TypeParameter tp , Declaration decl ) {
2571+ result = Input2:: getATypeParameterConstraint ( tp ) and
2572+ exists ( decl )
2573+ or
2574+ result = decl .getAdditionalTypeParameterConstraint ( tp )
2575+ }
2576+
2577+ class AccessEnvironment = CallResolutionContext ;
2578+
2579+ final private class CallFinal = Call ;
2580+
2581+ class Access extends CallFinal {
2582+ bindingset [ e]
2583+ Type getInferredType ( AccessEnvironment e , AccessPosition apos , TypePath path ) {
2584+ result = inferCallTypeIn ( this , e , apos , path )
2585+ }
2586+ }
2587+ }
2588+
2589+ private module CallMatching = MatchingWithEnvironment< CallMatchingInput > ;
2590+
2591+ pragma [ nomagic]
2592+ Type inferCallTypeOut (
2593+ Call call , TypePosition pos , AstNode n , CallResolutionContext ctx , TypePath path
2594+ ) {
2595+ n = call .getNodeAt ( pos ) and
2596+ result = CallMatching:: inferAccessType ( call , ctx , pos , path )
2597+ }
2598+
2599+ pragma [ nomagic]
2600+ private predicate hasUnknownTypeAt ( AstNode n , TypePath path ) {
2601+ inferType ( n , path ) instanceof UnknownType
2602+ }
2603+
2604+ pragma [ nomagic]
2605+ private predicate hasUnknownType ( AstNode n ) { hasUnknownTypeAt ( n , _) }
2606+
2607+ private signature Type inferCallTypeSig ( AstNode n , TypePosition pos , TypePath path ) ;
2608+
2609+ /**
2610+ * Given a predicate `inferCallType` for inferring the type of a call at a given
2611+ * position, this module exposes the predicate `check`, which wraps the input
2612+ * predicate and checks that types are only propagated into arguments when they
2613+ * are context-typed.
2614+ */
2615+ module CheckContextTyping< inferCallTypeSig / 3 inferCallType> {
2616+ pragma [ nomagic]
2617+ private Type inferCallNonReturnType ( AstNode n , TypePath prefix , TypePath path ) {
2618+ exists ( TypePosition pos |
2619+ result = inferCallType ( n , pos , path ) and
2620+ hasUnknownType ( n ) and
2621+ not pos .isReturn ( ) and
2622+ prefix = path .getAPrefix ( )
2623+ )
2624+ }
2625+
2626+ pragma [ nomagic]
2627+ Type check ( AstNode n , TypePath path ) {
2628+ result = inferCallType ( n , any ( TypePosition pos | pos .isReturn ( ) ) , path )
2629+ or
2630+ exists ( TypePath prefix |
2631+ result = inferCallNonReturnType ( n , prefix , path ) and
2632+ hasUnknownTypeAt ( n , prefix )
2633+ )
2634+ }
2635+ }
2636+
25042637 /**
25052638 * Gets the inferred root type of `n`, if any.
25062639 */
25072640 Type inferType ( AstNode n ) { result = inferType ( n , TypePath:: nil ( ) ) }
25082641
2642+ // todo: consistency checks
25092643 /** The cached stage of type inference. */
25102644 cached
25112645 module CachedStage {
0 commit comments