module Helium.StaticAnalysis.Miscellaneous.ConstraintInfo where
import Helium.Main.Args (Option(..))
import Top.Types
import Top.Ordering.Tree
import Helium.Syntax.UHA_Syntax
import Helium.StaticAnalysis.Miscellaneous.UHA_Source
import Helium.Syntax.UHA_Range
import Helium.StaticAnalysis.Messages.TypeErrors
import Helium.StaticAnalysis.Messages.Messages
import Helium.StaticAnalysis.Miscellaneous.DoublyLinkedTree
import Helium.StaticAnalysis.Miscellaneous.TypeConstraints
import Top.Constraint.Information
import Top.Implementation.Overloading
import Top.Interface.Basic (ErrorLabel)
import Top.Interface.Substitution (unificationErrorLabel)
import Top.Interface.TypeInference
import Helium.Utils.Utils (internalError)
import Data.Maybe
import Data.Function
import Data.List
data ConstraintInfo =
CInfo_ { location :: String
, sources :: (UHA_Source, Maybe UHA_Source)
, localInfo :: InfoTree
, properties :: Properties
, errorMessage :: Maybe TypeError
}
instance Show ConstraintInfo where
show = location
type Properties = [Property]
data Property
= FolkloreConstraint
| ConstraintPhaseNumber Int
| HasTrustFactor Float
| FuntionBindingEdge Int
| InstantiatedTypeScheme TpScheme
| SkolemizedTypeScheme (Tps, TpScheme)
| IsUserConstraint Int Int
| WithHint (String, MessageBlock)
| ReductionErrorInfo Predicate
| FromBindingGroup
| IsImported Name
| ApplicationEdge Bool [LocalInfo]
| ExplicitTypedBinding
| ExplicitTypedDefinition Tps Name
| Unifier Int (String, LocalInfo, String)
| EscapedSkolems [Int]
| PredicateArisingFrom (Predicate, ConstraintInfo)
| TypeSignatureLocation Range
| TypePair (Tp, Tp)
class HasProperties a where
getProperties :: a -> Properties
addProperty :: Property -> a -> a
addProperties :: Properties -> a -> a
addProperty = addProperties . (:[])
addProperties = flip (foldr addProperty)
instance HasProperties Properties where
getProperties = id
addProperty = (:)
addProperties = (++)
instance HasProperties ConstraintInfo where
getProperties = properties
addProperties ps info =
info { properties = ps ++ properties info }
maybeHead :: [a] -> Maybe a
maybeHead [] = Nothing
maybeHead (a:_) = Just a
headWithDefault :: a -> [a] -> a
headWithDefault a = fromMaybe a . maybeHead
maybeReductionErrorPredicate :: HasProperties a => a -> Maybe Predicate
maybeReductionErrorPredicate a =
maybeHead [ p | ReductionErrorInfo p <- getProperties a ]
isFolkloreConstraint :: HasProperties a => a -> Bool
isFolkloreConstraint a =
not $ null [ () | FolkloreConstraint <- getProperties a ]
maybeInstantiatedTypeScheme :: HasProperties a => a -> Maybe TpScheme
maybeInstantiatedTypeScheme a =
maybeHead [ s | InstantiatedTypeScheme s <- getProperties a, not (withoutQuantors s) ]
maybeSkolemizedTypeScheme :: HasProperties a => a -> Maybe (Tps, TpScheme)
maybeSkolemizedTypeScheme info =
maybeHead [ s | SkolemizedTypeScheme s <- getProperties info ]
maybeUserConstraint :: HasProperties a => a -> Maybe (Int, Int)
maybeUserConstraint a =
maybeHead [ (x, y) | IsUserConstraint x y <- getProperties a ]
phaseOfConstraint :: HasProperties a => a -> Int
phaseOfConstraint a =
headWithDefault 5 [ i | ConstraintPhaseNumber i <- getProperties a ]
isExplicitTypedBinding :: HasProperties a => a -> Bool
isExplicitTypedBinding a =
not $ null [ () | ExplicitTypedBinding <- getProperties a ]
maybeExplicitTypedDefinition :: HasProperties a => a -> Maybe (Tps, Name)
maybeExplicitTypedDefinition a =
maybeHead [ (ms, n) | ExplicitTypedDefinition ms n <- getProperties a ]
maybeTypeSignatureLocation :: HasProperties a => a -> Maybe Range
maybeTypeSignatureLocation a =
maybeHead [ r | TypeSignatureLocation r <- getProperties a ]
maybePredicateArisingFrom :: HasProperties a => a -> Maybe (Predicate, ConstraintInfo)
maybePredicateArisingFrom a =
maybeHead [ t | PredicateArisingFrom t <- getProperties a ]
getEscapedSkolems :: HasProperties a => a -> [Int]
getEscapedSkolems a =
concat [ is | EscapedSkolems is <- getProperties a ]
childConstraint :: Int -> String -> InfoTree -> Properties -> ConstraintInfo
childConstraint childNr theLocation infoTree theProperties =
CInfo_ { location = theLocation
, sources = ( (self . attribute) infoTree
, Just $ (self . attribute . selectChild childNr) infoTree
)
, localInfo = infoTree
, properties = theProperties
, errorMessage = Nothing
}
specialConstraint :: String -> InfoTree -> (UHA_Source, Maybe UHA_Source) -> Properties -> ConstraintInfo
specialConstraint theLocation infoTree theSources theProperties =
CInfo_ { location = theLocation
, sources = theSources
, localInfo = infoTree
, properties = theProperties
, errorMessage = Nothing
}
orphanConstraint :: Int -> String -> InfoTree -> Properties -> ConstraintInfo
orphanConstraint childNr theLocation infoTree theProperties =
CInfo_ { location = theLocation
, sources = ( (self . attribute . selectChild childNr) infoTree
, Nothing
)
, localInfo = infoTree
, properties = theProperties
, errorMessage = Nothing
}
resultConstraint :: String -> InfoTree -> Properties -> ConstraintInfo
resultConstraint theLocation infoTree theProperties =
CInfo_ { location = theLocation
, sources = ( (self . attribute) infoTree
, Nothing
)
, localInfo = infoTree
, properties = theProperties
, errorMessage = Nothing
}
variableConstraint :: String -> UHA_Source -> Properties -> ConstraintInfo
variableConstraint theLocation theSource theProperties =
CInfo_ { location = theLocation
, sources = (theSource, Nothing)
, localInfo = root LocalInfo { self = theSource, assignedType = Nothing , monos = [] } []
, properties = theProperties
, errorMessage = Nothing
}
cinfoBindingGroupExplicitTypedBinding :: Tps -> Name -> Name -> ConstraintInfo
cinfoSameBindingGroup :: Name -> ConstraintInfo
cinfoBindingGroupImplicit :: Name -> ConstraintInfo
cinfoBindingGroupExplicit :: Tps -> Names -> Name -> ConstraintInfo
cinfoGeneralize :: Name -> ConstraintInfo
cinfoBindingGroupExplicitTypedBinding ms name nameTS =
let props = [ FromBindingGroup, ExplicitTypedBinding, ExplicitTypedDefinition ms name,
HasTrustFactor 10.0, TypeSignatureLocation (getNameRange nameTS) ]
in variableConstraint "explicitly typed binding" (nameToUHA_Def name) props
cinfoSameBindingGroup name =
let props = [ FromBindingGroup, FolkloreConstraint ]
in variableConstraint "variable" (nameToUHA_Expr name) props
cinfoBindingGroupImplicit name =
let props = [ FromBindingGroup, FolkloreConstraint, HasTrustFactor 10.0 ]
in variableConstraint "variable" (nameToUHA_Expr name) props
cinfoBindingGroupExplicit ms defNames name =
let props1 = [ FromBindingGroup, FolkloreConstraint ]
props2 = case filter (name==) defNames of
[defName] -> [ExplicitTypedDefinition ms defName]
_ -> []
in variableConstraint "variable" (nameToUHA_Expr name) (props1 ++ props2)
cinfoGeneralize name =
variableConstraint ("Generalize " ++ show name) (nameToUHA_Expr name) []
type InfoTrees = [InfoTree]
type InfoTree = DoublyLinkedTree LocalInfo
data LocalInfo =
LocalInfo { self :: UHA_Source
, assignedType :: Maybe Tp
, monos :: Tps
}
typeSchemesInInfoTree :: FixpointSubstitution -> Predicates -> InfoTree -> [(Range, TpScheme)]
typeSchemesInInfoTree subst ps infoTree =
let local = attribute infoTree
rest = concatMap (typeSchemesInInfoTree subst ps) (children infoTree)
in case assignedType local of
Just tp -> let is = ftv tp
ps' = filter (any (`elem` is) . ftv) ps
scheme = generalizeAll (subst |-> (ps' .=>. tp))
in (rangeOfSource (self local), scheme) : rest
Nothing -> rest
type ConstraintSet = Tree (TypeConstraint ConstraintInfo)
type ConstraintSets = Trees (TypeConstraint ConstraintInfo)
instance TypeConstraintInfo ConstraintInfo where
unresolvedPredicate = addProperty . ReductionErrorInfo
ambiguousPredicate = addProperty . ReductionErrorInfo
escapedSkolems = addProperty . EscapedSkolems
predicateArisingFrom = addProperty . PredicateArisingFrom
equalityTypePair = setTypePair
instance PolyTypeConstraintInfo ConstraintInfo where
instantiatedTypeScheme = addProperty . InstantiatedTypeScheme
skolemizedTypeScheme = addProperty . SkolemizedTypeScheme
highlyTrustedFactor :: Float
highlyTrustedFactor = 10000.0
highlyTrusted :: Property
highlyTrusted = HasTrustFactor highlyTrustedFactor
isHighlyTrusted :: ConstraintInfo -> Bool
isHighlyTrusted info =
product [ i | HasTrustFactor i <- properties info ] >= highlyTrustedFactor
setTypePair :: (Tp, Tp) -> ConstraintInfo -> ConstraintInfo
setTypePair pair = addProperty (TypePair pair)
typepair :: ConstraintInfo -> (Tp, Tp)
typepair info = fromJust (maybeHead [ pair | TypePair pair <- getProperties info ])
isExprTyped :: ConstraintInfo -> Bool
isExprTyped info =
case fst (sources info) of
UHA_Expr (Expression_Typed _ _ _) -> True
_ -> False
tooGeneralLabels :: [ErrorLabel]
tooGeneralLabels = [skolemVersusConstantLabel, skolemVersusSkolemLabel, escapingSkolemLabel]
makeTypeErrors :: Substitution sub => [Option] -> ClassEnvironment -> OrderedTypeSynonyms -> sub -> [(ConstraintInfo, ErrorLabel)] -> TypeErrors
makeTypeErrors options classEnv synonyms sub errors =
let
list = groupBy ((==) `on` snd)
$ sortBy (compare `on` snd)
$ (if NoOverloadingTypeCheck `elem` options
then filter ((/= ambiguousLabel) . snd)
else id)
errors
final = groupBy ((==) `on` fst)
$ sortBy (compare `on` fst)
$ filter (not . null . snd)
[ make label (info : map fst rest) | (info, label):rest <- list ]
in case final of
[] -> []
hd:_ -> concatMap snd hd
where
special :: ConstraintInfo -> TypeError -> TypeError
special info defaultMessage =
maybe defaultMessage (sub |->) (maybeSpecialTypeError info)
make :: ErrorLabel -> [ConstraintInfo] -> (Int, TypeErrors)
make label infos
| label == unificationErrorLabel =
let f info =
case mguWithTypeSynonyms synonyms (sub |-> fst (typepair info)) (sub |-> snd (typepair info)) of
Left (InfiniteType _) ->
let hint = ("because", MessageString "unification would give infinite type")
in [ sub |-> special info (makeUnificationTypeError (addProperty (WithHint hint) info)) ]
Left _ ->
[ sub |-> special info (makeUnificationTypeError info) ]
Right _ -> []
in (1, concatMap f infos)
| label == missingInSignatureLabel =
let f info =
let (p, infoArising) = fromMaybe err (maybePredicateArisingFrom info)
range = fromMaybe err (maybeTypeSignatureLocation info)
mSource = if isExprTyped info then Nothing else Just (fst $ sources info)
scheme = maybe err snd (maybeSkolemizedTypeScheme info)
t1 = freezeVariablesInType (unqualify (unquantify scheme))
t2 = sub |-> fst (typepair info)
tuple = case mguWithTypeSynonyms synonyms t1 t2 of
Left _ -> (False, p)
Right (_, sub1) ->
let Predicate className tp = sub1 |-> p
sub' = listToSubstitution [ (i, TCon s) | (i, s) <- getQuantorMap scheme ]
in (True, Predicate className (sub' |-> unfreezeVariablesInType tp))
err = internalError "ConstraintInfo" "makeTypeErrors" "unknown class predicate"
in special info (makeMissingConstraintTypeError range mSource scheme tuple (fst $ sources infoArising))
in (2, map f infos)
| label `elem` tooGeneralLabels =
let f info =
let monoset = sub |-> ms
range = fromMaybe err (maybeTypeSignatureLocation info)
scheme1 = generalize monoset ([] .=>. sub |-> snd (typepair info))
(ms, scheme2) = fromMaybe err (maybeSkolemizedTypeScheme info)
source = uncurry fromMaybe (sources info)
err = internalError "ConstraintInfo" "makeTypeErrors" "unknown original type scheme"
in special info (makeNotGeneralEnoughTypeError (isExprTyped info) range source scheme1 scheme2)
in (if label == escapingSkolemLabel then 3 else 2, map f infos)
| label == unresolvedLabel =
let f info =
let source = fst (sources info)
extra = case maybeInstantiatedTypeScheme info of
Just scheme ->
Left (scheme, snd $ typepair info)
Nothing ->
Right (location info, sub |-> assignedType (attribute (localInfo info)))
pred' = let err = internalError "ConstraintInfo" "makeTypeErrors"
"unknown predicate which resulted in a reduction error"
in fromMaybe err $ maybeReductionErrorPredicate info
in special info (sub |-> makeReductionError source extra classEnv pred')
in (4, map f infos)
| label == ambiguousLabel =
let f info =
let scheme1 = fromMaybe err (maybeInstantiatedTypeScheme info)
scheme2 = generalizeAll ([] .=>. sub |-> fst (typepair info))
className = maybe err (\(Predicate x _) -> x) (maybeReductionErrorPredicate info)
err = internalError "ConstraintInfo" "makeTypeErrors" "unknown original type scheme"
in special info (makeUnresolvedOverloadingError (fst $ sources info) className (scheme1, scheme2))
in (5, map f infos)
| otherwise =
internalError "ConstraintInfo" "makeTypeErrors" ("unknown label "++show label)
makeUnificationTypeError :: ConstraintInfo -> TypeError
makeUnificationTypeError info =
let (source, term) = sources info
range = maybe (rangeOfSource source) rangeOfSource term
oneliner = MessageOneLiner (MessageString ("Type error in " ++ location info))
(t1, t2) = typepair info
msgtp1 = fromMaybe (toTpScheme t1) (maybeInstantiatedTypeScheme info)
msgtp2 = maybe (toTpScheme t2) snd (maybeSkolemizedTypeScheme info)
(reason1, reason2)
| isJust (maybeSkolemizedTypeScheme info) = ("inferred type", "declared type")
| isFolkloreConstraint info = ("type" , "expected type")
| otherwise = ("type" , "does not match")
table = [ s <:> MessageOneLineTree (oneLinerSource source') | (s, source') <- convertSources (sources info)]
++
[ reason1 >:> MessageType msgtp1
, reason2 >:> MessageType msgtp2
]
hints = [ hint | WithHint hint <- properties info ]
in TypeError [range] [oneliner] table hints
emptyConstraintInfo :: ConstraintInfo
emptyConstraintInfo =
CInfo_ { location = "Typing Strategy"
, sources = (UHA_Decls [], Nothing)
, localInfo = root (LocalInfo (UHA_Decls []) Nothing []) []
, properties = []
, errorMessage = Nothing
}
defaultConstraintInfo :: (UHA_Source, Maybe UHA_Source) -> ConstraintInfo
defaultConstraintInfo sourceTuple@(s1, s2) =
CInfo_ { location = descriptionOfSource theSource
, sources = sourceTuple
, localInfo = root myLocal []
, properties = []
, errorMessage = Nothing
}
where myLocal = LocalInfo {self = theSource, assignedType = Nothing, monos = []}
theSource = fromMaybe s1 s2
standardConstraintInfo :: ConstraintInfo
standardConstraintInfo =
CInfo_ { location = "Typing Strategy"
, sources = (UHA_Decls [], Nothing)
, localInfo = root myLocal []
, properties = [ ]
, errorMessage = Nothing
}
where myLocal = LocalInfo {self = UHA_Decls [], assignedType = Nothing, monos = []}
maybeSpecialTypeError :: ConstraintInfo -> Maybe TypeError
maybeSpecialTypeError = errorMessage
setTypeError :: TypeError -> ConstraintInfo -> ConstraintInfo
setTypeError typeError info =
info { errorMessage = Just typeError }