[GHC] #10585: Implement proper bidirectional type inference

GHC ghc-devs at haskell.org
Sun Jun 28 01:52:19 UTC 2015


#10585: Implement proper bidirectional type inference
-------------------------------------+-------------------------------------
              Reporter:  goldfire    |             Owner:
                  Type:  bug         |            Status:  new
              Priority:  normal      |         Milestone:
             Component:  Compiler    |           Version:  7.10.1
  (Type checker)                     |  Operating System:  Unknown/Multiple
              Keywords:              |   Type of failure:  None/Unknown
          Architecture:              |        Blocked By:
  Unknown/Multiple                   |   Related Tickets:
             Test Case:              |
              Blocking:              |
Differential Revisions:              |
-------------------------------------+-------------------------------------
 The [http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-
 rank/putting.pdf Practical Type Inference paper] (JFP '07) lays out a
 nice, relatively straightforward bidirectional type inference algorithm
 for GHC.

 The problem is that there is not great hygiene practiced around
 transitions between "down" checking and "up" checking, neither in the
 paper nor in the implementation. Examine Figure 8 of the paper, which
 presents the bidirectional typing rules. There are several syntactic forms
 which are fundamentally "up" forms -- forms that cannot gain any benefit
 from any type information being propagated downwards. Examples include
 App, Var, and Annot. Other forms propagate the very direction they are
 operated in, such as Let. And then some forms depend somewhat delicately
 on direction, such as the Abs rules.

 I propose changing these rules to be more in the style of Figure 4 from
 Dunfield & Krishnaswami's ICFP'13 paper [http://arxiv.org/pdf/1306.6032v1
 here]. The particular rule I'm interested in is DeclSub, paraphrased here:

 {{{
 G |-up e : s1
 s1 <= s2
 ---------------
 G |-down e : s2
 }}}

 (I'm not advocating other aspects of their approach, just the general idea
 around this rule being the only transition between down and up.)

 In GHC, mediating between "down" checking and "up" checking is done in an
 ad-hoc manner, made even more difficult by the fact that it takes two
 separate steps to pull off properly: we must first `deeplyInstantiate` the
 "up" type that we get and then call `tcSubType` (or one of its variants).
 And, because this has to be done for each "up" expression form (forms that
 cannot use down-propagated type information), it's easy to miss a case. As
 a case in point, consider the following code:

 {{{
 foo :: forall b. b -> (Int -> Int) -> b
 foo = undefined

 bar :: ((forall a. a -> a) -> Int) -> Int
 bar = undefined

 baz = bar (foo 3)
 bum = bar (3 `foo`)
 }}}

 In 7.10.1, `baz` is accepted, while `bum` is not. This is because the code
 for left-sections doesn't ever call `tcSubType` -- it uses `unifyType`.

 While that one case is easy enough to fix, I advocate a redesign of
 `tcExpr` to break it into two functions: one for "down" forms and one for
 "up" forms. These would, of course, be mutually recursive in order so that
 both can handle all expression forms. But these transitions could then be
 policed more easily.

 I don't actually think this would entail all that much work, and it would
 structurally remove the possibility of bugs such as the one above.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10585>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list