diff options
author | Simon Peyton Jones <simonpj@microsoft.com> | 2017-04-06 12:27:43 +0100 |
---|---|---|
committer | Simon Peyton Jones <simonpj@microsoft.com> | 2017-04-06 12:34:01 +0100 |
commit | 65b185d4886b4efa3efe3cc5ecc8dd6e07d89afe (patch) | |
tree | c8aa39e9fd28f8b4545b4df198d31627b1447edc /compiler | |
parent | 2ab7f626b94a5da4b544e01072219a95cd588202 (diff) | |
download | haskell-65b185d4886b4efa3efe3cc5ecc8dd6e07d89afe.tar.gz |
Be less aggressive about fragile-context warrnings
In the implementation of WarnSimplifiableClassConstraints, be
less aggressive about reporting a problem. We were complaining
about a "fragile" case that in fact was not fragile.
See Note [Simplifiable given constraints] in TcValidity.
This fixes Trac #13526.
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/typecheck/TcValidity.hs | 50 |
1 files changed, 33 insertions, 17 deletions
diff --git a/compiler/typecheck/TcValidity.hs b/compiler/typecheck/TcValidity.hs index 3023dfecc0..c28c21da97 100644 --- a/compiler/typecheck/TcValidity.hs +++ b/compiler/typecheck/TcValidity.hs @@ -41,7 +41,7 @@ import HsSyn -- HsType import TcRnMonad -- TcType, amongst others import TcEnv ( tcGetInstEnvs ) import FunDeps -import InstEnv ( ClsInst, lookupInstEnv, isOverlappable ) +import InstEnv ( InstMatch, lookupInstEnv ) import FamInstEnv ( isDominatedBy, injectiveBranches, InjectivityCheckResult(..) ) import FamInst ( makeInjectivityErrors ) @@ -810,7 +810,8 @@ check_class_pred env dflags ctxt pred cls tys | otherwise = do { check_arity - ; check_simplifiable_class_constraint + ; warn_simp <- woptM Opt_WarnSimplifiableClassConstraints + ; when warn_simp check_simplifiable_class_constraint ; checkTcM arg_tys_ok (predTyVarErr env pred) } where check_arity = checkTc (classArity cls == length tys) @@ -833,25 +834,22 @@ check_class_pred env dflags ctxt pred cls tys | DataTyCtxt {} <- ctxt -- Don't do this check for the "stupid theta" = return () -- of a data type declaration | otherwise - = do { instEnvs <- tcGetInstEnvs - ; let (matches, _, _) = lookupInstEnv False instEnvs cls tys - bad_matches = [ inst | (inst,_) <- matches - , not (isOverlappable inst) ] - ; warnIf (Reason Opt_WarnSimplifiableClassConstraints) - (not (null bad_matches)) - (simplifiable_constraint_warn bad_matches) } - - simplifiable_constraint_warn :: [ClsInst] -> SDoc - simplifiable_constraint_warn (match : _) + = do { envs <- tcGetInstEnvs + ; case lookupInstEnv False envs cls tys of + ([m], [], _) -> addWarnTc (Reason Opt_WarnSimplifiableClassConstraints) + (simplifiable_constraint_warn m) + _ -> return () } + + simplifiable_constraint_warn :: InstMatch -> SDoc + simplifiable_constraint_warn (match, _) = vcat [ hang (text "The constraint" <+> quotes (ppr (tidyType env pred))) 2 (text "matches an instance declaration") , ppr match , hang (text "This makes type inference for inner bindings fragile;") 2 (text "either use MonoLocalBinds, or simplify it using the instance") ] - simplifiable_constraint_warn [] = pprPanic "check_class_pred" (ppr pred) {- Note [Simplifiable given constraints] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A type signature like f :: Eq [(a,b)] => a -> b is very fragile, for reasons described at length in TcInteract @@ -862,9 +860,27 @@ fragility. But if we /infer/ the type of a local let-binding, things can go wrong (Trac #11948 is an example, discussed in the Note). So this warning is switched on only if we have NoMonoLocalBinds; in -that case the warning discourages uses from writing simplifiable class -constraints, at least unless the top-level instance is explicitly -declared as OVERLAPPABLE. +that case the warning discourages users from writing simplifiable +class constraints. + +The warning only fires if the constraint in the signature +matches the top-level instances in only one way, and with no +unifiers -- that is, under the same circumstances that +TcInteract.matchInstEnv fires an interaction with the top +level instances. For example (Trac #13526), consider + + instance {-# OVERLAPPABLE #-} Eq (T a) where ... + instance Eq (T Char) where .. + f :: Eq (T a) => ... + +We don't want to complain about this, even though the context +(Eq (T a)) matches an instance, because the user may be +deliberately deferring the choice so that the Eq (T Char) +has a chance to fire when 'f' is called. And the fragility +only matters when there's a risk that the instance might +fire instead of the local 'given'; and there is no such +risk in this case. Just use the same rules as for instance +firing! -} ------------------------- |