haskell-names-0.9.7: src/Language/Haskell/Names/RecordWildcards.hs
-- Wildcards are tricky, they deserve a module of their own
{-# LANGUAGE NamedFieldPuns, TupleSections #-}
module Language.Haskell.Names.RecordWildcards where
import qualified Data.Map as Map
import Data.Maybe
import Control.Monad
import Language.Haskell.Exts
import Language.Haskell.Names.Types
import Language.Haskell.Names.SyntaxUtils
import qualified Language.Haskell.Names.GlobalSymbolTable as Global
import qualified Language.Haskell.Names.LocalSymbolTable as Local
import Data.List (nub)
-- | Information about the names being introduced by a record wildcard
--
-- During resolving traversal, we always (lazily) construct this list when
-- we process PRec or RecConstr, even if it doesn't contain a wildcard.
--
-- Then, if the pattern or construction actually contains a wildcard, we use the computed value.
type WcNames = [WcField]
-- | Information about a field in the wildcard
data WcField = WcField
{ wcFieldName :: Name ()
-- ^ the field's simple name
, wcFieldSymbol :: Symbol
-- ^ the field's selector symbol
, wcExistsGlobalValue :: Bool
-- ^ whether there is a global value in scope with the same name as
-- the field but different from the field selector
}
getElidedFields
:: Global.Table
-> QName l
-> [Name l] -- mentioned field names
-> WcNames
getElidedFields globalTable con fields =
let
givenFieldNames :: Map.Map (Name ()) ()
givenFieldNames =
Map.fromList . map ((, ()) . dropAnn) $ fields
-- FIXME must report error when the constructor cannot be
-- resolved
(mbConOrigName, mbTypeOrigName) =
case Global.lookupValue con globalTable of
[symbol@Constructor{}] ->
(Just $ symbolName symbol, Just $ typeName symbol)
_ -> (Nothing, Nothing)
ourFieldInfos :: [Symbol]
ourFieldInfos = nub (do
conOrigName <- maybeToList mbConOrigName
symbol@(Selector {constructors}) <- concat (Map.elems globalTable)
guard (conOrigName `elem` constructors)
return symbol)
existsGlobalValue :: Name () -> Bool
existsGlobalValue name =
case Map.lookup (UnQual () name) globalTable of
Just [symbol]
| Just typeOrigName <- mbTypeOrigName
, Selector {} <- symbol
, typeName symbol == typeOrigName
-> False -- this is the field selector
| otherwise -> True -- exists, but not this field's selector
_ -> False -- doesn't exist or ambiguous
ourFieldNames :: Map.Map (Name ()) WcField
ourFieldNames = Map.fromList (do
symbol <- ourFieldInfos
let name = symbolName symbol
wcfield = WcField
{ wcFieldName = name
, wcFieldSymbol = symbol
, wcExistsGlobalValue = existsGlobalValue name
}
return (name,wcfield))
in Map.elems $ ourFieldNames `Map.difference` givenFieldNames
nameOfPatField :: PatField l -> Maybe (Name l)
nameOfPatField pf =
case pf of
PFieldPat _ qn _ -> Just $ qNameToName qn
PFieldPun _ qn -> Just $ qNameToName qn
PFieldWildcard {} -> Nothing
nameOfUpdField :: FieldUpdate l -> Maybe (Name l)
nameOfUpdField pf =
case pf of
FieldUpdate _ qn _ -> Just $ qNameToName qn
FieldPun _ qn -> Just $ qNameToName qn
FieldWildcard {} -> Nothing
patWcNames
:: Global.Table
-> QName l
-> [PatField l]
-> WcNames
patWcNames gt con patfs =
getElidedFields gt con $
mapMaybe nameOfPatField patfs
expWcNames
:: Global.Table
-> Local.Table
-> QName l
-> [FieldUpdate l]
-> WcNames
expWcNames gt lt con patfs =
filter isInScope $
getElidedFields gt con $
mapMaybe nameOfUpdField patfs
where
isInScope field
| Right {} <- Local.lookupValue qn lt = True
| otherwise = wcExistsGlobalValue field
where
qn = UnQual () (annName (wcFieldName field))