module Statics.Sealing ( sealWith, replaceTyCons, getSigTyCons, getSigItemTyCons, ) where import Util import qualified AST import Type import Statics.Constraint import Statics.Env as Env import Statics.Error import Statics.Subsume import Prelude () import qualified Data.Map as M -- | Perform generative signature matching sealWith ∷ MonadConstraint tv r m ⇒ [ModId] → Signature tv → Signature tv → m (Signature tv) sealWith μ sig0 sig1 = do let sig1' = renameSig (makeNameMap sig0) μ sig1 γ0 = sigToEnv sig0 tcsubst ← matchSigTycons γ0 sig1' subsumeSig γ0 (applyTCSInTyCon tcsubst sig1') let tcs = getGenTycons sig1' tcs' ← for tcs $ \tc → do ix ← tvUniqueID <$> newTV return tc { tcId = ix } return (substTyCons tcs tcs' sig1') -- | For mapping renamed names (from structures) into unrenamed names -- (in signatures) data NameMap = NameMap { nmVar ∷ Env VarId VarId, nmCon ∷ Env ConId ConId, nmTyp ∷ Env TypId TypId, nmMod ∷ Env ModId (ModId, NameMap), nmSig ∷ Env SigId SigId } instance Monoid NameMap where mempty = NameMap empty empty empty empty empty mappend (NameMap a1 a2 a3 a4 a5) (NameMap b1 b2 b3 b4 b5) = NameMap (a1 =+= b1) (a2 =+= b2) (a3 =+= b3) (a4 =+= b4) (a5 =+= b5) instance GenEmpty NameMap where genEmpty = mempty instance GenExtend NameMap NameMap where (=+=) = mappend instance GenLookup NameMap VarId VarId where e =..= k = nmVar e =..= k instance GenLookup NameMap ConId ConId where e =..= k = nmCon e =..= k instance GenLookup NameMap TypId TypId where e =..= k = nmTyp e =..= k instance GenLookup NameMap ModId (ModId, NameMap) where e =..= k = nmMod e =..= k instance GenLookup NameMap SigId SigId where e =..= k = nmSig e =..= k -- | Given a signature, construct a 'NameMap' mapping trivially-renamed -- versions of its names to the actual renamed version. makeNameMap ∷ Signature tv → NameMap makeNameMap = foldMap eachItem where eachItem (SgVal n _) = mempty { nmVar = unTag n =:= n } eachItem (SgTyp n tc) = mempty { nmTyp = unTag n =:= n, nmCon = Env.fromList ((unTag &&& id) <$> Env.domain (tcCons tc)) } eachItem (SgExn n _) = mempty { nmCon = unTag n =:= n } eachItem (SgSig n _) = mempty { nmSig = unTag n =:= n } eachItem (SgMod n sig) = mempty { nmMod = unTag n =:= (n, makeNameMap sig) } -- unTag ∷ AST.Id a ⇒ a R → a R unTag = AST.renId bogus -- | Make the names in a signature match the names from the module it's -- being applied to. renameSig ∷ NameMap → [ModId] → Signature tv → Signature tv renameSig nm μ = map eachItem where eachItem (SgVal n σ) = SgVal (nm !..! n) σ eachItem (SgTyp n tc) = SgTyp (nm !..! n) tc' where tc' = tc { tcName = J (reverse μ) (jname (tcName tc)), tcCons = Env.fromList (first (nm !..!) <$> Env.toList (tcCons tc)) } eachItem (SgExn n mσ) = SgExn (nm !..! n) mσ eachItem (SgMod n sig) = SgMod n' sig' where (n', nm') = nm !..! n sig' = renameSig nm' (n':μ) sig eachItem (SgSig n sig) = SgSig (nm !..! n) sig -- | Given a signature, find the tycon substitutions necessary to -- unify it with the module in the environment. matchSigTycons ∷ MonadConstraint tv r m ⇒ Γ tv → Signature tv → m TyConSubst matchSigTycons γ = execWriterT . eachSig [] where eachSig μ = mapM_ (eachItem μ) eachItem μ sigitem = case sigitem of SgVal _ _ → return () SgTyp n tc → do tc' ← γ !.! J (reverse μ) n tell (makeTyConSubst [tc] [tc']) SgExn _ _ → return () SgMod n sig → eachSig (n:μ) sig SgSig _ _ → return () -- | Check whether the given signature subsumes the signature -- implicit in the environment. subsumeSig ∷ MonadConstraint tv r m ⇒ Γ tv → Signature tv → m () subsumeSig γ = eachSig where eachSig = mapM_ eachItem eachItem sg0 = case sg0 of SgVal n σ → do σ' ← γ !.! n σ' ≤ σ `addErrorContext` [msg| In signature matching, type mismatch for value binding $q:n. |] SgTyp n tc → do tc' ← γ !.! n case varietyOf tc of OperatorType → matchTyCons tc' tc DataType → matchTyCons tc' tc AbstractType → do let sigAss assertion thing getter = tAssExp assertion ([msg| In signature matching, cannot match the definition for type $q:1 because the $words:thing does not match: |] (tcName tc)) (showMsg (getter tc')) (showMsg (getter tc)) sigAss (length (tcArity tc') == length (tcArity tc)) "number of type parameters" (length . tcArity) sigAss (all2 (⊑) (tcArity tc') (tcArity tc)) "variance" tcArity sigAss (all2 (⊒) (tcBounds tc') (tcBounds tc)) "parameter bounds" tcBounds sigAss (tcQual tc' ⊑ tcQual tc) "qualifier" tcQual SgExn n mσ → do emσ' ← γ !.! n case emσ' of Left _ → typeBug "subsumeSig" "Datacon where exn expected" Right mσ' → matchParamType mσ' mσ [msg| exception $q:n |] SgMod n sig → do (_, γ') ← γ !.! n subsumeSig γ' sig SgSig n sig → do (sig', _) ← γ !.! n matchSigs sig' sig -- | Check that exception parameter types match, given the constructor -- name, the actual type, and the expected type. matchParamType ∷ MonadConstraint tv r m ⇒ Maybe (Type Empty) → Maybe (Type Empty) → Message H → m () matchParamType mσ mσ' what = case (mσ, mσ') of (Nothing, Nothing) → return () (Just σ, Just σ') → elimEmptyF σ =: elimEmptyF σ' `addErrorContext` [msg| In signature matching, type mismatch in parameter of $msg:what |] _ → tErrExp [msg| In signature matching, parameter of $msg:what does not match |] (maybe [msg| no parameter |] pprMsg mσ) (maybe [msg| no parameter |] pprMsg mσ') -- | Check that two signatures match EXACTLY. -- First signature is what we have, and second is what we want. matchSigs ∷ MonadConstraint tv r m ⇒ Signature tv → Signature tv → m () matchSigs = loop where loop [] [] = return () loop (SgVal n1 σ1 : sgs1) (SgVal n2 σ2 : sgs2) | n1 == n2 = do σ1 =: σ2 `addErrorContext` [msg| In matching signatures, types do not match for value binding $q:n1. |] loop sgs1 sgs2 loop (SgTyp n1 tc1 : sgs1) (SgTyp n2 tc2 : sgs2) | n1 == n2 = do matchTyCons tc2 tc1 loop (substTyCon tc1 tc2 sgs1) sgs2 loop (SgExn n1 mσ1 : sgs1) (SgExn n2 mσ2 : sgs2) | n1 == n2 = do matchParamType mσ2 mσ1 [msg| exception $q:n1 |] loop sgs1 sgs2 loop (SgMod n1 sig1 : sgs1) (SgMod n2 sig2 : sgs2) | n1 == n2 = do matchSigs sig1 sig2 loop sgs1 sgs2 loop (SgSig n1 sig1 : sgs1) (SgSig n2 sig2 : sgs2) | n1 == n2 = do matchSigs sig1 sig2 loop sgs1 sgs2 loop [] (sg : _) = do (n, what) ← whatIs sg typeError [msg| In exact signature matching, missing expected $what $qmsg:n. |] loop (sg : _) [] = do (n, what) ← whatIs sg typeError [msg| In exact signature matching, found unexpected $what $qmsg:n. |] loop (sg1 : _) (sg2 : _) = do (n1, what1) ← whatIs sg1 (n2, what2) ← whatIs sg2 typeError [msg| In exact signature matching (for signatures as entries in signatures being matched), got signature items didn’t match:
actual:
$what1 $qmsg:n1
expected:
$what2 $qmsg:n2
|] -- whatIs (SgVal n _) = return (pprMsg n, "value") whatIs (SgTyp n _) = return (pprMsg n, "type") whatIs (SgExn n _) = return (pprMsg n, "exception") whatIs (SgMod n _) = return (pprMsg n, "module") whatIs (SgSig n _) = return (pprMsg n, "module type") -- | Get a list of all the tycons that need a new index allocated -- because they're generative. getGenTycons ∷ Signature tv → [TyCon] getGenTycons = execWriter . eachSig where eachSig = mapM_ eachItem eachItem sg0 = case sg0 of SgVal _ _ → return () SgTyp _ tc → unless (varietyOf tc == OperatorType) (tell [tc]) SgExn _ _ → return () SgMod _ sig → eachSig sig SgSig _ _ → return () -- | Check that two type constructors match exactly. matchTyCons ∷ MonadConstraint tv r m ⇒ TyCon → TyCon → m () matchTyCons tc1 tc2 = case (varietyOf tc1, varietyOf tc2) of (AbstractType, AbstractType) → do tcArity tc1 ==! tcArity tc2 $ "arity or variance" tcBounds tc1 ==! tcBounds tc2 $ "parameter bound" tcGuards tc1 ==! tcGuards tc2 $ "guarded parameters" tcQual tc1 ==! tcQual tc2 $ "qualifier" (DataType, DataType) → do length (tcArity tc1) ==! length (tcArity tc2) $ "number of parameters" let rhs1 = tcCons tc1 rhs2 = tcCons tc2 forM_ (Env.toList rhs1) $ \(k, mσ1) → do mσ2 ← rhs2 !.! k matchParamType mσ2 mσ1 [msg| constructor $q:k |] (OperatorType, _) | tyconExtEq tc1 tc2 → return () (_, OperatorType) | tyconExtEq tc1 tc2 → return () (OperatorType, OperatorType) → do let next1 = fromMaybe [] (tcNext tc1) next2 = fromMaybe [] (tcNext tc2) ncs1 = length next1 ncs2 = length next1 ncs1 ==! ncs2 $ "number of clauses" forM_ (zip3 next1 next2 [1 ∷ Int .. ]) $ \((tp1, σ1), (tp2, σ2), ix) → do length tp1 ==! length tp2 $ if ncs1 == 1 then "number of type parameters" else "number of parameters else in clause " ++ show ix zipWithM_ matchTyPats tp1 tp2 σ1 ==! σ2 $ if ncs1 == 1 then "type synonym right-hand sides" else "type operator right-hand sides in clause " ++ show ix (v1, v2) → v1 ==! v2 $ "kind of definition" where (a1 ==! a2) what = tAssExp (a1 == a2) ([msg| In signature matching, cannot match definition for type $q:1 because the $words:what does not match: |] (tcName tc1)) (pprMsg a1) (pprMsg a2) -- | Extensional equality for type constructors. -- This is probably too weak. tyconExtEq ∷ TyCon → TyCon → Bool tyconExtEq tc1 tc2 | tcBounds tc1 == tcBounds tc2 = let tvs = fvTy <$> [ 1 .. length (tcArity tc1) ] in TyApp tc1 tvs == TyApp tc2 tvs tyconExtEq _ _ = False -- | To check that two type patterns match, and return the pairs of -- type variables that line up and thus need renaming. matchTyPats ∷ MonadAlmsError m ⇒ TyPat → TyPat → m () matchTyPats (TpVar _) (TpVar _) = return () matchTyPats (TpRow _) (TpRow _) = return () matchTyPats (TpApp tc1 tvs1) (TpApp tc2 tvs2) | tc1 == tc2 = zipWithM_ matchTyPats tvs1 tvs2 matchTyPats tp1 tp2 = tErrExp [msg| In signature matching, cannot match type patterns: |] (pprMsg tp1) (pprMsg tp2) --- --- TYPE CONSTRUCTOR SUBSTITUTION --- -- | A substitution mapping type constructors to other type -- constructors type TyConSubst = M.Map Int TyCon -- | Construct a tycon substitution from a list of tycons and a list -- to map them to. makeTyConSubst ∷ [TyCon] → [TyCon] → TyConSubst makeTyConSubst tcs tcs' = M.fromList (zip (tcId <$> tcs) tcs') class SubstTyCon a where applyTCS, applyTCSInTyCon ∷ TyConSubst → a → a applyTCSInTyCon = applyTCS instance SubstTyCon a ⇒ SubstTyCon (Maybe a) where applyTCS = fmap . applyTCS applyTCSInTyCon = fmap . applyTCSInTyCon instance SubstTyCon a ⇒ SubstTyCon [a] where applyTCS = fmap . applyTCS applyTCSInTyCon = fmap . applyTCSInTyCon instance SubstTyCon v ⇒ SubstTyCon (Env k v) where applyTCS = fmap . applyTCS applyTCSInTyCon = fmap . applyTCSInTyCon instance (SubstTyCon a, SubstTyCon b) ⇒ SubstTyCon (a, b) where applyTCS s = applyTCS s *** applyTCS s applyTCSInTyCon s = applyTCSInTyCon s *** applyTCSInTyCon s instance (SubstTyCon a, SubstTyCon b, SubstTyCon c) ⇒ SubstTyCon (a, b, c) where applyTCS s (a, b, c) = (applyTCS s a, applyTCS s b, applyTCS s c) applyTCSInTyCon s (a, b, c) = (applyTCSInTyCon s a, applyTCSInTyCon s b, applyTCSInTyCon s c) instance (SubstTyCon a, SubstTyCon b) ⇒ SubstTyCon (Either a b) where applyTCS s = applyTCS s +++ applyTCS s applyTCSInTyCon s = applyTCSInTyCon s +++ applyTCSInTyCon s instance SubstTyCon TyCon where applyTCS s tc | Just tc' ← M.lookup (tcId tc) s = applyTCSInTyCon s tc' | otherwise = applyTCSInTyCon s tc applyTCSInTyCon s tc = tc { tcNext = applyTCS s (tcNext tc), tcCons = applyTCS s (tcCons tc) } instance SubstTyCon TyPat where applyTCS s tp0 = case tp0 of TpVar _ → tp0 TpRow _ → tp0 TpApp tc σs → TpApp (applyTCS s tc) (applyTCS s σs) instance SubstTyCon (Type tv) where applyTCS s σ0 = case σ0 of TyVar _ → σ0 TyQu qu αs σ → TyQu qu αs (applyTCS s σ) TyMu α σ → TyMu α (applyTCS s σ) TyRow lab σ1 σ2 → TyRow lab (applyTCS s σ1) (applyTCS s σ2) TyApp tc σs → TyApp (applyTCS s tc) (applyTCS s σs) instance SubstTyCon (SigItem tv) where applyTCS s sg0 = case sg0 of SgVal n σ → SgVal n (applyTCS s σ) SgTyp n tc → SgTyp n (applyTCS s tc) SgExn n mσ → SgExn n (applyTCS s mσ) SgMod n sig → SgMod n (applyTCS s sig) SgSig n sig → SgSig n (applyTCS s sig) applyTCSInTyCon s sg0 = case sg0 of SgVal n σ → SgVal n (applyTCS s σ) SgTyp n tc → SgTyp n (applyTCSInTyCon s tc) SgExn n mσ → SgExn n (applyTCS s mσ) SgMod n sig → SgMod n (applyTCSInTyCon s sig) SgSig n sig → SgSig n (applyTCS s sig) -- | Replace all the type constructors whose indices match the ones in the -- list with the ones in the list. replaceTyCons ∷ SubstTyCon a ⇒ [TyCon] → a → a replaceTyCons tcs = substTyCons tcs tcs -- Give a list of tycons to replace and a list of tycons to replace them -- with, replaces them all recursively, including knot-tying substTyCons ∷ SubstTyCon a ⇒ [TyCon] → [TyCon] → a → a substTyCons tcs tcs' = applyTCS (makeTyConSubst tcs tcs') -- | Replace all occurrences of the first tycon with the second substTyCon ∷ SubstTyCon a ⇒ TyCon → TyCon → a → a substTyCon tc tc' = substTyCons [tc] [tc'] -- | Get all the tycons that are bound in a signature getSigTyCons ∷ Signature tv → [TyCon] getSigTyCons = concatMap getSigItemTyCons -- | Get all the tycons that are bound in a signature item getSigItemTyCons ∷ SigItem tv → [TyCon] getSigItemTyCons (SgVal _ _) = [] getSigItemTyCons (SgTyp _ tc) = [tc] getSigItemTyCons (SgExn _ _) = [] getSigItemTyCons (SgMod _ sig) = getSigTyCons sig getSigItemTyCons (SgSig _ sig) = getSigTyCons sig