Commit 36d207aa authored by simonpj@microsoft.com's avatar simonpj@microsoft.com

Fix constraint handling for lazy patterns

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.
parent 9d0c0720
......@@ -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
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment