[commit: ghc] wip/T11067: Wibbles (60e4a58)

git at git.haskell.org git at git.haskell.org
Fri Nov 27 17:45:24 UTC 2015


Repository : ssh://git@git.haskell.org/ghc

On branch  : wip/T11067
Link       : http://ghc.haskell.org/trac/ghc/changeset/60e4a5860773ed0030131141a98aa3d30a6a894f/ghc

>---------------------------------------------------------------

commit 60e4a5860773ed0030131141a98aa3d30a6a894f
Author: Simon Peyton Jones <simonpj at microsoft.com>
Date:   Fri Nov 27 17:45:43 2015 +0000

    Wibbles


>---------------------------------------------------------------

60e4a5860773ed0030131141a98aa3d30a6a894f
 compiler/typecheck/TcTyDecls.hs                    | 64 ++++++++--------------
 .../tests/indexed-types/should_compile/T10318.hs   | 35 ++++++++++++
 testsuite/tests/indexed-types/should_compile/all.T |  1 +
 3 files changed, 59 insertions(+), 41 deletions(-)

diff --git a/compiler/typecheck/TcTyDecls.hs b/compiler/typecheck/TcTyDecls.hs
index 3813d5f..080808d 100644
--- a/compiler/typecheck/TcTyDecls.hs
+++ b/compiler/typecheck/TcTyDecls.hs
@@ -165,48 +165,30 @@ exmaple, is fine
 even though C's full definition uses C.
 
 Making the check static also makes it conservative.  Eg
+  type family F a
+  class F a => C a
+Here an instance of (F a) might mention C:
+  type instance F [a] = C a
+and now we'd have a loop.
+
+The static check works like this, starting with C
+  * Look at C's superclass predicates
+  * If any is a type-function application, fail
+  * If any has C at the head, fail
+  * If any has a type class D at the head,
+    make the same test with D
+
+A tricky point is: what if there is a type variable at the head:
   class f a => C f a
-  class C a => D a
-Here if we produced a constraint (C D a), then its
-superclass would be (D a)
-
-
-We want definitions such as:
-
-  class C cls a where cls a => a -> a
-  class C D a => D a where
-
-to be accepted, even though a naive acyclicity check would reject the
-program as having a cycle between D and its superclass.  Why? Because
-when we instantiate
-     D ty1
-we get the superclas
-     C D ty1
-and C has no superclasses, so we have terminated with a finite set.
-
-More precisely, the rule is this: the superclasses sup_C of a class C
-are rejected iff:
-
-  C \elem expand(sup_C)
-
-Where expand is defined as follows:
-
-(1)  expand(a ty1 ... tyN) = expand(ty1) \union ... \union expand(tyN)
-
-(2)  expand(D ty1 ... tyN) = {D}
-                             \union sup_D[ty1/x1, ..., tyP/xP]
-                             \union expand(ty(P+1)) ... \union expand(tyN)
-           where (D x1 ... xM) is a class, P = min(M,N)
-
-(3)  expand(T ty1 ... tyN) = expand(ty1) \union ... \union expand(tyN)
-        where T is not a class
-
-Eqn (1) is conservative; when there's a type variable at the head,
-look in all the argument types.  Eqn (2) expands superclasses; the
-third component of the union is like Eqn (1).  Eqn (3) happens mainly
-when the context is a (constraint) tuple, such as (Eq a, Show a).
-
-Furthermore, expand always looks through type synonyms.
+Can this loop with some instantation of C? Well, no. You might try
+  C C a
+to instantiate 'f' with 'C', but that's ill kinded.  So what about
+  C (C ?) a
+To make it loop you'd need an infinte thing
+  C (C (C ... )) a
+
+So I don't think we can make a loop through a type variable in
+the head.
 -}
 
 checkClassCycles :: Class -> Maybe SDoc
diff --git a/testsuite/tests/indexed-types/should_compile/T10318.hs b/testsuite/tests/indexed-types/should_compile/T10318.hs
new file mode 100644
index 0000000..04a2ca1
--- /dev/null
+++ b/testsuite/tests/indexed-types/should_compile/T10318.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE FlexibleContexts, TypeSynonymInstances,
+             FlexibleInstances, TypeFamilies,
+             UndecidableSuperClasses #-}
+
+module T10318 where
+
+-- | Product of non-zero elements always non-zero.
+-- Every integral domain has a field of fractions.
+-- The field of fractions of any field is itself.
+class (Frac (Frac a) ~ Frac a, Fractional (Frac a), IntegralDomain (Frac a))
+  => IntegralDomain a where
+  type Frac a :: *
+  embed :: a -> Frac a
+
+instance IntegralDomain Integer where
+  type Frac Integer = Rational
+  embed = fromInteger
+
+instance IntegralDomain Rational where
+  type Frac Rational = Rational
+  embed = id
+
+g :: IntegralDomain a => a -> a
+g x = g x
+
+h :: a -> Frac a
+h x = h x
+
+-- This is the test function
+
+f :: IntegralDomain a => a -> Frac a
+f x = g (h (h x))
+  -- Given: IntegralDomain (Frac a)
+  -- Wanted: IntegralDomain (Frac (Frac a))
+
diff --git a/testsuite/tests/indexed-types/should_compile/all.T b/testsuite/tests/indexed-types/should_compile/all.T
index 39b8a3a..7eea00e 100644
--- a/testsuite/tests/indexed-types/should_compile/all.T
+++ b/testsuite/tests/indexed-types/should_compile/all.T
@@ -265,3 +265,4 @@ test('T10753', normal, compile, [''])
 test('T10806', normal, compile_fail, [''])
 test('T10815', normal, compile, [''])
 test('T10931', normal, compile, [''])
+test('T10318', normal, compile, [''])



More information about the ghc-commits mailing list