[GHC] #15009: Float equalities past local equalities

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature | Status: new request | Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- Consider the two datatypes {{{#!hs data T1 a where MkT1 :: a ~ b => b -> T1 a data T2 a where MkT2 :: a -> T2 a }}} To me, these look like two ways of writing the same thing. Yet they participate quite differently in type inference. If I say {{{#!hs f (MkT1 a) = not a g (MkT2 a) = not a }}} then `g` is accepted (with type `T2 Bool -> Bool`) while `f` is rejected as GHC mutters about untouchable type variables. Of course, GHC's notion of untouchable type variables is a Good Thing, with arguments I won't rehearse here. It all boils down to this rule: (*) Information that arises in the context of an equality constraint cannot force unification of a unification variable born outside that context. I think this rule is a bit too strict, though. I propose a new rule: (%) Information that arises in the context of a ''non-purely-local'' equality constraint cannot force unification of a unification variable born outside that context. where '''Definition:''' An equality constraint is ''purely local'' if every type variable free in the constraint has the same scope as the constraint itself. That is, if an equality constraint mentions only variables freshly brought into scope -- and no others -- then we can still unify outer unification variables. Happy consequences of this choice: * `f` above could be accepted, as the equality in `MkT1` is purely local. * Rule (%) allows users to effectively let-bind type variables, like this: {{{#!hs f :: forall a. (a ~ SOME REALLY LONG TYPE) => Maybe a -> Maybe a -> Either a a }}} Currently, this kind of definition (if it's, say, within a `where` clause) triggers rule (*) in the body of the function, and thus causes type inference to produce a different result than just inlining `SOME REALLY LONG TYPE`. * We could theoretically simplify our treatment of GADTs. Right now, if you say {{{#!hs data G a b c where MkG :: MkG d Int [e] }}} GHC has to cleverly figure out that `d` is a universal variable and that `e` is an existential, producing the following Core: {{{#!hs data G a b c where MkG :: forall d b c. forall e. (b ~ Int, c ~ [e]) => MkG d b c }}} If we use rule (%), then I believe the following Core would behave identically: {{{#!hs data G a b c where MkG :: forall a b c. forall d e. (a ~ d, b ~ Int, c ~ [e]). MkG a b c }}} This treatment is more uniform and easier to implement. (Note that the equality constraints themselves are unboxed, so there's no change in runtime performance.) What are the downsides of (%)? I don't think there are any, save an easy extra line or two in GHC to check for locality. Rule (*) exists because, without it, GHC can infer non-principal types. However, I conjecture that Rule (%) also serves to require inference of only principal types, while being more permissive than Rule (*). I'm curious for your thoughts on this proposal. (I am making it here, as this is really an implementation concern, with no discernible effect on, say, the user manual, although it would allow GHC to accept more programs.) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Actually GHC already does this, or something very like it. See `Note [When does an implication have given equalities?]` and especially `Note [Let-bound skolems]` in `TcSMonad`. I don't say that it's perfectly implemented (eg I'm a bit worried about whether it's sensitive to whether the constraint looks like `a ~ b` or `b ~ a`) but it does more or less what you way. Your condition (%) doesn't look right. After all, in your example the constraint `a ~ b` has both `a` and `b` free -- and the former is not locally bound. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by Iceland_jack): * cc: Iceland_jack (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): So I suppose I'm precisely after `Note [Let-bound skolems]`. But it doesn't seem to be working in the cases above (I haven't yet explored why, but I will). You're right that my (%) is wrong. Of course a purely local equality (as defined above) should not affect floating, but also a let-bind-like equality should, too. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by nfrisby): * cc: nfrisby (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): I took a look. Turns out that it is what I guessed:
I don't say that it's perfectly implemented (eg I'm a bit worried about whether it's sensitive to whether the constraint looks like a ~ b or b ~ a)
We end up with {{{ Implic { TcLevel = 2 Skolems = a_a2do[sk:2] p_a2dt[sk:2] No-eqs = True Status = Unsolved Given = Wanted = WC {wc_impl = Implic { TcLevel = 3 Skolems = b_a2dp[ssk:3] No-eqs = True Status = Unsolved Given = $d~_a2dq :: (b_a2dp[ssk:3] :: *) ~ (a_a2do[tau:2] :: *) Wanted = WC {wc_simple = [WD] hole{co_a2dE} {1}:: (p_a2dt[sk:2] :: *) GHC.Prim.~# (Bool :: *) (CNonCanonical) [WD] hole{co_a2dR} {1}:: (a_a2do[sk:2] :: *) GHC.Prim.~# (Bool :: *) (CNonCanonical)} a pattern with constructor: MkT1 :: forall b a. ((b :: *) ~ (a :: *)) => b -> T1 a }}} After expanding superclases etc, that Given `b_a2dp ~ a_a2do` turns into {{{ [G] (a_a2do[tau:2] :: *) GHC.Prim.~# (b_a2dp[ssk:3] :: *) }}} with the unification variable carefully placed on the left (see `TcUnify.swapOverTyVars`. But alas `TcSMonad.getNoGivenEqs` only looks on the left in the test `skolem_bound_here`. So it conservatively says "this implication may bind non-local equalities" and we lose. I see two ways to fix this 1. Make `getNoGivenEqs` look on the right as well as the left. That, a Given `CTyEqCan` equality `b ~# a` would not count as a "given equality" if `a` is a skolem bound by this implication. 2. Change `swapOverTyVars` so that puts the skolem (not the meta-tyvar) on the left for Givens. (No change for Wanteds.) I think (2) is better. I think (1) won't help at all. Consider doing type inference for `f`: {{{ data T1 a where MkT1 :: a ~ b => b -> T1 a f (MkT1 a) = not a }}} We decide (roughly) that `f :: T alpha[1] -> beta[1]`, and then simplify this implication {{{ forall[2] b. (alpha[1] ~# b) => alpha[1] ~# Bool, beta[1] ~# Bool }}} (The `[1]` and `[2]` are the level numbers; `alpha[1]` is untouchable inside the `[2]` implication.) If we orient the Given as shown, with `alpha[1]` on the left, we'll rewrite the Wanteds thus: {{{ forall[2] b. (alpha[1] ~# b) => b ~# Bool, beta[1] ~# Bool }}} And now, ''even if we say that the Given doesn't count as a "given equality"'', we still can't solve that `b ~# Bool`. (We could float and solve `beta[1] ~# Bool`, but that's only half the problem.) What we want is to orient the Given the other way round `b ~# alpha[1]`. Then we could float out ''both'' Wanteds. Another way to think about putting `b` on the left is that it helps to eliminate occurrences of skolems, which might prevent floating. Richard, any thoughts? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): I very much like 2a, especially because it has an articulable reason behind it. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities
-------------------------------------+-------------------------------------
Reporter: goldfire | Owner: (none)
Type: feature request | Status: new
Priority: normal | Milestone: 8.4.3
Component: Compiler | Version: 8.2.2
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Simon Peyton Jones

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nomeata): Nice! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by simonpj): * testcase: => gadt/T15009 * status: new => closed * resolution: => fixed -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): Very happy to see the progress on this ticket! I think it would even be worthwhile for GHC to be a bit more aggressive here. In particular, in the snippet below, I'd like GHC to infer that {{{test :: () -> NT ((:~:) Char) Maybe}}}. I see this comment's example as decomposing the ticket's example {{{T1}}} into {{{:~:}}} and {{{NT}}}: the equality constraint and the {{{forall}}} need not be collocated. If I understand correctly, the key hurdle to that inference is revealed on line 3640 of the tc-trace below and the reason {{{getNoGivenEqs}}} reports {{{False}}} here is because {{{x_a1d8[sk:2]}}} is level 2 whereas the implication is level 3. I haven't found any Notes explaining why {{{getNoGivenEqs}}} checks that the skolem is from the current implication. Would it be safe and worthwhile to relax this check to, for example, as in this this particular case, require that there be no givens between the skolem's level and the current implication's level? (Or more loosely perhaps: no such equality- like givens, or even no equality-like givens involving this skolem?) {{{ $ cat Test.hs {-# LANGUAGE GADTs, LambdaCase, Rank2Types #-} {-# OPTIONS_GHC -ddump-tc-trace #-} module Test where import Data.Type.Equality newtype NT f g = MkNT{appNT :: forall x. f x -> g x} test () = MkNT (\case Refl -> Just 'c') $ ./ghc-8.6.0.20180810/bin/ghc.exe --make Test.hs >catch 2>&1 $ grep --text -B7 -A5 -n -e 'May have given eq' catch [snip] -- 3634- final wc = WC {wc_simple = 3635- [WD] hole{co_a1dv} {1}:: g_a1d6[tau:1] 3636- GHC.Prim.~# Maybe (CNonCanonical) 3637- [WD] hole{co_a1dy} {2}:: a_a1dx[tau:1] 3638- GHC.Prim.~# Char (CNonCanonical)} 3639- current evbinds = {} 3640-getNoGivenEqs 3641: May have given equalities 3642- Skols: [] 3643- Inerts: {Equalities: [G] co_a1dj {0}:: x_a1d8[sk:2] 3644- GHC.Prim.~# a_a1dx[tau:1] (CTyEqCan) 3645- Unsolved goals = 0} 3646- Insols: {} -- [snip] }}} Thank you for your time. -Nick P.S. - My Reddit post from a few months ago gives a bit more discussion of this kind of example; https://www.reddit.com/r/haskell/comments/8h9mz8/a_way_to_improve_inference_... Thanks again, Richard, for pointed me here from there; timely! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): I think you are referring to `Note [Let-bound skolems]` in `TcSMonad`? Suppose we have {{{ data T where MkT :: (a ~ Int) => a -> T }}} Here `a` is existentially bound, but it's really just a let-binding; we may as well have written {{{ data T where MkT :: Int -> T }}} Now consider type inference on this, where we try `f :: alpha -> T -> beta`. {{{ f x y = case y of MkT -> x }}} We'll get an implication constraint {{{ forall[2] a. (a~Int) => alpha[1] ~ beta[1] }}} Can we float that equality out, and unify `alpha := beta`? We say yes, because of `Note [Let-bound skolems]`. You ask whether the `a` needs to be bound at the same level (i.e. in the same implication) as the `(a ~ Int)`. I think it does. Consider {{{ data S a where MkS :: (a ~ Int) => S a g :: forall a. S a -> a -> blah g x y = let h = \z. ( z :: Int , case x of MkS -> [y,z]) in ... }}} When doing inference on `h` we'll assign `y :: alpha[1]`, say. Then from the body of the lambda we'll get {{{ alpha[1] ~ Int -- From z::Int forall[2]. (a ~ Int) => alpha[1] ~ a -- From [y,z] }}} Now, suppose we decide to float `alpha ~ a` out of the implication and then unify `alpha := a`. Now we are stuck! But if treat `alpha ~ Int` first, and unify `alpha := Int`, all is fine. But we absolutely cannot float that equality or we will get stuck. Does that help explain? I could add this to the Note. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:11 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): Replying to [comment:11 simonpj]:
I think you are referring to `Note [Let-bound skolems]` in `TcSMonad`?
Yep, that's the most relevant Note.
...
Snipped. (Should it have been "When doing inference on {{{h}}} we'll assign {{{z :: alpha[1]}}}"? Since {{{y}}} is already "declared" {{{:: a}}} from the {{{g}}} signature.)
Does that help explain? I could add this to the Note.
Yes, that explanation does help. I'm ruminating now about how it compares to the main jfp-OutsideIn narrative eg so I can better suggest how to update the Note. Beyond the Note, though, my comment above was asking how feasible it is for the following example to work. My intuition (which I'm working to unpack) thinks it should. But 1) am I wrong? and 2) how much work to have GHC accept it? {{{ data S a where MkS :: (a ~ Int) => S a -- from your 2nd example data Query f b = MkQuery (forall q. f q -> q -> b) -- a new ingredient g3 :: Query S Int -- GHC 8.6 cannot infer this type. Should a later GHC? g3 = MkQuery (\MkS x -> x) }}} I'm trying to decode my intuition for what exactly is it about {{{g3}}} (contrasted with your {{{g}}}) that should justify unifying{{{beta := Int}}} in this case. For example, if we define {{{QueryS}}} as a manual specialization of {{{Query}}} with {{{f = S}}}, then your previous commit for this ticket works for {{{g2}}} below. {{{ data QueryS b where MkQueryS :: (a ~ Int) => (a -> b) -> QueryS b to :: Query S b -> QueryS b to (MkQuery f) = MkQueryS (f MkS) from :: QueryS b -> Query S b from (MkQueryS f) = MkQuery (\MkS -> f) g2 = MkQueryS (\x -> x) -- GHC 8.6 infers QueryS Int }}} This isomorphism and inference for {{{g2}}} reassures me that my hope for {{{g3}}} is at least within the realm of possibility. In {{{g3}}} there is nothing between the implicit type lambda binding {{{q}}} and the pattern match on {{{MkS}}}. So I think we end up with nested implications with nothing in between. (I'm very doubtful that I have the level numbers correct here.) {{{ forall[2] a. -- from the type lambda forall[3] . (a ~ Int) => -- from the MkS pattern (a ~ alpha[2],a ~ beta[1]) }}} Perhaps my optimistic intuition for {{{g3}}} is because the outer implication here is so trivial: {{{alpha}}} and {{{beta}}} do not occur in (any other equality-like constraints in) any siblings of our inner implication, so floating {{{alpha ~ beta}}} wouldn't have any consequences. Is that suggestive of some refinement to eg {{{getNoGivenEqs}}} or am I lost in the weeds? Thank you for your patience. -Nick -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:12 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Hmm. I think you may be suggesting this transformation on constraints: {{{ forall tvs1. blah1 => forall tvs2. blah2 => unsolved-stuff --> forall (tvs1, tvs2). (blah1, blah2) => unsolved-stuff }}} The hope would be that an equality in the inner `blah2` might look like `(a~ty)`, where `a` is bound by the outer `tvs1`. If so, then in the transformed implication, the equality would fall under `Note [Let-bound skolems]`, and we could float things from `unsolved-stuff` out. I think that is plausible, and it's an interesting idea that I had not onsidered.. The side condition is that there are no "wanteds" as a peer to the inner implication, like this {{{ forall tvs1. blah1 => ( forall tvs2. blah2 => unsolved-stuff , alpha ~ Int ) }}} If there was, the argument of comment:11 would apply. But what if there were ''two'' such implications? Floating constraints out of either would disable floating constraints out of the other! This looks complicated and unprincipled to me. I suggest just using a type signature. Extremely complicated type inference is not necessarily a boon to the programmer! Perhaps there is a simple, principled algorithm hiding in there; but I don't yet see it. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:13 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): Replying to [comment:5 simonpj]:
I took a look. Turns out that it is what I guessed:
I don't say that it's perfectly implemented (eg I'm a bit worried about whether it's sensitive to whether the constraint looks like a ~ b or b ~ a)
We end up with {{{ Implic { TcLevel = 2 Skolems = a_a2do[sk:2] p_a2dt[sk:2] No-eqs = True Status = Unsolved Given = Wanted = WC {wc_impl = Implic { TcLevel = 3 Skolems = b_a2dp[ssk:3] No-eqs = True Status = Unsolved Given = $d~_a2dq :: (b_a2dp[ssk:3] :: *) ~ (a_a2do[tau:2] :: *) Wanted = WC {wc_simple = [WD] hole{co_a2dE} {1}:: (p_a2dt[sk:2] :: *) GHC.Prim.~# (Bool :: *) (CNonCanonical) [WD] hole{co_a2dR} {1}:: (a_a2do[sk:2] :: *) GHC.Prim.~# (Bool :: *) (CNonCanonical)} a pattern with constructor: MkT1 :: forall b a. ((b :: *) ~ (a :: *)) => b -> T1 a }}}
How is it that {{{a_a2do}}} occurs both as {{{a_a2do[sk:2]}}} and also as {{{a_a2do[tau:2]}}} in this implication tree? I'm still working on a response to your suggested edit of the Note, but while I was rereading your comments I noticed the above. I'm surprised to see the same type variable (by unique at least) occurring with two different forms of `Details`. Seems like I'm missing some relevant concept. The Notes in TcType didn't seem relevant or didn't discuss it. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:14 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): For future interested parties (e.g. me :/), I suggest reading these Notes in this order. (These links are into the tip of the {{{ghc-8.6}}} branch as of this moment.) 1. {{{Note [TcLevel and untouchable type variables]}}}, {{{Note [WantedInv]}}}, {{{Note [Skolem escape prevention]}}}, and {{{Note [TcLevel assignment]}}} in the {{{Untoucable type variables}}} [sic] section starting at [https://github.com/ghc/ghc/blob/8bed140099f8ab78e3e728fd2e50dd73d7210e84/com... TcType.hs line 679] 1. {{{Note [Float Equalities out of Implications]}}}, {{{Note [Float equalities from under a skolem binding]}}}, {{{Note [Which equalities to float]}}}, {{{Note [Skolem escape]}}}, and {{{Note [What prevents a constraint from floating]}}} within the {{{Floating equalities}}} section starting at [https://github.com/ghc/ghc/blob/8bed140099f8ab78e3e728fd2e50dd73d7210e84/com... TcSimplify.hsline 2132] 1. {{{Note [When does an implication have given equalities?]}}} and {{{Note [Let-bound skolems]}}} starting at [https://github.com/ghc/ghc/blob/8bed140099f8ab78e3e728fd2e50dd73d7210e84/com... TcSMonad.hs line 2142] -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:15 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): Replying to [comment:11 simonpj]: I've spent the evening reading all the examples I could find (see previous comment) and drafting this comment. I think the example you gave in #[comment:11] does address my precise question in #[comment:10] about {{{getNoGivenEqs}}} in a way that no other existing example does. So it would probably be useful in the Note. However, I can also now ask a more exact question! I pose two questions below. With GHC 8.6.0.20180810, the following does not typecheck, because {{{z}}} is untouchable under the pattern matches. {{{ data TFun z = MkTFun (forall y. T y -> z) data T a where T1 :: T Bool T2 :: T Char example () = MkTFun (\x -> case x of { T1 -> True; T2 -> False }) }}} The constraint solver gets stuck in this state in the {{{-ddump-tc- trace}}} (I grabbed this from the middle because the taus become sks after a certain point and I didn't intend for that to be relevant). {{{ solveNestedImplications starting { solveImplication { Implic { TcLevel = 2 Skolems = y_aWl[sk:2] No-eqs = True Status = Unsolved Given = Wanted = WC {wc_impl = Implic { TcLevel = 3 Skolems = No-eqs = False Status = Unsolved Given = co_aWq :: y_aWl[sk:2] GHC.Prim.~# Bool Wanted = WC {wc_simple = [WD] hole{co_aWB} {1}:: z_aWj[tau:1] GHC.Prim.~# Bool (CNonCanonical)} Binds = EvBindsVar<aWs> a pattern with constructor: T1 :: T Bool, in a case alternative } Implic { TcLevel = 3 Skolems = No-eqs = False Status = Unsolved Given = co_aWt :: y_aWl[sk:2] GHC.Prim.~# Char Wanted = WC {wc_simple = [WD] hole{co_aWC} {1}:: z_aWj[tau:1] GHC.Prim.~# Bool (CNonCanonical)} Binds = EvBindsVar<aWv> a pattern with constructor: T2 :: T Char, in a case alternative }} Binds = EvBindsVar<aWw> a type expected by the context: forall y. T y -> z_aWj[tau:1] } Inerts {Unsolved goals = 0} Inert fsks = [] }}} {{{co_aWB}}} and {{{co_aWC}}} do not float; their parent implication has a given equality (where {{{skolem_bound_here}}} is {{{False}}}), so the {{{floatEqualities}}} function gives up immediately. Thus, my first question is: Why wouldn't we want GHC to float {{{z[tau:1] ~ Bool}}} past {{{y[sk:2] ~ Bool}}} here? The rest of this comment explains why I don't think the existing examples answer that question. This simple rule that causes {{{floatEqualities}}} to currently give up here is motivated by examples found in {{{Note [Float Equalities out of Implications]}}} and also in Section 5.1 and Example 5.1 of jfp- outsidein.pdf (print pages 35 and 37). However, in all of those examples the given and the wanted equalities involve metavars with the same level. In this comment's example, the levels and {{{TcTyVarDetails}}} flavors are different and that seems relevant to me. In particular, because of the levels, {{{z := y}}} is not a valid assignment -- but such an assignment (which is valid when {{{level(y) <= level(z)}}}) motivates the conservative rule in both {{{Note [Float Equalities out of Implications]}}} and also Section 5.1 of the paper. Relatedly, Example 5.1 from the paper points out that an assignment akin to {{{z := F y}}} might become a solution; that assignment is also invalid if {{{level(y) > level(z)}}}. Your example from #[comment:11] ... {{{ data S a where MkS :: S Int g :: forall a. S a -> a -> () g x y = let h = \z -> ( z :: Int , case x of MkS -> [y,z]) in h (3 :: Int) `seq` () }}} ... gets stuck here in the {{{-ddump-tc-trace}}} ... {{{ setImplicationStatus(not-all-solved) } Implic { TcLevel = 1 Skolems = a_a1ln[sk:1] No-eqs = True Status = Unsolved Given = Wanted = WC {wc_impl = Implic { TcLevel = 2 Skolems = No-eqs = False Status = Unsolved Given = co_a1lH :: a_a1ln[sk:1] GHC.Prim.~# Int Wanted = WC {wc_simple = [WD] hole{co_a1lY} {2}:: b_a1ly[tau:1] GHC.Prim.~# [Int] (CNonCanonical)} Binds = EvBindsVar<a1lL> a pattern with constructor: MkS :: S Int, in a case alternative }} Binds = EvBindsVar<a1lV> the type signature for: g :: forall a. S a -> a -> () } Constraint solver steps = 3 unflattenGivens [] End simplifyTop } reportUnsolved { }}} ... as it should! The given and the wanted both have type variables of the same level, so {{{b := a}}} is a valid assignment. The example from #[comment:5] that directly motivated this exact ticket is a little more involved. In that case, the given equality is between variables of level 2 and 3, while the wanted equality is for a variable of level 2. To some degree, the {{{level(y) > level(z)}}} condition does hold here. It's certainly more subtle than my example, but maybe the let-bound skolem rule could be subsumed by my hypothetical "float equalities past equalities if the involved levels are OK" rule. My second question is: Does such a "float equalities past equalities if the involved levels are OK" rule seem feasible/practical? Has that been investigated at all? I'm particularly worried I'm overlooking some pitfall due to the open world assumption, though skolems seem relatively localized. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:16 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj):
How is it that a_a2do occurs both as a_a2do[sk:2] and also as a_a2do[tau:2] in this implication tree?
I strongly suspect that `a_a2do` started life as a unification variable `[tau:2]`, but we ended up generalising over it, so we skolemised it to `a_a2do`. That is we unify `a_a2do := a_a2do[sk]`. I believe that the implication tree is being displayed here without zonking; that is, without taking account of unifications. I'd be shocked if we had both, but ''without'' having `a_a2do[tau]` being unified with `a_a2do[sk]`. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:17 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): What, precisely, is the rule you propose? I don't know precisely what "float equalities past equalities if the involved levels are OK" means. I think you mean something like "you can float a wanted equality W past a given equality G iff that G not to be used in any solution of W. But it's hard to come up with an implementation of that rule, other than "don't float past equalities". Whatever we choose must be simple and predictable. I worry about things like this {{{ Implic { TcLevel = 3 , Skolems = [] , Given = (y ~ Bool, F y Int ~ Char) , Wanted = z[tau:2] ~ F Bool t[tau:1] } }}} Can I float that wanted? Well, no. Maybe, later, we'll discover that `t := Int`. Then our `F Bool Int` can be rewritten to `F y Int` (with the local equality), and thence to Char. But if we floated it out we'd get stuck. If you have a better rule, that'd be great. I just don't see it yet. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:18 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): I think I'm slowly making progress! I'm doing it at wiki:FloatMoreEqs2018, to avoid polluting this ticket any more. I might open a separate feature request at some point. In particular, I work through an example there {{{ forall[3] y. () => ( (u : <U>) , forall[4]. (g : y ~ <Y>) => (w1 : <W1>) ) }}} showing why floating {{{w1}}} out from under {{{g}}} can be problematic. I think this answers my question from comment:10 more precisely than does comment:11. That example motivates a rule, which I'm now working to broaden and concretize:
We can float {{{w1}}} out from under {{{g}}} if {{{y}}} does not and can never again occur in {{{<W1>}}}.
The destination I have in mind is something like "retiring" {{{g}}} (e.g. discarding it) if we can determine that it has eliminated all possible occurrences of {{{y}}} under it now and forever. I think some relatively simple heuristics are within reach -- will keep working on it. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:19 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): I've worked through some interesting examples and distilled them into a first draft rule. CTRL+F for "Rule 20181104" on wiki:FloatMoreEqs2018. The page has dated entries, but today's should supersede all the older ones. I'm traveling on vacation for a couple weeks, so that will certainly derail me for a while. Today's entry was an effort to have something useful to come back to, but it has a couple TODOs. I think the first (Example 5) is a matter of finding the right presentation, while the second (Does RHS of {{{w}}} matter?) is something I just haven't thought much about yet. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:20 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15009: Float equalities past local equalities -------------------------------------+------------------------------------- Reporter: goldfire | Owner: (none) Type: feature request | Status: closed Priority: normal | Milestone: 8.4.3 Component: Compiler | Version: 8.2.2 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: gadt/T15009 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nfrisby): If I correctly understand what the "inert substitution" is, then I think I can summarize the intent of my draft rule as follows.
Let dis[i] be the domain of the inert substitution active in the wanteds of TcLevel i. We can safely float a wanted CTyEqCan {{{w : alpha[L] ~ <t>}}} from level K to level L if dis[K] can never change (which implies dis[L] can never change -- ie no CTyEqCan might flip to reveal a new LHS) and if dis[K] - dis[L] does not include any metavars, any flattening skolems, or any skolems of level <= L.
-- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15009#comment:21 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC