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: