{-# LANGUAGE FlexibleContexts #-}

{-| Module      :  TieBreakerHeuristics
    License     :  GPL

    Maintainer  :  helium@cs.uu.nl
    Stability   :  experimental
    Portability :  portable
    
    A tie-breaker heuristic will be used if all other heuristics cannot decide on
    which error to report. 
-}

module Helium.StaticAnalysis.Heuristics.TieBreakerHeuristics where

import Top.Types
import Top.Interface.TypeInference (getTypeSynonyms)
import Top.Implementation.TypeGraph.Heuristic

-----------------------------------------------------------------------------

class HasTrustFactor a where
   trustFactor :: a -> Float

avoidTrustedConstraints :: HasTrustFactor info => Heuristic info
avoidTrustedConstraints = 
   Heuristic (
      let f (_, info) = return (trustFactor info)
      in minimalEdgeFilter "Trust factor of edge" f)

-----------------------------------------------------------------------------

class HasDirection a where
   isTopDown :: a -> Bool

-- note: because True > False, we use the minimal edge filter to keep
--       all the top down edges
avoidFolkloreConstraints :: HasDirection info => Heuristic info
avoidFolkloreConstraints = 
   Heuristic (
      let f (_, info) = return (isTopDown info)
      in minimalEdgeFilter "Is a top down edge" f)

-----------------------------------------------------------------------------

-- no "unification-around-a-corner-type-error"
typeVariableInvolved :: HasTwoTypes info => Heuristic info
typeVariableInvolved = 
   let f pair@(_, info) = 
          doWithoutEdge pair $
         do typeTuple <- getSubstitutedTypes info
            synonyms  <- getTypeSynonyms
            case typeTuple of
               (Just t1, Just t2) ->
                  let i = nextFTV (t1, t2)
                      (i1, t1') = changeTypeVariables i  t1
                      (_ , t2') = changeTypeVariables i1 t2
                  in return (not (unifiable synonyms t1' t2'))
               _ -> return True
   in Heuristic (edgeFilter "type variable involved" f)
   
changeTypeVariables :: Int -> Tp -> (Int, Tp)
changeTypeVariables i tp = 
   case tp of
      TVar _   -> (i+1, TVar i)
      TCon s   -> (i, TCon s)
      TApp l r -> let (i1, l') = changeTypeVariables i  l
                      (i2, r') = changeTypeVariables i1 r
                  in (i2, TApp l' r')
