Repository : ssh://darcs.haskell.org//srv/darcs/ghc

On branch  : overlapping-tyfams

http://hackage.haskell.org/trac/ghc/changeset/67835fa4fe6e367a80adf62d717337818908bc94

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

commit 67835fa4fe6e367a80adf62d717337818908bc94
Author: Richard Eisenberg <e...@cis.upenn.edu>
Date:   Mon Dec 3 12:54:36 2012 -0500

    Removed confluent overlap check from branched type family instances.
    
    It was discovered that confluent overlap within groups implemented
    the "obvious" way doesn't support substitution. We (Simon PJ,
    Stephanie Weirich, Conor McBride, and I) were unable to come up with
    a simple solution to this problem. In any case, we'll wait until
    someone shouts loudly enough to make this worthwhile.

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

 compiler/types/FamInstEnv.lhs |   88 ++++++++++++++++++-----------------------
 1 files changed, 39 insertions(+), 49 deletions(-)

diff --git a/compiler/types/FamInstEnv.lhs b/compiler/types/FamInstEnv.lhs
index ea84816..5f91eef 100644
--- a/compiler/types/FamInstEnv.lhs
+++ b/compiler/types/FamInstEnv.lhs
@@ -589,39 +589,38 @@ no such other instance exists.
 
 Note [Confluence checking within groups]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Consider the following definition:
+GHC allows type family instances to have overlapping patterns as long as the
+right-hand sides are coincident in the region of overlap. Can we extend this
+notion of confluent overlap to branched instances? Not in any obvious way.
 
-type family And (a :: Bool) (b :: Bool) :: Bool
+Consider this:
 
 type instance where
-  And False a     = False
-  And True  b     = b
-  And c     False = False
-  And d     True  = d
-
-We wish to simplify (And e True). The search (using a one-way match
-from type patterns to target) quickly eliminates the first three
-possibilities. The fourth one matches, with subst [d |-> e]. We then
-go back and check the first three. We check each one to see if it
-can possibly unify with the target (And e True). The first one does,
-with subst [a |-> True, e |-> False]. To show that this is consistent
-with the fourth equation, we must apply both substitutions to the
-RHS of the fourth equation. Similarly, unifying with the second
-equation gives us a subst [b |-> True, e |-> True], also requiring
-the application of both substitutions to show consistency. The
-third equation does not unify, so we're done and can simplify
-(And e True) to e, as desired.
-
-Why don't we just unify the two equations in the group? Because, in
-general, we don't want to. Consider this:
-
-type family F a b
-type instance where
-  F a a = Int
-  F a b = b
-
-We should be able to simplify (F x Int) but not (F y Bool). We need
-the information from matching the target to differentiate these cases.
+  F Int = Int
+  F a = a
+
+Without confluence checking (in other words, as implemented), we cannot now
+simplify an application of (F b) -- b might unify with Int later on, so this
+application is stuck. However, it would seem easy to just check that, in the
+region of overlap, (i.e. b |-> Int), the right-hand sides coincide, so we're
+OK. The problem happens when we are simplifying an application (F (G a)),
+where (G a) is stuck. What, now, is the region of overlap? We can't soundly
+simplify (F (G a)) without knowing that the right-hand sides are confluent
+in the region of overlap, but we also can't in any obvious way identify the
+region of overlap. We don't want to do analysis on the instances of G, because
+that is not sound in a world with open type families. (If G were known to be
+closed, there might be a way forward here.) To find the region of overlap,
+it is conceivable that we might convert (G a) to some fresh type variable and
+then unify, but we must be careful to convert every (G a) to the same fresh
+type variable. And then, what if there is an (H a) lying around? It all seems
+rather subtle, error-prone, confusing, and probably won't help anyone. So,
+we're not doing it.
+
+So, why is this not a problem with non-branched confluent overlap? Because
+we don't need to verify that an application is apart from anything. The
+non-branched confluent overlap check happens when we add the instance to the
+environment -- we're unifying among patterns, which cannot contain type family
+appplications. So, we're safe there and can continue supporting that feature.
 
 \begin{code}
 -- when matching a type family application, we get a FamInst,
@@ -648,8 +647,8 @@ lookupFamInstEnv
 lookupFamInstEnv
   = lookup_fam_inst_env match True
   where
-    match seen branch@(FamInstBranch { fib_tvs = tpl_tvs,
-                                       fib_lhs = tpl_tys })
+    match seen (FamInstBranch { fib_tvs = tpl_tvs
+                              , fib_lhs = tpl_tys })
           _ match_tys 
       = ASSERT( tyVarsOfTypes match_tys `disjointVarSet` tpl_tvs )
                 -- Unification will break badly if the variables overlap
@@ -657,7 +656,7 @@ lookupFamInstEnv
         case tcMatchTys tpl_tvs tpl_tys match_tys of
           -- success
           Just subst
-            | checkConflict seen match_tys subst branch
+            | checkConflict seen match_tys
             -> (Nothing, StopSearching) -- we found an incoherence, so stop 
searching
             -- see Note [Early failure optimisation for instance groups]
 
@@ -670,23 +669,14 @@ lookupFamInstEnv
     -- see Note [Instance checking within groups]
     checkConflict :: [FamInstBranch] -- the previous branches in the instance 
that matched
                   -> [Type]          -- the types in the tyfam application we 
are matching
-                  -> TvSubst         -- the subst witnessing the match between 
those types and...
-                  -> FamInstBranch   -- ...this FamInstBranch
                   -> Bool            -- is there a conflict?
-    checkConflict [] _ _ _ = False
-    checkConflict ((FamInstBranch { fib_lhs = tpl_tys
-                                  , fib_rhs = inner_rhs }) : rest)
-               match_tys outer_subst
-               outer_branch@(FamInstBranch { fib_rhs = outer_rhs })
-      = let rest_apart = checkConflict rest match_tys outer_subst outer_branch 
in
-        case tcApartTys instanceBindFun tpl_tys match_tys of
-          NotApart inner_subst -> -- see Note [Confluence checking within 
groups]
-            let outer_rhs' = substTy inner_subst $
-                             substTy outer_subst outer_rhs
-                inner_rhs' = substTy inner_subst inner_rhs in
-            not (outer_rhs' `eqType` inner_rhs') || rest_apart
-          MaybeApart -> True -- there is a type family application involved; 
it might unify later
-          SurelyApart -> rest_apart
+    checkConflict [] _ = False
+    checkConflict ((FamInstBranch { fib_lhs = tpl_tys }) : rest) match_tys
+          -- see Note [Confluence checking within groups]
+      | SurelyApart <- tcApartTys instanceBindFun tpl_tys match_tys
+      = checkConflict rest match_tys
+      | otherwise
+      = True
 
 lookupFamInstEnvConflicts
     :: FamInstEnvs



_______________________________________________
Cvs-ghc mailing list
Cvs-ghc@haskell.org
http://www.haskell.org/mailman/listinfo/cvs-ghc

Reply via email to