{-| Module : UnifierHeuristics License : GPL Maintainer : helium@cs.uu.nl Stability : experimental Portability : portable A heuristic that tries to blaim two program locations that both contribute to the type error, instead of preferring (or choosing) one location over the other. The type error messages will be more "symmetric". -} module Helium.StaticAnalysis.Heuristics.UnifierHeuristics where import Top.Types import Top.Interface.TypeInference import Top.Implementation.TypeGraph.Basics import Top.Implementation.TypeGraph.ClassMonadic import Top.Implementation.TypeGraph.Heuristic import Helium.StaticAnalysis.Heuristics.RepairHeuristics import Helium.StaticAnalysis.Miscellaneous.ConstraintInfo import Data.Function import Data.List (partition, sortBy) import Data.Maybe class IsUnifier a where typeErrorForUnifier :: (Tp, Tp) -> (a, a) -> a isUnifier :: a -> Maybe (Int, (String, LocalInfo, String)) unifierVertex :: (HasTypeGraph m info, IsUnifier info) => Selector m info unifierVertex = Selector ("Unification vertex", f) where f (EdgeId _ _ this, info) = case isUnifier info of Nothing -> return Nothing Just (unifier, _) -> do neighbours <- edgesFrom (VertexId unifier) let (unifiersUnsorted, contexts) = partition p (map f' neighbours) f' (EdgeId (VertexId v1) (VertexId v2) _, info') | v1 == unifier = (v2, info') | otherwise = (v1, info') p (_, info') = case isUnifier info' of Nothing -> False Just (u,_) -> u == unifier -- sort this list by the ordering in which the type variables were assigned -- to prevent undeterministic type error messages. unifiers = sortBy (compare `on` fst) unifiersUnsorted doWithoutEdges neighbours $ do synonyms <- getTypeSynonyms unifierMTypes <- mapM (substituteTypeSafe . TVar . fst) unifiers contextMTypes <- mapM (substituteTypeSafe . TVar . fst) contexts if any isNothing (unifierMTypes ++ contextMTypes) then return Nothing else do let unifierTypes = map fromJust unifierMTypes contextTypes = map fromJust contextMTypes indices = let predicate i = unifiableList synonyms (deleteIndex i unifierTypes ++ contextTypes) in filter predicate [0..length unifierTypes - 1] case indices of -- if there are exactly two branches that cause the problem, then report -- these two in a type error [index1, index2] | not (unifiableList synonyms (unifierTypes ++ contextTypes)) -> let (v1, info1) = unifiers !! index1 (v2, info2) = unifiers !! index2 edges = [ edge | (edge@(EdgeId (VertexId va) (VertexId vb) i), _) <- neighbours, predicate va vb, this==i ] predicate va vb = (va == unifier && vb `elem` [v1, v2]) || (vb == unifier && va `elem` [v1, v2]) newInfo = typeErrorForUnifier (TVar v1, TVar v2) (info1, info2) in return $ Just (7, "two inconsistent branches", edges, newInfo) _ -> return Nothing