Fix constraint handling for lazy patterns
authorsimonpj@microsoft.com <unknown>
Fri, 24 Nov 2006 23:05:48 +0000 (23:05 +0000)
committersimonpj@microsoft.com <unknown>
Fri, 24 Nov 2006 23:05:48 +0000 (23:05 +0000)
Lazy patterns are quite tricky!  Consider
f ~(C x) = 3
Can the Num constraint from the 3 be discharged by a Num dictionary
bound by the pattern?  Definitely not!

See Note [Hopping the LIE in lazy patterns] in TcPat

The type checker wasn't ensuring this, and that was causing all
manner of strange things to happen.  It actually manifested as a
strictness bug reported by Sven Panne.

I've added his test case as tcrun040.

compiler/hsSyn/HsUtils.lhs
compiler/typecheck/TcPat.lhs

index 5d57cf4..3302820 100644 (file)
@@ -380,15 +380,12 @@ collectl (L l pat) bndrs
     go (TuplePat pats _ _)       = foldr collectl bndrs pats
                                  
     go (ConPatIn c ps)           = foldr collectl bndrs (hsConArgs ps)
-    go (ConPatOut { pat_dicts = ds, 
-                   pat_binds = bs, pat_args = ps })
-                                 = map noLoc ds
-                                   ++ collectHsBindLocatedBinders bs
-                                   ++ foldr collectl bndrs (hsConArgs ps)
+    go (ConPatOut {pat_args=ps})  = foldr collectl bndrs (hsConArgs ps)
+       -- See Note [Dictionary binders in ConPatOut]
     go (LitPat _)                = bndrs
     go (NPat _ _ _ _)            = bndrs
     go (NPlusKPat n _ _ _)        = n : bndrs
-
+                                 
     go (SigPatIn pat _)                  = collectl pat bndrs
     go (SigPatOut pat _)         = collectl pat bndrs
     go (TypePat ty)               = bndrs
@@ -397,6 +394,16 @@ collectl (L l pat) bndrs
     go (CoPat _ pat ty)           = collectl (noLoc pat) bndrs
 \end{code}
 
+Note [Dictionary binders in ConPatOut]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Do *not* gather (a) dictionary and (b) dictionary bindings as binders
+of a ConPatOut pattern.  For most calls it doesn't matter, because
+it's pre-typechecker and there are no ConPatOuts.  But it does matter
+more in the desugarer; for example, DsUtils.mkSelectorBinds uses
+collectPatBinders.  In a lazy pattern, for example f ~(C x y) = ...,
+we want to generate bindings for x,y but not for dictionaries bound by
+C.  (The type checker ensures they would not be used.)
+
 \begin{code}
 collectSigTysFromPats :: [InPat name] -> [LHsType name]
 collectSigTysFromPats pats = foldr collect_lpat [] pats
index a5d4209..f945292 100644 (file)
@@ -332,11 +332,26 @@ tc_pat pstate (BangPat pat) pat_ty thing_inside
 --
 -- Nor should a lazy pattern bind any existential type variables
 -- because they won't be in scope when we do the desugaring
+--
+-- Note [Hopping the LIE in lazy patterns]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- In a lazy pattern, we must *not* discharge constraints from the RHS
+-- from dictionaries bound in the pattern.  E.g.
+--     f ~(C x) = 3
+-- We can't discharge the Num constraint from dictionaries bound by
+-- the pattern C!  
+--
+-- So we have to make the constraints from thing_inside "hop around" 
+-- the pattern.  Hence the getLLE and extendLIEs later.
+
 tc_pat pstate lpat@(LazyPat pat) pat_ty thing_inside
-  = do { (pat', pat_tvs, res) <- tc_lpat pat pat_ty pstate $ \ _ ->
-                                 thing_inside pstate
-                                       -- Ignore refined pstate',
-                                       -- revert to pstate
+  = do { (pat', pat_tvs, (res,lie)) 
+               <- tc_lpat pat pat_ty pstate $ \ _ ->
+                  getLIE (thing_inside pstate)
+               -- Ignore refined pstate', revert to pstate
+       ; extendLIEs lie
+       -- getLIE/extendLIEs: see Note [Hopping the LIE in lazy patterns]
+
        -- Check no existentials
        ; if (null pat_tvs) then return ()
          else lazyPatErr lpat pat_tvs