summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Peyton Jones <simonpj@microsoft.com>2016-02-26 09:02:07 +0000
committerSimon Peyton Jones <simonpj@microsoft.com>2016-02-26 17:16:20 +0000
commit7496be5c0ab96bcc9ab70ab873aa561674b7789d (patch)
tree8641befe7a4f4fbd4b6d8802a2a22d362cb31781
parent4ddfe1352e20d805a0ad6eeea0400ee218023bfb (diff)
downloadhaskell-7496be5c0ab96bcc9ab70ab873aa561674b7789d.tar.gz
Exclude TyVars from the constraint solver
There is a general invariant that the constraint solver doesn't see TyVars, only TcTyVars. But when checking the generic-default signature of a class, we called checkValidType on the generic-default type, which had the class TyVar free. That in turn meant that it wasn't considered during flattening, which led to the error reported in Trac #11608. The fix is simple: call checkValidType on the /closed/ type. Easy. While I was at it, I added a bunch of ASSERTs about the TcTyVar invariant.
-rw-r--r--compiler/typecheck/TcErrors.hs49
-rw-r--r--compiler/typecheck/TcTyClsDecls.hs16
-rw-r--r--compiler/typecheck/TcType.hs50
-rw-r--r--testsuite/tests/typecheck/should_compile/T11608.hs14
-rw-r--r--testsuite/tests/typecheck/should_compile/all.T1
5 files changed, 96 insertions, 34 deletions
diff --git a/compiler/typecheck/TcErrors.hs b/compiler/typecheck/TcErrors.hs
index b1cc44975c..58bcafdda9 100644
--- a/compiler/typecheck/TcErrors.hs
+++ b/compiler/typecheck/TcErrors.hs
@@ -961,6 +961,20 @@ Here the second equation is unreachable. The original constraint
the *signature* (Trac #7293). So, for Given errors we replace the
env (and hence src-loc) on its CtLoc with that from the immediately
enclosing implication.
+
+Note [Error messages for untouchables]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider (Trac #9109)
+ data G a where { GBool :: G Bool }
+ foo x = case x of GBool -> True
+
+Here we can't solve (t ~ Bool), where t is the untouchable result
+meta-var 't', because of the (a ~ Bool) from the pattern match.
+So we infer the type
+ f :: forall a t. G a -> t
+making the meta-var 't' into a skolem. So when we come to report
+the unsolved (t ~ Bool), t won't look like an untouchable meta-var
+any more. So we don't assert that it is.
-}
mkEqErr :: ReportErrCtxt -> [Ct] -> TcM ErrMsg
@@ -1212,9 +1226,13 @@ mkTyVarEqErr dflags ctxt report ct oriented tv1 ty2
; mkErrorMsgFromCt ctxt ct (mconcat [msg, tv_extra, report]) }
-- Nastiest case: attempt to unify an untouchable variable
+ -- See Note [Error messages for untouchables]
| (implic:_) <- cec_encl ctxt -- Get the innermost context
- , Implic { ic_env = env, ic_given = given, ic_info = skol_info } <- implic
- = do { let msg = important $ misMatchMsg ct oriented ty1 ty2
+ , Implic { ic_env = env, ic_given = given
+ , ic_tclvl = lvl, ic_info = skol_info } <- implic
+ = ASSERT2( isTcTyVar tv1 && not (isTouchableMetaTyVar lvl tv1)
+ , ppr tv1 ) -- See Note [Error messages for untouchables]
+ do { let msg = important $ misMatchMsg ct oriented ty1 ty2
tclvl_extra = important $
nest 2 $
sep [ quotes (ppr tv1) <+> text "is untouchable"
@@ -1324,23 +1342,21 @@ extraTyVarInfo :: ReportErrCtxt -> TcTyVar -> TcType -> SDoc
-- Add on extra info about skolem constants
-- NB: The types themselves are already tidied
extraTyVarInfo ctxt tv1 ty2
- = tv_extra tv1 $$ ty_extra ty2
+ = ASSERT2( isTcTyVar tv1, ppr tv1 )
+ tv_extra tv1 $$ ty_extra ty2
where
implics = cec_encl ctxt
ty_extra ty = case tcGetTyVar_maybe ty of
Just tv -> tv_extra tv
Nothing -> empty
- tv_extra tv | isTcTyVar tv, isSkolemTyVar tv
- , let pp_tv = quotes (ppr tv)
- = case tcTyVarDetails tv of
- SkolemTv {} -> pprSkol implics tv
- FlatSkol {} -> pp_tv <+> text "is a flattening type variable"
- RuntimeUnk {} -> pp_tv <+> text "is an interactive-debugger skolem"
- MetaTv {} -> empty
-
- | otherwise -- Normal case
- = empty
+ tv_extra tv
+ | let pp_tv = quotes (ppr tv)
+ = case tcTyVarDetails tv of
+ SkolemTv {} -> pprSkol implics tv
+ FlatSkol {} -> pp_tv <+> text "is a flattening type variable"
+ RuntimeUnk {} -> pp_tv <+> text "is an interactive-debugger skolem"
+ MetaTv {} -> empty
suggestAddSig :: ReportErrCtxt -> TcType -> TcType -> SDoc
-- See Note [Suggest adding a type signature]
@@ -1354,7 +1370,7 @@ suggestAddSig ctxt ty1 ty2
where
inferred_bndrs = nub (get_inf ty1 ++ get_inf ty2)
get_inf ty | Just tv <- tcGetTyVar_maybe ty
- , isTcTyVar tv, isSkolemTyVar tv
+ , isSkolemTyVar tv
, (_, InferSkol prs) <- getSkolemInfo (cec_encl ctxt) tv
= map fst prs
| otherwise
@@ -1711,8 +1727,9 @@ carry on. For example
f x = x:x
Here we will infer somethiing like
f :: forall a. a -> [a]
-with a suspended error of (a ~ [a]). So 'a' is now a skolem, but not
-one bound by the programmer! Here we really should report an occurs check.
+with a deferred error of (a ~ [a]). So in the deferred unsolved constraint
+'a' is now a skolem, but not one bound by the programmer in the context!
+Here we really should report an occurs check.
So isUserSkolem distinguishes the two.
diff --git a/compiler/typecheck/TcTyClsDecls.hs b/compiler/typecheck/TcTyClsDecls.hs
index 31eaeb0d5d..6a0daabe8a 100644
--- a/compiler/typecheck/TcTyClsDecls.hs
+++ b/compiler/typecheck/TcTyClsDecls.hs
@@ -2247,9 +2247,7 @@ checkValidClass cls
; unless constrained_class_methods $
mapM_ check_constraint (tail (theta1 ++ theta2))
- ; case dm of
- Just (_, GenericDM ty) -> checkValidType ctxt ty
- _ -> return ()
+ ; check_dm ctxt dm
}
where
ctxt = FunSigCtxt op_name True -- Report redundant class constraints
@@ -2279,6 +2277,18 @@ checkValidClass cls
fam_tvs = tyConTyVars fam_tc
mini_env = zipVarEnv tyvars (mkTyVarTys tyvars)
+ check_dm :: UserTypeCtxt -> DefMethInfo -> TcM ()
+ -- Check validity of the /top-level/ generic-default type
+ -- E.g for class C a where
+ -- default op :: forall b. (a~b) => blah
+ -- we do not want to do an ambiguity check on a type with
+ -- a free TyVar 'a' (Trac #11608). See TcType
+ -- Note [TyVars and TcTyVars during type checking]
+ -- Hence the mkSpecForAllTys to close the type.
+ check_dm ctxt (Just (_, GenericDM ty))
+ = checkValidType ctxt (mkSpecForAllTys tyvars ty)
+ check_dm _ _ = return ()
+
checkFamFlag :: Name -> TcM ()
-- Check that we don't use families without -XTypeFamilies
-- The parser won't even parse them, but I suppose a GHC API
diff --git a/compiler/typecheck/TcType.hs b/compiler/typecheck/TcType.hs
index 972cbae749..73a46ae6c6 100644
--- a/compiler/typecheck/TcType.hs
+++ b/compiler/typecheck/TcType.hs
@@ -375,16 +375,26 @@ Similarly consider
When doing kind inference on {S,T} we don't want *skolems* for k1,k2,
because they end up unifying; we want those SigTvs again.
-Note [TyVars and TcTyVars]
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+Note [TyVars and TcTyVars during type checking]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Var type has constructors TyVar and TcTyVar. They are used
as follows:
-* TcTyVar: used only during type checking. Should never appear
+* TcTyVar: used /only/ during type checking. Should never appear
afterwards. May contain a mutable field, in the MetaTv case.
-* TyVar: can appear any time. During type checking they behave
- precisely like (SkolemTv False) = vanillaSkolemTv
+* TyVar: is never seen by the constraint solver, except locally
+ inside a type like (forall a. [a] ->[a]), where 'a' is a TyVar.
+ We instantiate these with TcTyVars before exposing the type
+ to the constraint solver.
+
+I have swithered about the latter invariant, excluding TyVars from the
+constraint solver. It's not strictly essential, and indeed
+(historically but still there) Var.tcTyVarDetails returns
+vanillaSkolemTv for a TyVar.
+
+But ultimately I want to seeparate Type from TcType, and in that case
+we would need to enforce the separation.
-}
-- A TyVarDetails is inside a TyVar
@@ -838,7 +848,8 @@ allBoundVariabless = mapUnionVarSet allBoundVariables
isTouchableOrFmv :: TcLevel -> TcTyVar -> Bool
isTouchableOrFmv ctxt_tclvl tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
MetaTv { mtv_tclvl = tv_tclvl, mtv_info = info }
-> ASSERT2( checkTcLevelInvariant ctxt_tclvl tv_tclvl,
ppr tv $$ ppr tv_tclvl $$ ppr ctxt_tclvl )
@@ -850,7 +861,8 @@ isTouchableOrFmv ctxt_tclvl tv
isTouchableMetaTyVar :: TcLevel -> TcTyVar -> Bool
isTouchableMetaTyVar ctxt_tclvl tv
| isTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
MetaTv { mtv_tclvl = tv_tclvl }
-> ASSERT2( checkTcLevelInvariant ctxt_tclvl tv_tclvl,
ppr tv $$ ppr tv_tclvl $$ ppr ctxt_tclvl )
@@ -861,7 +873,8 @@ isTouchableMetaTyVar ctxt_tclvl tv
isFloatedTouchableMetaTyVar :: TcLevel -> TcTyVar -> Bool
isFloatedTouchableMetaTyVar ctxt_tclvl tv
| isTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
MetaTv { mtv_tclvl = tv_tclvl } -> tv_tclvl `strictlyDeeperThan` ctxt_tclvl
_ -> False
| otherwise = False
@@ -880,44 +893,51 @@ isTyConableTyVar tv
-- with a type constructor application; in particular,
-- not a SigTv
| isTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
MetaTv { mtv_info = SigTv } -> False
_ -> True
| otherwise = True
isFmvTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
MetaTv { mtv_info = FlatMetaTv } -> True
_ -> False
-- | True of both given and wanted flatten-skolems (fak and usk)
isFlattenTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
FlatSkol {} -> True
MetaTv { mtv_info = FlatMetaTv } -> True
_ -> False
-- | True of FlatSkol skolems only
isFskTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
FlatSkol {} -> True
_ -> False
isSkolemTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
MetaTv {} -> False
_other -> True
isOverlappableTyVar tv
| isTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
SkolemTv overlappable -> overlappable
_ -> False
| otherwise = False
isMetaTyVar tv
| isTyVar tv
- = case tcTyVarDetails tv of
+ = ASSERT2( isTcTyVar tv, ppr tv )
+ case tcTyVarDetails tv of
MetaTv {} -> True
_ -> False
| otherwise = False
diff --git a/testsuite/tests/typecheck/should_compile/T11608.hs b/testsuite/tests/typecheck/should_compile/T11608.hs
new file mode 100644
index 0000000000..5627135b5b
--- /dev/null
+++ b/testsuite/tests/typecheck/should_compile/T11608.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeFamilies #-}
+
+module T11608 where
+
+type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t
+
+class Each s t a b | s -> a, t -> b, s b -> t, t a -> s where
+ each :: Traversal s t a b
+ default each :: (Traversable g, s ~ g a, t ~ g b) => Traversal s t a b
+ each = traverse
diff --git a/testsuite/tests/typecheck/should_compile/all.T b/testsuite/tests/typecheck/should_compile/all.T
index 4310be440c..ca8cd0a28f 100644
--- a/testsuite/tests/typecheck/should_compile/all.T
+++ b/testsuite/tests/typecheck/should_compile/all.T
@@ -506,3 +506,4 @@ test('T11458', normal, compile, [''])
test('T11524', normal, compile, [''])
test('T11552', normal, compile, [''])
test('T11246', normal, compile, [''])
+test('T11608', normal, compile, [''])