From 5f3528244ad3ec004bb67a8a2ec086fe90318ce7 Mon Sep 17 00:00:00 2001 From: simonpj <unknown> Date: Thu, 22 Jun 2000 14:45:41 +0000 Subject: [PATCH] [project @ 2000-06-22 14:45:41 by simonpj] *** NO NEED TO MERGE WITH 4.07 *** (but it would do no harm) * Improve an error message when overlapping instance declarations are present. Carl Witty reported this infelicitous message. The problem arises for this code: class Foo a class (Foo a) => Bar a data Dat a = Dat instance Foo (Dat a) instance Foo (Dat Integer) instance Bar (Dat a) The instance decl for Bar should say instance Foo (Dat a) => Bar (Dat a) because the overlapping instance decls for Foo can't be resolved (or at least might vary depending on how a is instantiated). --- ghc/compiler/typecheck/Inst.lhs | 16 ++++---- ghc/compiler/typecheck/TcMatches.lhs | 2 +- ghc/compiler/typecheck/TcSimplify.lhs | 53 +++++++++++++++++++-------- ghc/compiler/types/InstEnv.lhs | 49 ++++++++++++++++++------- 4 files changed, 83 insertions(+), 37 deletions(-) diff --git a/ghc/compiler/typecheck/Inst.lhs b/ghc/compiler/typecheck/Inst.lhs index d4565d01d1ff..c73497e33a92 100644 --- a/ghc/compiler/typecheck/Inst.lhs +++ b/ghc/compiler/typecheck/Inst.lhs @@ -59,7 +59,7 @@ import PrelInfo ( isStandardClass, isCcallishClass, isNoDictClass ) import Name ( OccName, Name, mkDictOcc, mkMethodOcc, mkIPOcc, getOccName, nameUnique ) import PprType ( pprPred ) -import InstEnv ( InstEnv, lookupInstEnv ) +import InstEnv ( InstEnv, lookupInstEnv, InstEnvResult(..) ) import SrcLoc ( SrcLoc ) import Type ( Type, PredType(..), ThetaType, mkTyVarTy, isTyVarTy, mkDictTy, mkPredTy, @@ -659,9 +659,9 @@ lookupInst :: Inst -- Dictionaries lookupInst dict@(Dict _ (Class clas tys) loc) - = case lookupInstEnv (ppr clas) (classInstEnv clas) tys of + = case lookupInstEnv (classInstEnv clas) tys of - Just (tenv, dfun_id) + FoundInst tenv dfun_id -> let subst = mkSubst (tyVarsOfTypes tys) tenv (tyvars, rho) = splitForAllTys (idType dfun_id) @@ -682,7 +682,7 @@ lookupInst dict@(Dict _ (Class clas tys) loc) in returnNF_Tc (GenInst dicts rhs) - Nothing -> returnNF_Tc NoInstance + other -> returnNF_Tc NoInstance lookupInst dict@(Dict _ _ loc) = returnNF_Tc NoInstance -- Methods @@ -760,12 +760,12 @@ lookupSimpleInst :: InstEnv -> NF_TcM s (Maybe [(Class,[Type])]) -- Here are the needed (c,t)s lookupSimpleInst class_inst_env clas tys - = case lookupInstEnv (ppr clas) class_inst_env tys of - Nothing -> returnNF_Tc Nothing - - Just (tenv, dfun) + = case lookupInstEnv class_inst_env tys of + FoundInst tenv dfun -> returnNF_Tc (Just (substClasses (mkSubst emptyInScopeSet tenv) theta')) where (_, theta, _) = splitSigmaTy (idType dfun) theta' = map (\(Class clas tys) -> (clas,tys)) theta + + other -> returnNF_Tc Nothing \end{code} diff --git a/ghc/compiler/typecheck/TcMatches.lhs b/ghc/compiler/typecheck/TcMatches.lhs index 0fb4aba6b646..ebd6ba56e2bd 100644 --- a/ghc/compiler/typecheck/TcMatches.lhs +++ b/ghc/compiler/typecheck/TcMatches.lhs @@ -1,4 +1,4 @@ -\% +% % (c) The GRASP/AQUA Project, Glasgow University, 1992-1998 % \section[TcMatches]{Typecheck some @Matches@} diff --git a/ghc/compiler/typecheck/TcSimplify.lhs b/ghc/compiler/typecheck/TcSimplify.lhs index 8c4de82dac7b..288ecf82c6eb 100644 --- a/ghc/compiler/typecheck/TcSimplify.lhs +++ b/ghc/compiler/typecheck/TcSimplify.lhs @@ -136,7 +136,7 @@ import Inst ( lookupInst, lookupSimpleInst, LookupInstResult(..), isStdClassTyVarDict, isMethodFor, instToId, instBindingRequired, instCanBeGeneralised, newDictFromOld, newFunDepFromDict, - getDictClassTys, getIPs, + getDictClassTys, getIPs, isTyVarDict, getDictPred_maybe, getMethodTheta_maybe, instLoc, pprInst, zonkInst, tidyInst, tidyInsts, Inst, LIE, pprInsts, pprInstsInFull, @@ -154,7 +154,7 @@ import Type ( Type, ThetaType, TauType, ClassContext, mkTyVarTy, getTyVar, isTyVarTy, splitSigmaTy, tyVarsOfTypes ) -import InstEnv ( InstEnv ) +import InstEnv ( InstEnv, lookupInstEnv, InstEnvResult(..) ) import Subst ( mkTopTyVarSubst, substClasses ) import PprType ( pprConstraint ) import TysWiredIn ( unitTy ) @@ -1266,22 +1266,45 @@ addTopInstanceErr dict (tidy_env, tidy_dict) = tidyInst emptyTidyEnv dict addNoInstanceErr str givens dict - = addInstErrTcM (instLoc dict) - (tidy_env, - sep [ptext SLIT("Could not deduce") <+> quotes (pprInst tidy_dict), - nest 4 $ ptext SLIT("from the context:") <+> pprInsts tidy_givens] - $$ - ptext SLIT("Probable cause:") <+> - vcat [sep [ptext SLIT("missing") <+> quotes (pprInst tidy_dict), - ptext SLIT("in") <+> str], - if isClassDict dict && all_tyvars then empty else - ptext SLIT("or missing instance declaration for") <+> quotes (pprInst tidy_dict)] - ) + = addInstErrTcM (instLoc dict) (tidy_env, doc) where - all_tyvars = all isTyVarTy tys - (_, tys) = getDictClassTys dict + doc = vcat [herald <+> quotes (pprInst tidy_dict), + nest 4 $ ptext SLIT("from the context") <+> pprInsts tidy_givens, + ambig_doc, + ptext SLIT("Probable fix:"), + nest 4 fix1, + nest 4 fix2] + + herald = ptext SLIT("Could not") <+> unambig_doc <+> ptext SLIT("deduce") + unambig_doc | ambig_overlap = ptext SLIT("unambiguously") + | otherwise = empty + + ambig_doc + | not ambig_overlap = empty + | otherwise + = vcat [ptext SLIT("The choice of (overlapping) instance declaration"), + nest 4 (ptext SLIT("depends on the instantiation of") <+> + quotes (pprWithCommas ppr (varSetElems (tyVarsOfInst tidy_dict))))] + + fix1 = sep [ptext SLIT("Add") <+> quotes (pprInst tidy_dict), + ptext SLIT("to the") <+> str] + + fix2 | isTyVarDict dict || ambig_overlap + = empty + | otherwise + = ptext SLIT("Or add an instance declaration for") <+> quotes (pprInst tidy_dict) + (tidy_env, tidy_dict:tidy_givens) = tidyInsts emptyTidyEnv (dict:givens) + -- Checks for the ambiguous case when we have overlapping instances + ambig_overlap | isClassDict dict + = case lookupInstEnv (classInstEnv clas) tys of + NoMatch ambig -> ambig + other -> False + | otherwise = False + where + (clas,tys) = getDictClassTys dict + -- Used for the ...Thetas variants; all top level addNoInstErr (c,ts) = addErrTc (ptext SLIT("No instance for") <+> quotes (pprConstraint c ts)) diff --git a/ghc/compiler/types/InstEnv.lhs b/ghc/compiler/types/InstEnv.lhs index 231fe203392d..d0fc445d7fb6 100644 --- a/ghc/compiler/types/InstEnv.lhs +++ b/ghc/compiler/types/InstEnv.lhs @@ -5,7 +5,8 @@ \begin{code} module InstEnv ( - InstEnv, emptyInstEnv, addToInstEnv, lookupInstEnv + InstEnv, emptyInstEnv, addToInstEnv, + lookupInstEnv, InstEnvResult(..) ) where #include "HsVersions.h" @@ -147,30 +148,52 @@ emptyInstEnv = [] isEmptyInstEnv env = null env \end{code} -@lookupInstEnv@ looks up in a @InstEnv@, using a one-way match. Since the env is kept -ordered, the first match must be the only one. -The thing we are looking up can have an -arbitrary "flexi" part. +@lookupInstEnv@ looks up in a @InstEnv@, using a one-way match. Since +the env is kept ordered, the first match must be the only one. The +thing we are looking up can have an arbitrary "flexi" part. \begin{code} -lookupInstEnv :: SDoc -- For error report - -> InstEnv -- The envt +lookupInstEnv :: InstEnv -- The envt -> [Type] -- Key - -> Maybe (TyVarSubstEnv, Id) - -lookupInstEnv doc env key + -> InstEnvResult + +data InstEnvResult + = FoundInst -- There is a (template,substitution) pair + -- that makes the template match the key, + -- and no template is an instance of the key + TyVarSubstEnv Id + + | NoMatch Bool -- Boolean is true iff there is at least one + -- template that matches the key. + -- (but there are other template(s) that are + -- instances of the key, so we don't report + -- FoundInst) + -- The NoMatch True case happens when we look up + -- Foo [a] + -- in an InstEnv that has entries for + -- Foo [Int] + -- Foo [b] + -- Then which we choose would depend on the way in which 'a' + -- is instantiated. So we say there is no match, but identify + -- it as ambiguous case in the hope of giving a better error msg. + -- See the notes above from Jeff Lewis + +lookupInstEnv env key = find env where key_vars = tyVarsOfTypes key - find [] = Nothing + find [] = NoMatch False find ((tpl_tyvars, tpl, val) : rest) = case matchTys tpl_tyvars tpl key of Nothing -> case matchTys key_vars key tpl of Nothing -> find rest - Just (_, _) -> Nothing + Just (_, _) -> NoMatch (any_match rest) Just (subst, leftovers) -> ASSERT( null leftovers ) - Just (subst, val) + FoundInst subst val + any_match rest = or [ maybeToBool (matchTys tvs tpl key) + | (tvs,tpl,_) <- rest + ] \end{code} @addToInstEnv@ extends a @InstEnv@, checking for overlaps. -- GitLab