[GHC] #13674: GHC doesn't discharge heterogenous equality constraint when it ought to

#13674: GHC doesn't discharge heterogenous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.0.1 (Type checker) | Keywords: TypeInType | Operating System: Unknown/Multiple Architecture: | Type of failure: GHC rejects Unknown/Multiple | valid program Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- Here's some code, reduced from an example in https://github.com/ekmett/constraints/issues/55: {{{#!hs {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} import Data.Proxy import GHC.Exts (Constraint) import GHC.TypeLits import Unsafe.Coerce (unsafeCoerce) data Dict :: Constraint -> * where Dict :: a => Dict a infixr 9 :- newtype a :- b = Sub (a => Dict b) -- | Given that @a :- b@, derive something that needs a context @b@, using the context @a@ (\\) :: a => (b => r) -> (a :- b) -> r r \\ Sub Dict = r newtype Magic n = Magic (KnownNat n => Dict (KnownNat n)) magic :: forall n m o. (Integer -> Integer -> Integer) -> (KnownNat n, KnownNat m) :- KnownNat o magic f = Sub $ unsafeCoerce (Magic Dict) (natVal (Proxy :: Proxy n) `f` natVal (Proxy :: Proxy m)) type family Lcm :: Nat -> Nat -> Nat where axiom :: forall a b. Dict (a ~ b) axiom = unsafeCoerce (Dict :: Dict (a ~ a)) lcmNat :: forall n m. (KnownNat n, KnownNat m) :- KnownNat (Lcm n m) lcmNat = magic lcm lcmIsIdempotent :: forall n. Dict (n ~ Lcm n n) lcmIsIdempotent = axiom newtype GF (n :: Nat) = GF Integer x :: GF 5 x = GF 3 y :: GF 5 y = GF 4 foo :: (KnownNat m, KnownNat n) => GF m -> GF n -> GF (Lcm m n) foo m@(GF x) n@(GF y) = GF $ (x*y) `mod` (lcm (natVal m) (natVal n)) bar :: (KnownNat m) => GF m -> GF m -> GF m bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) }}} Compiling this (with either GHC 8.0.1, 8.0.2, 8.2.1, or HEAD) gives you a downright puzzling type error: {{{ $ /opt/ghc/head/bin/ghci Bug.hs GHCi, version 8.3.20170509: http://www.haskell.org/ghc/ :? for help Loaded GHCi configuration from /home/rgscott/.ghci [1 of 1] Compiling Main ( Bug.hs, interpreted ) Bug.hs:54:21: error: • Couldn't match type ‘m’ with ‘Lcm m m’ ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:53:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(-)’, namely ‘foo x y’ In the expression: foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) In an equation for ‘bar’: bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) • Relevant bindings include y :: GF m (bound at Bug.hs:54:17) x :: GF m (bound at Bug.hs:54:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) | 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | ^^^^^^^ Bug.hs:54:31: error: • Could not deduce: m ~ Lcm m m from the context: m ~ Lcm m m bound by a type expected by the context: m ~ Lcm m m => GF m at Bug.hs:54:31-85 ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:53:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(\\)’, namely ‘foo y x’ In the first argument of ‘(\\)’, namely ‘foo y x \\ lcmNat @m @m’ In the second argument of ‘(-)’, namely ‘foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m)’ • Relevant bindings include y :: GF m (bound at Bug.hs:54:17) x :: GF m (bound at Bug.hs:54:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) | 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | ^^^^^^^ }}} In particular, I'd like to emphasize this part: {{{ • Could not deduce: m ~ Lcm m m from the context: m ~ Lcm m m }}} Wat!? Surely, GHC can deduce `m ~ Lcm m m` from `m ~ Lcm m m`? I decided to flip on `-fprint-explicit-kinds` and see if there was some other issue lurking beneath the surface: {{{ $ /opt/ghc/8.2.1/bin/ghci Bug.hs -fprint-explicit-kinds GHCi, version 8.2.0.20170505: http://www.haskell.org/ghc/ :? for help Loaded GHCi configuration from /home/rgscott/.ghci [1 of 1] Compiling Main ( Bug.hs, interpreted ) Bug.hs:54:21: error: • Couldn't match type ‘m’ with ‘Lcm m m’ ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:53:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(-)’, namely ‘foo x y’ In the expression: foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) In an equation for ‘bar’: bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) • Relevant bindings include y :: GF m (bound at Bug.hs:54:17) x :: GF m (bound at Bug.hs:54:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) | 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | ^^^^^^^ Bug.hs:54:31: error: • Could not deduce: (m :: Nat) ~~ (Lcm m m :: Nat) from the context: m ~ Lcm m m bound by a type expected by the context: m ~ Lcm m m => GF m at Bug.hs:54:31-85 ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:53:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(\\)’, namely ‘foo y x’ In the first argument of ‘(\\)’, namely ‘foo y x \\ lcmNat @m @m’ In the second argument of ‘(-)’, namely ‘foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m)’ • Relevant bindings include y :: GF m (bound at Bug.hs:54:17) x :: GF m (bound at Bug.hs:54:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) | 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | ^^^^^^^ }}} Well, not a whole lot changed. We now have this, slightly more specific error instead: {{{ • Could not deduce: (m :: Nat) ~~ (Lcm m m :: Nat) from the context: m ~ Lcm m m }}} Huh, this is flummoxing. Surely `(m :: Nat) ~~ (Lcm m m :: Nat)` ought to be the same thing as `m ~ Lcm m m`, right? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by oerjan): For what it's worth, it still doesn't help (in GHC 8.0.1) if I change all the `~`s to `~~`s. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by oerjan): * cc: oerjan (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Description changed by RyanGlScott: @@ -51,0 +51,9 @@ + instance KnownNat n => Num (GF n) where + xf@(GF x) + GF y = GF $ (x+y) `mod` (natVal xf) + xf@(GF x) - GF y = GF $ (x-y) `mod` (natVal xf) + xf@(GF x) * GF y = GF $ (x*y) `mod` (natVal xf) + abs = id + signum xf@(GF x) | x==0 = xf + | otherwise = GF 1 + fromInteger = GF + @@ -74,1 +83,1 @@ - Bug.hs:54:21: error: + Bug.hs:63:21: error: @@ -79,1 +88,1 @@ - at Bug.hs:53:1-44 + at Bug.hs:62:1-44 @@ -90,5 +99,5 @@ - y :: GF m (bound at Bug.hs:54:17) - x :: GF m (bound at Bug.hs:54:6) - bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) - | - 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() + y :: GF m (bound at Bug.hs:63:17) + x :: GF m (bound at Bug.hs:63:6) + bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) + | + 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() @@ -98,1 +107,1 @@ - Bug.hs:54:31: error: + Bug.hs:63:31: error: @@ -103,5 +112,5 @@ - at Bug.hs:54:31-85 - ‘m’ is a rigid type variable bound by - the type signature for: - bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m - at Bug.hs:53:1-44 + at Bug.hs:63:31-85 + ‘m’ is a rigid type variable bound by + the type signature for: + bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m + at Bug.hs:62:1-44 @@ -115,5 +124,5 @@ - y :: GF m (bound at Bug.hs:54:17) - x :: GF m (bound at Bug.hs:54:6) - bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) - | - 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() + y :: GF m (bound at Bug.hs:63:17) + x :: GF m (bound at Bug.hs:63:6) + bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) + | + 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() @@ -136,2 +145,2 @@ - $ /opt/ghc/8.2.1/bin/ghci Bug.hs -fprint-explicit-kinds - GHCi, version 8.2.0.20170505: http://www.haskell.org/ghc/ :? for help + $ /opt/ghc/head/bin/ghci Bug.hs -fprint-explicit-kinds + GHCi, version 8.3.20170509: http://www.haskell.org/ghc/ :? for help @@ -141,1 +150,1 @@ - Bug.hs:54:21: error: + Bug.hs:63:21: error: @@ -146,1 +155,1 @@ - at Bug.hs:53:1-44 + at Bug.hs:62:1-44 @@ -157,5 +166,5 @@ - y :: GF m (bound at Bug.hs:54:17) - x :: GF m (bound at Bug.hs:54:6) - bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) - | - 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() + y :: GF m (bound at Bug.hs:63:17) + x :: GF m (bound at Bug.hs:63:6) + bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) + | + 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() @@ -165,1 +174,1 @@ - Bug.hs:54:31: error: + Bug.hs:63:31: error: @@ -170,5 +179,5 @@ - at Bug.hs:54:31-85 - ‘m’ is a rigid type variable bound by - the type signature for: - bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m - at Bug.hs:53:1-44 + at Bug.hs:63:31-85 + ‘m’ is a rigid type variable bound by + the type signature for: + bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m + at Bug.hs:62:1-44 @@ -182,7 +191,7 @@ - y :: GF m (bound at Bug.hs:54:17) - x :: GF m (bound at Bug.hs:54:6) - bar :: GF m -> GF m -> GF m (bound at Bug.hs:54:1) - | - 54 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() - (lcmIsIdempotent @m) - | ^^^^^^^ + y :: GF m (bound at Bug.hs:63:17) + x :: GF m (bound at Bug.hs:63:6) + bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) + | + 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() + (lcmIsIdempotent @m) + | New description: Here's some code, reduced from an example in https://github.com/ekmett/constraints/issues/55: {{{#!hs {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} import Data.Proxy import GHC.Exts (Constraint) import GHC.TypeLits import Unsafe.Coerce (unsafeCoerce) data Dict :: Constraint -> * where Dict :: a => Dict a infixr 9 :- newtype a :- b = Sub (a => Dict b) -- | Given that @a :- b@, derive something that needs a context @b@, using the context @a@ (\\) :: a => (b => r) -> (a :- b) -> r r \\ Sub Dict = r newtype Magic n = Magic (KnownNat n => Dict (KnownNat n)) magic :: forall n m o. (Integer -> Integer -> Integer) -> (KnownNat n, KnownNat m) :- KnownNat o magic f = Sub $ unsafeCoerce (Magic Dict) (natVal (Proxy :: Proxy n) `f` natVal (Proxy :: Proxy m)) type family Lcm :: Nat -> Nat -> Nat where axiom :: forall a b. Dict (a ~ b) axiom = unsafeCoerce (Dict :: Dict (a ~ a)) lcmNat :: forall n m. (KnownNat n, KnownNat m) :- KnownNat (Lcm n m) lcmNat = magic lcm lcmIsIdempotent :: forall n. Dict (n ~ Lcm n n) lcmIsIdempotent = axiom newtype GF (n :: Nat) = GF Integer instance KnownNat n => Num (GF n) where xf@(GF x) + GF y = GF $ (x+y) `mod` (natVal xf) xf@(GF x) - GF y = GF $ (x-y) `mod` (natVal xf) xf@(GF x) * GF y = GF $ (x*y) `mod` (natVal xf) abs = id signum xf@(GF x) | x==0 = xf | otherwise = GF 1 fromInteger = GF x :: GF 5 x = GF 3 y :: GF 5 y = GF 4 foo :: (KnownNat m, KnownNat n) => GF m -> GF n -> GF (Lcm m n) foo m@(GF x) n@(GF y) = GF $ (x*y) `mod` (lcm (natVal m) (natVal n)) bar :: (KnownNat m) => GF m -> GF m -> GF m bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) }}} Compiling this (with either GHC 8.0.1, 8.0.2, 8.2.1, or HEAD) gives you a downright puzzling type error: {{{ $ /opt/ghc/head/bin/ghci Bug.hs GHCi, version 8.3.20170509: http://www.haskell.org/ghc/ :? for help Loaded GHCi configuration from /home/rgscott/.ghci [1 of 1] Compiling Main ( Bug.hs, interpreted ) Bug.hs:63:21: error: • Couldn't match type ‘m’ with ‘Lcm m m’ ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:62:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(-)’, namely ‘foo x y’ In the expression: foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) In an equation for ‘bar’: bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) • Relevant bindings include y :: GF m (bound at Bug.hs:63:17) x :: GF m (bound at Bug.hs:63:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) | 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | ^^^^^^^ Bug.hs:63:31: error: • Could not deduce: m ~ Lcm m m from the context: m ~ Lcm m m bound by a type expected by the context: m ~ Lcm m m => GF m at Bug.hs:63:31-85 ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:62:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(\\)’, namely ‘foo y x’ In the first argument of ‘(\\)’, namely ‘foo y x \\ lcmNat @m @m’ In the second argument of ‘(-)’, namely ‘foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m)’ • Relevant bindings include y :: GF m (bound at Bug.hs:63:17) x :: GF m (bound at Bug.hs:63:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) | 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | ^^^^^^^ }}} In particular, I'd like to emphasize this part: {{{ • Could not deduce: m ~ Lcm m m from the context: m ~ Lcm m m }}} Wat!? Surely, GHC can deduce `m ~ Lcm m m` from `m ~ Lcm m m`? I decided to flip on `-fprint-explicit-kinds` and see if there was some other issue lurking beneath the surface: {{{ $ /opt/ghc/head/bin/ghci Bug.hs -fprint-explicit-kinds GHCi, version 8.3.20170509: http://www.haskell.org/ghc/ :? for help Loaded GHCi configuration from /home/rgscott/.ghci [1 of 1] Compiling Main ( Bug.hs, interpreted ) Bug.hs:63:21: error: • Couldn't match type ‘m’ with ‘Lcm m m’ ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:62:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(-)’, namely ‘foo x y’ In the expression: foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) In an equation for ‘bar’: bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) • Relevant bindings include y :: GF m (bound at Bug.hs:63:17) x :: GF m (bound at Bug.hs:63:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) | 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | ^^^^^^^ Bug.hs:63:31: error: • Could not deduce: (m :: Nat) ~~ (Lcm m m :: Nat) from the context: m ~ Lcm m m bound by a type expected by the context: m ~ Lcm m m => GF m at Bug.hs:63:31-85 ‘m’ is a rigid type variable bound by the type signature for: bar :: forall (m :: Nat). KnownNat m => GF m -> GF m -> GF m at Bug.hs:62:1-44 Expected type: GF m Actual type: GF (Lcm m m) • In the first argument of ‘(\\)’, namely ‘foo y x’ In the first argument of ‘(\\)’, namely ‘foo y x \\ lcmNat @m @m’ In the second argument of ‘(-)’, namely ‘foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m)’ • Relevant bindings include y :: GF m (bound at Bug.hs:63:17) x :: GF m (bound at Bug.hs:63:6) bar :: GF m -> GF m -> GF m (bound at Bug.hs:63:1) | 63 | bar (x :: GF m) y = foo x y - foo y x \\ lcmNat @m @m \\ Sub @() (lcmIsIdempotent @m) | }}} Well, not a whole lot changed. We now have this, slightly more specific error instead: {{{ • Could not deduce: (m :: Nat) ~~ (Lcm m m :: Nat) from the context: m ~ Lcm m m }}} Huh, this is flummoxing. Surely `(m :: Nat) ~~ (Lcm m m :: Nat)` ought to be the same thing as `m ~ Lcm m m`, right? -- -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): The original program is wrong, but the error message also is wrong. Because `Lcm` is a nullary type family, the constraint `m ~ Lcm m m` is morally equivalent to `m ~ Either m m`. This constraint is an occurs-check failure and is surely going to lead to an error. It seems that the error- reporting code is mischaracterizing it, though. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott):
This constraint is an occurs-check failure and is surely going to lead to an error.
Ah, that's a good point. I forgot that `Lcm m m` doesn't reduce as a type family (you're only ever to get out the `lcm` of `m` and `m` at the value level by using `knownNat` with the reflection-style trickery in `magic`, but this doesn't work at the type level). -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): Actually, I'm not sure if I find this occurs check explanation entirely satisfactory. SkorikGG points out [https://github.com/ekmett/constraints/issues/55#issuecomment-300767277 here] that you can make this program work with a slight tweak: {{{#!hs {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} import Data.Proxy import Data.Type.Equality import GHC.Exts (Constraint) import GHC.TypeLits import Unsafe.Coerce (unsafeCoerce) data Dict :: Constraint -> * where Dict :: a => Dict a infixr 9 :- newtype a :- b = Sub (a => Dict b) -- | Given that @a :- b@, derive something that needs a context @b@, using the context @a@ (\\) :: a => (b => r) -> (a :- b) -> r r \\ Sub Dict = r newtype Magic n = Magic (KnownNat n => Dict (KnownNat n)) magic :: forall n m o. (Integer -> Integer -> Integer) -> (KnownNat n, KnownNat m) :- KnownNat o magic f = Sub $ unsafeCoerce (Magic Dict) (natVal (Proxy :: Proxy n) `f` natVal (Proxy :: Proxy m)) type family Lcm :: Nat -> Nat -> Nat where axiom :: forall a b. Dict (a ~ b) axiom = unsafeCoerce (Dict :: Dict (a ~ a)) lcmNat :: forall n m. (KnownNat n, KnownNat m) :- KnownNat (Lcm n m) lcmNat = magic lcm lcmIsIdempotent :: forall n. Dict (Lcm n n ~ n) lcmIsIdempotent = axiom newtype GF (n :: Nat) = GF Integer instance KnownNat n => Num (GF n) where xf@(GF x) + GF y = GF $ (x+y) `mod` (natVal xf) xf@(GF x) - GF y = GF $ (x-y) `mod` (natVal xf) xf@(GF x) * GF y = GF $ (x*y) `mod` (natVal xf) abs = id signum xf@(GF x) | x==0 = xf | otherwise = GF 1 fromInteger = GF x :: GF 5 x = GF 3 y :: GF 5 y = GF 4 foo :: (KnownNat m, KnownNat n) => GF m -> GF n -> GF (Lcm m n) foo m@(GF x) n@(GF y) = GF $ (x*y) `mod` (lcm (natVal m) (natVal n)) dictToRefl :: Dict (a ~ b) -> a :~: b dictToRefl Dict = Refl bar :: (KnownNat m) => GF m -> GF m -> GF m bar (x :: GF m) y = castWith (apply Refl $ dictToRefl (lcmIsIdempotent :: Dict (Lcm m m ~ m))) (foo x y - foo y x) \\ lcmNat @m @m z :: GF 5 z = bar x y }}} Notice that in `bar`, we're using a trick of converting the `lcmIsIdempotent` `Dict` to a `(:~:)` using `dictToRefl`. And it's clear that we are using the fact that `Lcm m m ~ m` in order for `bar` to typecheck, but this time, GHC accepts it! Should it? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by oerjan): I assume the difference, as usual in these kinds of tricks, is that in the workaround, GHC never gets directly asked to produce `Lcm m m ~ m` as a wanted constraint, and so never gets a chance to smell anything fishy. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: GHC doesn't discharge heterogeneous equality constraint when it ought to -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): comment:8 is essentially correct. Consider two scenarios: 1. We have a Given proving `blah` and a Wanted `blah`. We use the Given to solve the Wanted. Later, we learn that `blah` is `Lcm m m ~ m`, but nothing above changes. 2. We have a Given proving `blah`. We learn that `blah` is `Lcm m m ~ m`, so we mark it as insoluble. Then, we get a Wanted `blah`, which we see is `Lcm m m ~ m`. This, too, is marked as insoluble. In case 1, we'll succeed; in case 2, we'll fail. The problem is that the only difference in these cases is the order in which constraints are treated and/or solved, something notoriously difficult to control. The "fix" for this problem is not to error on occurs-checks, which would then allow us to succeed in case 2. That seems unsatisfactory, though, because even if occurs-checks don't immediately error, we'll have a hard time solving `m ~ Lcm m m` from `Lcm m m ~ m`. I'm inclined to say that we let this behavior stand. Note that when we accept the program, nothing goes wrong. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: Poor error message which masks occurs-check failure -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: Type of failure: GHC rejects | Unknown/Multiple valid program | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott):
I'm inclined to say that we let this behavior stand. Note that when we accept the program, nothing goes wrong.
OK, I can live with that. Sorry for derailing the discussion in the ticket a bit, but I wanted make sure I understood what was going on. Since the behavior here seems to be correct (modulo error messages), I'll change the title accordingly. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13674: Poor error message which masks occurs-check failure
-------------------------------------+-------------------------------------
Reporter: RyanGlScott | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler (Type | Version: 8.0.1
checker) |
Resolution: | Keywords: TypeInType
Operating System: Unknown/Multiple | Architecture:
Type of failure: GHC rejects | Unknown/Multiple
valid program | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Simon Peyton Jones

#13674: Poor error message which masks occurs-check failure
-------------------------------------+-------------------------------------
Reporter: RyanGlScott | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler (Type | Version: 8.0.1
checker) |
Resolution: | Keywords: TypeInType
Operating System: Unknown/Multiple | Architecture:
Type of failure: GHC rejects | Unknown/Multiple
valid program | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Simon Peyton Jones

#13674: Poor error message which masks occurs-check failure -------------------------------------+------------------------------------- Reporter: RyanGlScott | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: Component: Compiler (Type | Version: 8.0.1 checker) | Resolution: fixed | Keywords: TypeInType Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: GHC rejects | Test Case: valid program | indexed_types/should_fail/T13674 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by simonpj): * status: new => closed * testcase: => indexed_types/should_fail/T13674 * resolution: => fixed Comment: This was a very interesting ticket that showed up quite a deep bug. Thanks! I don't think this is worth merging. It's a very dark corner, the change is significant, and there's a chance that I've done something wrong (notwithstanding validate). -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13674#comment:13 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC