{- --------------------------------------------------------------------------- The main unification functions for NTs -} module Type.Unify(unify,unifyr) where import NT(NT(..),NewType(..),freeNT,anyVarNT) import Type.Subst import IntState hiding (NewType) import Id(Id) import qualified Data.Map as Map import Flags unify :: IntState -> Map.Map Id NT -> (NT,NT) -> Either (Map.Map Id NT, String) (Map.Map Id NT) unify state phi (t1@(NTany tvn1),t2) = case applySubst phi tvn1 of Nothing -> extendV state phi tvn1 (subst phi t2) Just phitvn -> unify state phi (phitvn,subst phi t2) unify state phi (t1@(NTvar tvn1 _),(NTany tvn2)) = case applySubst phi tvn2 of Nothing -> extendV state phi tvn2 (subst phi t1) Just phitvn -> unify state phi (phitvn,subst phi t1) unify state phi (t1@(NTvar tvn1 _),t2) = case applySubst phi tvn1 of Nothing -> extendV state phi tvn1 (subst phi t2) Just phitvn -> unify state phi (phitvn,subst phi t2) unify state phi (t1@(NTcons _ _ _),t2@(NTany tvn2)) = case applySubst phi tvn2 of Nothing -> extendV state phi tvn2 (subst phi t1) Just phitvn -> unify state phi (phitvn,subst phi t1) unify state phi (t1@(NTcons _ _ _),t2@(NTvar tvn2 _)) = case applySubst phi tvn2 of Nothing -> extendV state phi tvn2 (subst phi t1) Just phitvn -> unify state phi (phitvn,subst phi t1) unify state phi (t1@(NTcons c1 _ ts1),t2@(NTcons c2 _ ts2)) = if c1 == c2 && isNotTypeSynonym state c1 then if length ts1 == length ts2 -- length check because of constructor classes then unifyl state phi (zip ts1 ts2) else case lookupIS state c1 of Just info -> Left (phi, "can not unify " ++ show (tidI info) ++ " with " ++ show (length ts1) ++ " and " ++ show (length ts2) ++ " arguments") else case (unifyExpand state c1,unifyExpand state c2) of (Left s1 ,Left s2 ) -> let flags = getFlags state {- this happens because of circular dependency complications ... -} in if sUnifyHack flags && s1 == s2 then unifyl state phi (zip ts1 ts2) else Left (phi,"type clash between " ++ s1 ++ " and " ++ s2) (Left _ ,Right (d2,nt2)) -> unify state phi (t1 ,expand nt2 ts2) (Right (d1,nt1),Left _ ) -> unify state phi (expand nt1 ts1,t2 ) (Right (d1,nt1),Right (d2,nt2)) | d2<=d1 -> unify state phi (expand nt1 ts1,t2 ) (Right (d1,nt1),Right (d2,nt2)) -> unify state phi (t1 ,expand nt2 ts2) unify state phi (t1@(NTcons c1 _ ts1),t2@(NTapp ta2 tb2)) = case expandAll state t1 of t1@(NTcons c1 k ts1) -> -- strace ("unify(1) " ++ show t1 ++ " " ++ show t2) $ case ts1 of [] -> case lookupIS state c1 of Just info -> Left (phi, "type clash between type applicationa and " ++ show (tidI info)) _ -> unifyl state phi [(NTcons c1 k (init ts1),ta2),(last ts1,tb2)] unify state phi (t1@(NTapp ta1 tb1),t2@(NTany tvn2)) = -- strace ("unify(2) " ++ show t1 ++ " " ++ show t2) $ case applySubst phi tvn2 of Nothing -> extendV state phi tvn2 (subst phi t1) Just phitvn -> unify state phi (phitvn,subst phi t1) unify state phi (t1@(NTapp ta1 tb1),t2@(NTvar tvn2 _)) = -- strace ("unify(3) " ++ show t1 ++ " " ++ show t2) $ case applySubst phi tvn2 of Nothing -> extendV state phi tvn2 (subst phi t1) Just phitvn -> unify state phi (phitvn,subst phi t1) unify state phi (t1@(NTapp ta1 tb1),t2@(NTcons c2 _ ts2)) = case expandAll state t2 of t2@(NTcons c2 k ts2) -> -- strace ("unify(4) " ++ show t1 ++ " " ++ show t2) $ case ts2 of [] -> case lookupIS state c2 of Just info -> Left (phi, "type clash between " ++ show (tidI info) ++ " and type application ") _ -> unifyl state phi [(ta1,NTcons c2 k (init ts2)),(tb1,last ts2)] unify state phi (t1@(NTapp ta1 tb1),t2@(NTapp ta2 tb2)) = -- strace ("unify(5) " ++ show t1 ++ " " ++ show t2) $ unifyl state phi [(ta1,ta2),(tb1,tb2)] unify state phi (t1@(NTexist tvn1 _),(NTexist tvn2 _)) = if tvn1 == tvn2 then Right phi else Left (phi,"type clash between existential types") unify state phi ((NTexist _ _),(NTcons c _ _)) = case lookupIS state c of Just info -> Left (phi, "type clash between " ++ show (tidI info) ++ " and existential type ") unify state phi ((NTcons c _ _),(NTexist _ _)) = case lookupIS state c of Just info -> Left (phi, "type clash between " ++ show (tidI info) ++ " and existential type ") unify state phi ((NTexist _ _),(NTapp _ _)) = Left (phi,"type clash between existential type and type application") unify state phi ((NTapp _ _),(NTexist _ _)) = Left (phi,"type clash between existential type and type application") unify state phi (t1@(NTexist e _),t2@(NTany tvn2)) = -- strace ("unify exist " ++ show e ++ " any " ++ show tvn2) $ case applySubst phi tvn2 of Nothing -> extendV state phi tvn2 (subst phi t1) Just phitvn -> unify state phi (phitvn,subst phi t1) unify state phi (t1@(NTexist e _),t2@(NTvar tvn2 _)) = -- strace ("unify exist " ++ show e ++ " var " ++ show tvn2) $ case applySubst phi tvn2 of Nothing -> extendV state phi tvn2 (subst phi t1) Just phitvn -> unify state phi (phitvn,subst phi t1) unify state phi _ = error "unify phi _" unifyl :: IntState -> Map.Map Id NT -> [(NT,NT)] -> Either (Map.Map Id NT, String) (Map.Map Id NT) unifyl state phi eqns = foldr unify' (Right phi) eqns where unify' eqn (Right phi) = unify state phi eqn unify' eqn (Left err) = Left err {- unify a list of NTs from left to right -} unifyr :: IntState -> Map.Map Id NT -> [NT] -> Either (Map.Map Id NT, String) (Map.Map Id NT) unifyr state phi [] = Right phi unifyr state phi [e] = Right phi unifyr state phi (e1:e2:es) = case unify state phi (e1,e2) of Left err -> Left err Right phi' -> unifyr state phi' (e1:es) ------ -- expand any type synonym at top, so that none is at top in result expandAll :: IntState -> NT -> NT expandAll state t@(NTcons tcon _ ts) = case unifyExpand state tcon of Left _ -> t Right (d,nt) -> expandAll state (expand nt ts) expandAll state t = t isNotTypeSynonym :: IntState -> Id -> Bool isNotTypeSynonym state con = case unifyExpand state con of Right _ -> False Left _ -> True -- expand all type synonyms, so that none is left in result fullyExpand :: IntState -> NT -> NT fullyExpand state t = case expandAll state t of NTstrict t -> NTstrict (fullyExpand state t) NTapp t1 t2 -> NTapp (fullyExpand state t1) (fullyExpand state t2) NTcons id k ts -> NTcons id k (map (fullyExpand state) ts) t -> t {- If tcon is a type synoym, then unifyExpand returns the depth and the definition body of the type synoym. Otherwise it returns the printable name of tcon. -} unifyExpand :: IntState -> Id -> Either String (Int,NewType) unifyExpand state tcon = case lookupIS state tcon of Just info -> case depthI info of Just d -> Right (d,ntI info) Nothing -> Left (show (tidI info)) {- Replaces the free variables of the type given as first argument by the types given as second argument. Precondition: no free variable of the first type should occur in any of the other types (ids of their free variables should be higher). Otherwise evaluation of the function will not terminate. -} expand :: NewType -> [NT] -> NT expand (NewType free [] ctxs [nt]) ts = subst (list2Subst (zip free ts)) nt {- Extends substitution by subtitution of `t' for `tvn'. Performs occurrence check and assures that replacement of `tvn' is a type variable, if `t' expands to a type variable. -} extendV :: IntState -> Map.Map Id NT -> Id -> NT -> Either (Map.Map Id NT, String) (Map.Map Id NT) extendV state phi tvn t = let t' = expandAll state t in case anyVarNT t' of Just tvn' -> if tvn' == tvn then Right phi else Right (addSubst phi tvn t') Nothing -> if tvn `elem` freeNT t' then let t'' = fullyExpand state t' -- expansion may have less free variables in if tvn `elem` freeNT t'' then Left (phi,"(type-variable occurrence check fails)") else Right (addSubst phi tvn t'') else Right (addSubst phi tvn t) -- do not expand unnecessarily