summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Peyton Jones <simonpj@microsoft.com>2017-06-28 12:38:59 +0100
committerSimon Peyton Jones <simonpj@microsoft.com>2017-06-28 12:38:59 +0100
commitfae672f647fe00c303d8fb56971563c1a76ad04e (patch)
tree5a1a8f38e6be315962ff9e6166a6314fd88f9da9
parentc80920d26f4eef8e87c130412d007628cff7589d (diff)
downloadhaskell-fae672f647fe00c303d8fb56971563c1a76ad04e.tar.gz
Fix constraint solving for forall-types
Trac #13879 showed that when we were trying to solve (forall z1 (y1::z1). ty1) ~ (forall z2 (y2:z2). ty2) we'd end up spitting out z1~z2 with no binding site for them. Those kind equalities need to be inside the implication. I ended up re-factoring the code for solving forall-equalities. It's quite nice now.
-rw-r--r--compiler/typecheck/TcCanonical.hs95
-rw-r--r--compiler/typecheck/TcSMonad.hs98
-rw-r--r--testsuite/tests/typecheck/should_compile/T13879.hs29
-rw-r--r--testsuite/tests/typecheck/should_compile/all.T1
-rw-r--r--testsuite/tests/typecheck/should_fail/tcfail174.stderr2
5 files changed, 152 insertions, 73 deletions
diff --git a/compiler/typecheck/TcCanonical.hs b/compiler/typecheck/TcCanonical.hs
index ff00d4220b..d373ee908e 100644
--- a/compiler/typecheck/TcCanonical.hs
+++ b/compiler/typecheck/TcCanonical.hs
@@ -23,6 +23,8 @@ import Coercion
import FamInstEnv ( FamInstEnvs )
import FamInst ( tcTopNormaliseNewTypeTF_maybe )
import Var
+import VarEnv( mkInScopeSet )
+import VarSet( extendVarSetList )
import Outputable
import DynFlags( DynFlags )
import NameSet
@@ -589,27 +591,7 @@ can_eq_nc' _flat _rdr_env _envs ev eq_rel ty1 _ ty2 _
can_eq_nc' _flat _rdr_env _envs ev eq_rel
s1@(ForAllTy {}) _ s2@(ForAllTy {}) _
- | CtWanted { ctev_loc = loc, ctev_dest = orig_dest } <- ev
- = do { let (bndrs1,body1) = tcSplitForAllTyVarBndrs s1
- (bndrs2,body2) = tcSplitForAllTyVarBndrs s2
- ; if not (equalLength bndrs1 bndrs2)
- then do { traceTcS "Forall failure" $
- vcat [ ppr s1, ppr s2, ppr bndrs1, ppr bndrs2
- , ppr (map binderArgFlag bndrs1)
- , ppr (map binderArgFlag bndrs2) ]
- ; canEqHardFailure ev s1 s2 }
- else
- do { traceTcS "Creating implication for polytype equality" $ ppr ev
- ; kind_cos <- zipWithM (unifyWanted loc Nominal)
- (map binderKind bndrs1) (map binderKind bndrs2)
- ; all_co <- deferTcSForAllEq (eqRelRole eq_rel) loc
- kind_cos (bndrs1,body1) (bndrs2,body2)
- ; setWantedEq orig_dest all_co
- ; stopWith ev "Deferred polytype equality" } }
- | otherwise
- = do { traceTcS "Omitting decomposition of given polytype equality" $
- pprEq s1 s2 -- See Note [Do not decompose given polytype equalities]
- ; stopWith ev "Discard given polytype equality" }
+ = can_eq_nc_forall ev eq_rel s1 s2
-- See Note [Canonicalising type applications] about why we require flat types
can_eq_nc' True _rdr_env _envs ev eq_rel (AppTy t1 s1) _ ty2 _
@@ -641,6 +623,77 @@ can_eq_nc' True _rdr_env _envs ev _eq_rel _ ps_ty1 _ ps_ty2
; canEqHardFailure ev ps_ty1 ps_ty2 }
---------------------------------
+can_eq_nc_forall :: CtEvidence -> EqRel
+ -> Type -> Type -- LHS and RHS
+ -> TcS (StopOrContinue Ct)
+-- (forall as. phi1) ~ (forall bs. phi2)
+-- Check for length match of as, bs
+-- Then build an implication constraint: forall as. phi1 ~ phi2[as/bs]
+-- But remember also to unify the kinds of as and bs
+-- (this is the 'go' loop), and actually substitute phi2[as |> cos / bs]
+-- Remember also that we might have forall z (a:z). blah
+-- so we must proceed one binder at a time (Trac #13879)
+
+can_eq_nc_forall ev eq_rel s1 s2
+ | CtWanted { ctev_loc = loc, ctev_dest = orig_dest } <- ev
+ = do { let free_tvs1 = tyCoVarsOfType s1
+ free_tvs2 = tyCoVarsOfType s2
+ (bndrs1, phi1) = tcSplitForAllTyVarBndrs s1
+ (bndrs2, phi2) = tcSplitForAllTyVarBndrs s2
+ ; if not (equalLength bndrs1 bndrs2)
+ then do { traceTcS "Forall failure" $
+ vcat [ ppr s1, ppr s2, ppr bndrs1, ppr bndrs2
+ , ppr (map binderArgFlag bndrs1)
+ , ppr (map binderArgFlag bndrs2) ]
+ ; canEqHardFailure ev s1 s2 }
+ else
+ do { traceTcS "Creating implication for polytype equality" $ ppr ev
+ ; let empty_subst1 = mkEmptyTCvSubst $ mkInScopeSet free_tvs1
+ ; (subst1, skol_tvs) <- tcInstSkolTyVarsX empty_subst1 $
+ binderVars bndrs1
+
+ ; let skol_info = UnifyForAllSkol phi1
+ phi1' = substTy subst1 phi1
+
+ -- Unify the kinds, extend the substitution
+ go (skol_tv:skol_tvs) subst (bndr2:bndrs2)
+ = do { let tv2 = binderVar bndr2
+ ; kind_co <- unifyWanted loc Nominal
+ (tyVarKind skol_tv)
+ (substTy subst (tyVarKind tv2))
+ ; let subst' = extendTvSubst subst tv2
+ (mkCastTy (mkTyVarTy skol_tv) kind_co)
+ ; co <- go skol_tvs subst' bndrs2
+ ; return (mkForAllCo skol_tv kind_co co) }
+
+ -- Done: unify phi1 ~ phi2
+ go [] subst bndrs2
+ = ASSERT( null bndrs2 )
+ unifyWanted loc (eqRelRole eq_rel)
+ phi1' (substTy subst phi2)
+
+ go _ _ _ = panic "cna_eq_nc_forall" -- case (s:ss) []
+
+ empty_subst2 = mkEmptyTCvSubst $ mkInScopeSet $
+ free_tvs2 `extendVarSetList` skol_tvs
+
+ ; (implic, _ev_binds, all_co) <- buildImplication skol_info skol_tvs [] $
+ go skol_tvs empty_subst2 bndrs2
+ -- We have nowhere to put these bindings
+ -- but TcSimplify.setImplicationStatus
+ -- checks that we don't actually use them
+ -- when skol_info = UnifyForAllSkol
+
+ ; updWorkListTcS (extendWorkListImplic implic)
+ ; setWantedEq orig_dest all_co
+ ; stopWith ev "Deferred polytype equality" } }
+
+ | otherwise
+ = do { traceTcS "Omitting decomposition of given polytype equality" $
+ pprEq s1 s2 -- See Note [Do not decompose given polytype equalities]
+ ; stopWith ev "Discard given polytype equality" }
+
+---------------------------------
-- | Compare types for equality, while zonking as necessary. Gives up
-- as soon as it finds that two types are not equal.
-- This is quite handy when some unification has made two
diff --git a/compiler/typecheck/TcSMonad.hs b/compiler/typecheck/TcSMonad.hs
index 473f3255eb..bb4f55788f 100644
--- a/compiler/typecheck/TcSMonad.hs
+++ b/compiler/typecheck/TcSMonad.hs
@@ -7,7 +7,7 @@ module TcSMonad (
WorkList(..), isEmptyWorkList, emptyWorkList,
extendWorkListNonEq, extendWorkListCt, extendWorkListDerived,
extendWorkListCts, extendWorkListEq, extendWorkListFunEq,
- appendWorkList,
+ appendWorkList, extendWorkListImplic,
selectNextWorkItem,
workListSize, workListWantedCount,
getWorkList, updWorkListTcS,
@@ -16,9 +16,9 @@ module TcSMonad (
TcS, runTcS, runTcSDeriveds, runTcSWithEvBinds,
failTcS, warnTcS, addErrTcS,
runTcSEqualities,
- nestTcS, nestImplicTcS, setEvBindsTcS,
+ nestTcS, nestImplicTcS, setEvBindsTcS, buildImplication,
- runTcPluginTcS, addUsedGRE, addUsedGREs, deferTcSForAllEq,
+ runTcPluginTcS, addUsedGRE, addUsedGREs,
-- Tracing etc
panicTcS, traceTcS,
@@ -93,7 +93,7 @@ module TcSMonad (
-- MetaTyVars
newFlexiTcSTy, instFlexi, instFlexiX,
cloneMetaTyVar, demoteUnfilledFmv,
- tcInstType,
+ tcInstType, tcInstSkolTyVarsX,
TcLevel, isTouchableMetaTyVarTcS,
isFilledMetaTyVar_maybe, isFilledMetaTyVar,
@@ -277,8 +277,8 @@ extendWorkListDeriveds loc evs wl
| isDroppableDerivedLoc loc = wl { wl_deriv = evs ++ wl_deriv wl }
| otherwise = extendWorkListEqs (map mkNonCanonical evs) wl
-extendWorkListImplic :: Implication -> WorkList -> WorkList
-extendWorkListImplic implic wl = wl { wl_implics = implic `consBag` wl_implics wl }
+extendWorkListImplic :: Bag Implication -> WorkList -> WorkList
+extendWorkListImplic implics wl = wl { wl_implics = implics `unionBags` wl_implics wl }
extendWorkListCt :: Ct -> WorkList -> WorkList
-- Agnostic
@@ -2530,6 +2530,45 @@ nestTcS (TcS thing_inside)
; return res }
+buildImplication :: SkolemInfo
+ -> [TcTyVar] -- Skolems
+ -> [EvVar] -- Givens
+ -> TcS result
+ -> TcS (Bag Implication, TcEvBinds, result)
+-- Just like TcUnify.buildImplication, but in the TcS monnad,
+-- using the work-list to gather the constraints
+buildImplication skol_info skol_tvs givens (TcS thing_inside)
+ = TcS $ \ env ->
+ do { new_wl_var <- TcM.newTcRef emptyWorkList
+ ; tc_lvl <- TcM.getTcLevel
+ ; let new_tclvl = pushTcLevel tc_lvl
+
+ ; res <- TcM.setTcLevel new_tclvl $
+ thing_inside (env { tcs_worklist = new_wl_var })
+
+ ; wl@WL { wl_eqs = eqs } <- TcM.readTcRef new_wl_var
+ ; if null eqs
+ then return (emptyBag, emptyTcEvBinds, res)
+ else
+ do { env <- TcM.getLclEnv
+ ; ev_binds_var <- TcM.newTcEvBinds
+ ; let wc = ASSERT2( null (wl_funeqs wl) && null (wl_rest wl) &&
+ null (wl_deriv wl) && null (wl_implics wl), ppr wl )
+ WC { wc_simple = listToCts eqs
+ , wc_impl = emptyBag
+ , wc_insol = emptyCts }
+ imp = Implic { ic_tclvl = new_tclvl
+ , ic_skols = skol_tvs
+ , ic_no_eqs = True
+ , ic_given = givens
+ , ic_wanted = wc
+ , ic_status = IC_Unsolved
+ , ic_binds = ev_binds_var
+ , ic_env = env
+ , ic_needed = emptyVarSet
+ , ic_info = skol_info }
+ ; return (unitBag imp, TcEvBinds ev_binds_var, res) } }
+
{-
Note [Propagate the solved dictionaries]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2925,7 +2964,8 @@ tcInstType :: ([TyVar] -> TcM (TCvSubst, [TcTyVar]))
-- (type vars, preds (incl equalities), rho)
tcInstType inst_tyvars id = wrapTcS (TcM.tcInstType inst_tyvars id)
-
+tcInstSkolTyVarsX :: TCvSubst -> [TyVar] -> TcS (TCvSubst, [TcTyVar])
+tcInstSkolTyVarsX subst tvs = wrapTcS $ TcM.tcInstSkolTyVarsX subst tvs
-- Creating and setting evidence variables and CtFlavors
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3144,47 +3184,3 @@ from which we get the implication
See TcSMonad.deferTcSForAllEq
-}
--- Deferring forall equalities as implications
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-deferTcSForAllEq :: Role -- Nominal or Representational
- -> CtLoc -- Original wanted equality flavor
- -> [Coercion] -- among the kinds of the binders
- -> ([TyVarBinder],TcType) -- ForAll tvs1 body1
- -> ([TyVarBinder],TcType) -- ForAll tvs2 body2
- -> TcS Coercion
-deferTcSForAllEq role loc kind_cos (bndrs1,body1) (bndrs2,body2)
- = do { let tvs1' = zipWithEqual "deferTcSForAllEq"
- mkCastTy (mkTyVarTys tvs1) kind_cos
- body2' = substTyWithUnchecked tvs2 tvs1' body2
- ; (subst, skol_tvs) <- wrapTcS $ TcM.tcInstSkolTyVars tvs1
- ; let phi1 = Type.substTyUnchecked subst body1
- phi2 = Type.substTyUnchecked subst body2'
- skol_info = UnifyForAllSkol phi1
-
- ; (ctev, hole_co) <- newWantedEq loc role phi1 phi2
- ; env <- getLclEnv
- ; ev_binds <- newTcEvBinds
- -- We have nowhere to put these bindings
- -- but TcSimplify.setImplicationStatus
- -- checks that we don't actually use them
- ; let new_tclvl = pushTcLevel (tcl_tclvl env)
- wc = WC { wc_simple = singleCt (mkNonCanonical ctev)
- , wc_impl = emptyBag
- , wc_insol = emptyCts }
- imp = Implic { ic_tclvl = new_tclvl
- , ic_skols = skol_tvs
- , ic_no_eqs = True
- , ic_given = []
- , ic_wanted = wc
- , ic_status = IC_Unsolved
- , ic_binds = ev_binds
- , ic_env = env
- , ic_needed = emptyVarSet
- , ic_info = skol_info }
- ; updWorkListTcS (extendWorkListImplic imp)
- ; let cobndrs = zip skol_tvs kind_cos
- ; return $ mkForAllCos cobndrs hole_co }
- where
- tvs1 = binderVars bndrs1
- tvs2 = binderVars bndrs2
diff --git a/testsuite/tests/typecheck/should_compile/T13879.hs b/testsuite/tests/typecheck/should_compile/T13879.hs
new file mode 100644
index 0000000000..9708c1dd41
--- /dev/null
+++ b/testsuite/tests/typecheck/should_compile/T13879.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeInType #-}
+{-# LANGUAGE TypeOperators #-}
+module Bug where
+
+import Data.Kind
+
+data family Sing (a :: k)
+
+data HR (a :: j) (b :: k) where
+ HRefl :: HR a a
+
+data instance Sing (z :: HR a b) where
+ SHRefl :: Sing HRefl
+
+foo :: forall (j :: Type) (k :: Type) (a :: j)
+ (b :: k) (r :: HR a b)
+ (p :: forall (z :: Type) (y :: z). HR a y -> Type).
+ Sing r
+ -> App p HRefl
+ -> HRApp p r
+foo SHRefl pHRefl = pHRefl
+
+type App f x = f x
+type HRApp (f :: forall (z :: Type) (y :: z). HR a y -> Type)
+ (x :: HR a b) = f x
diff --git a/testsuite/tests/typecheck/should_compile/all.T b/testsuite/tests/typecheck/should_compile/all.T
index dc7bd7b707..2fc0241fb7 100644
--- a/testsuite/tests/typecheck/should_compile/all.T
+++ b/testsuite/tests/typecheck/should_compile/all.T
@@ -565,3 +565,4 @@ test('T13785', normal, compile, [''])
test('T13804', normal, compile, [''])
test('T13822', normal, compile, [''])
test('T13871', normal, compile, [''])
+test('T13879', normal, compile, [''])
diff --git a/testsuite/tests/typecheck/should_fail/tcfail174.stderr b/testsuite/tests/typecheck/should_fail/tcfail174.stderr
index 6cc3fffdd3..e8073887ac 100644
--- a/testsuite/tests/typecheck/should_fail/tcfail174.stderr
+++ b/testsuite/tests/typecheck/should_fail/tcfail174.stderr
@@ -10,7 +10,7 @@ tcfail174.hs:16:14: error:
• Couldn't match type ‘a’ with ‘a1’
because type variable ‘a1’ would escape its scope
This (rigid, skolem) type variable is bound by
- the type a1 -> a1
+ the type a -> a
at tcfail174.hs:16:1-14
Expected type: Capture (forall x. x -> a)
Actual type: Capture (forall a. a -> a)