From b2442ead2cf1e50700cf5db945a08824a6cdcbaf Mon Sep 17 00:00:00 2001 From: Gardanta Spirito Date: Thu, 17 Aug 2023 02:34:25 +0300 Subject: [PATCH 1/2] Refactor substituteType --- src/Grace/Infer.hs | 36 ++++----- src/Grace/Type.hs | 193 ++++++++++----------------------------------- 2 files changed, 59 insertions(+), 170 deletions(-) diff --git a/src/Grace/Infer.hs b/src/Grace/Infer.hs index 3cca626..2d32a30 100644 --- a/src/Grace/Infer.hs +++ b/src/Grace/Infer.hs @@ -326,28 +326,28 @@ subtype _A0 _B0 = do -- <:∃R (_, Type.Exists{ domain = Domain.Type, .. }) -> do scopedUnsolvedType nameLocation \a -> - subtype _A0 (Type.substituteType name 0 a type_) + subtype _A0 (Type.substituteType name a type_) (_, Type.Exists{ domain = Domain.Fields, .. }) -> do scopedUnsolvedFields \a -> do - subtype _A0 (Type.substituteFields name 0 a type_) + subtype _A0 (Type.substituteFields name a type_) (_, Type.Exists{ domain = Domain.Alternatives, .. }) -> do scopedUnsolvedAlternatives \a -> do - subtype _A0 (Type.substituteAlternatives name 0 a type_) + subtype _A0 (Type.substituteAlternatives name a type_) -- <:∀L (Type.Forall{ domain = Domain.Type, .. }, _) -> do scopedUnsolvedType nameLocation \a -> do - subtype (Type.substituteType name 0 a type_) _B0 + subtype (Type.substituteType name a type_) _B0 (Type.Forall{ domain = Domain.Fields, .. }, _) -> do scopedUnsolvedFields \a -> do - subtype (Type.substituteFields name 0 a type_) _B0 + subtype (Type.substituteFields name a type_) _B0 (Type.Forall{ domain = Domain.Alternatives, .. }, _) -> do scopedUnsolvedAlternatives \a -> do - subtype (Type.substituteAlternatives name 0 a type_) _B0 + subtype (Type.substituteAlternatives name a type_) _B0 (Type.Scalar{ scalar = s0 }, Type.Scalar{ scalar = s1 }) | s0 == s1 -> do @@ -791,13 +791,13 @@ instantiateTypeL a _A0 = do -- InstLExt Type.Exists{ domain = Domain.Type, .. } -> do scopedUnsolvedType nameLocation \b -> do - instantiateTypeR (Type.substituteType name 0 b type_) a + instantiateTypeR (Type.substituteType name b type_) a Type.Exists{ domain = Domain.Fields, .. } -> do scopedUnsolvedFields \b -> do - instantiateTypeR (Type.substituteFields name 0 b type_) a + instantiateTypeR (Type.substituteFields name b type_) a Type.Exists{ domain = Domain.Alternatives, .. } -> do scopedUnsolvedAlternatives \b -> do - instantiateTypeR (Type.substituteAlternatives name 0 b type_) a + instantiateTypeR (Type.substituteAlternatives name b type_) a -- InstLArr Type.Function{..} -> do @@ -966,13 +966,13 @@ instantiateTypeR _A0 a = do -- InstRAllL Type.Forall{ domain = Domain.Type, .. } -> do scopedUnsolvedType nameLocation \b -> do - instantiateTypeR (Type.substituteType name 0 b type_) a + instantiateTypeR (Type.substituteType name b type_) a Type.Forall{ domain = Domain.Fields, .. } -> do scopedUnsolvedFields \b -> do - instantiateTypeR (Type.substituteFields name 0 b type_) a + instantiateTypeR (Type.substituteFields name b type_) a Type.Forall{ domain = Domain.Alternatives, .. } -> do scopedUnsolvedAlternatives \b -> do - instantiateTypeR (Type.substituteAlternatives name 0 b type_) a + instantiateTypeR (Type.substituteAlternatives name b type_) a Type.Optional{..} -> do let _ΓL = _Γ @@ -1890,13 +1890,13 @@ check Syntax.Lambda{ location = _, ..} Type.Function{..} = do -- ∃I check e Type.Exists{ domain = Domain.Type, .. } = do scopedUnsolvedType nameLocation \a -> do - check e (Type.substituteType name 0 a type_) + check e (Type.substituteType name a type_) check e Type.Exists{ domain = Domain.Fields, .. } = do scopedUnsolvedFields \a -> do - check e (Type.substituteFields name 0 a type_) + check e (Type.substituteFields name a type_) check e Type.Exists{ domain = Domain.Alternatives, .. } = do scopedUnsolvedAlternatives \a -> do - check e (Type.substituteAlternatives name 0 a type_) + check e (Type.substituteAlternatives name a type_) -- ∀I check e Type.Forall{..} = do @@ -2012,7 +2012,7 @@ inferApplication Type.Forall{ domain = Domain.Type, .. } e = do let a' = Type.UnsolvedType{ location = nameLocation, existential = a} - inferApplication (Type.substituteType name 0 a' type_) e + inferApplication (Type.substituteType name a' type_) e inferApplication Type.Forall{ domain = Domain.Fields, .. } e = do a <- fresh @@ -2020,7 +2020,7 @@ inferApplication Type.Forall{ domain = Domain.Fields, .. } e = do let a' = Type.Fields [] (Monotype.UnsolvedFields a) - inferApplication (Type.substituteFields name 0 a' type_) e + inferApplication (Type.substituteFields name a' type_) e inferApplication Type.Forall{ domain = Domain.Alternatives, .. } e = do a <- fresh @@ -2028,7 +2028,7 @@ inferApplication Type.Forall{ domain = Domain.Alternatives, .. } e = do let a' = Type.Alternatives [] (Monotype.UnsolvedAlternatives a) - inferApplication (Type.substituteAlternatives name 0 a' type_) e + inferApplication (Type.substituteAlternatives name a' type_) e -- ∃App inferApplication Type.Exists{..} e = do diff --git a/src/Grace/Type.hs b/src/Grace/Type.hs index 58268bf..cf8a381 100644 --- a/src/Grace/Type.hs +++ b/src/Grace/Type.hs @@ -1,10 +1,12 @@ {-# LANGUAGE ApplicativeDo #-} +{-# LANGUAGE BlockArguments #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveLift #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} @@ -36,7 +38,7 @@ module Grace.Type , prettyTextLiteral ) where -import Control.Lens (Plated(..)) +import Control.Lens (Plated(..), (%~)) import Data.Bifunctor (Bifunctor(..)) import Data.Generics.Product (the) import Data.Generics.Sum (_As) @@ -262,166 +264,53 @@ solveAlternatives unsolved (Monotype.Alternatives alternativeMonotypes alternati transformType type_ = type_ +{-| Helper function for traversing the tree during `Type` substitutions. +-} +substitute :: Text -> Domain -> (Type s -> Type s) -> Type s -> Type s +substitute a aDomain substituter = sub + where + sub x = + let result = substituter x + in (plate . Lens.filtered allowed %~ sub) result + + allowed Exists{ .. } + | a == name && domain == aDomain = False + allowed Forall{ .. } + | a == name && domain == aDomain = False + allowed _ = True + {-| Replace all occurrences of a variable within one `Type` with another `Type`, - given the variable's label and index + given the variable's label -} -substituteType :: Text -> Int -> Type s -> Type s -> Type s -substituteType a n _A type_ = - case type_ of - VariableType{..} - | a == name && n == 0 -> _A - | otherwise -> VariableType{..} - - UnsolvedType{..} -> - UnsolvedType{..} - - Exists{ type_ = oldType, .. } -> Exists{ type_ = newType, .. } - where - newType = substituteType a n' _A oldType - - n' | a == name && domain == Domain.Type = n + 1 - | otherwise = n - - Forall{ type_ = oldType, .. } -> Forall{ type_ = newType, .. } - where - newType = substituteType a n' _A oldType - - n' | a == name && domain == Domain.Type = n + 1 - | otherwise = n - - Function{ input = oldInput, output = oldOutput, .. } -> - Function{ input = newInput, output = newOutput, .. } - where - newInput = substituteType a n _A oldInput - - newOutput = substituteType a n _A oldOutput - - Optional{ type_ = oldType, .. } -> Optional{ type_ = newType, .. } - where - newType = substituteType a n _A oldType - - List{ type_ = oldType, .. } -> List{ type_ = newType, .. } - where - newType = substituteType a n _A oldType - - Record{ fields = Fields kAs ρ, .. } -> - Record{ fields = Fields (map (second (substituteType a n _A)) kAs) ρ, .. } - - Union{ alternatives = Alternatives kAs ρ, .. } -> - Union{ alternatives = Alternatives (map (second (substituteType a n _A)) kAs) ρ, .. } - - Scalar{..} -> - Scalar{..} +substituteType :: Text -> Type s -> Type s -> Type s +substituteType a _A = substitute a Domain.Type \case + VariableType {..} + | a == name -> _A + v -> v {-| Replace all occurrences of a variable within one `Type` with another `Type`, - given the variable's label and index + given the variable's label -} -substituteFields :: Text -> Int -> Record s -> Type s -> Type s -substituteFields ρ0 n r@(Fields kτs ρ1) type_ = - case type_ of - VariableType{..} -> - VariableType{..} - - UnsolvedType{..} -> - UnsolvedType{..} - - Exists{ type_ = oldType, .. } -> Exists{ type_ = newType, .. } - where - newType = substituteFields ρ0 n' r oldType - - n' | ρ0 == name && domain == Domain.Fields = n + 1 - | otherwise = n - - Forall{ type_ = oldType, .. } -> Forall{ type_ = newType, .. } - where - newType = substituteFields ρ0 n' r oldType - - n' | ρ0 == name && domain == Domain.Fields = n + 1 - | otherwise = n - - Function{ input = oldInput, output = oldOutput, .. } -> - Function{ input = newInput, output = newOutput, .. } - where - newInput = substituteFields ρ0 n r oldInput - - newOutput = substituteFields ρ0 n r oldOutput - - Optional{ type_ = oldType, .. } -> Optional{ type_ = newType, .. } - where - newType = substituteFields ρ0 n r oldType - - List{ type_ = oldType, .. } -> List{ type_ = newType, .. } - where - newType = substituteFields ρ0 n r oldType - - Record{ fields = Fields kAs0 ρ, .. } - | VariableFields ρ0 == ρ && n == 0 -> - Record{ fields = Fields (map (second (substituteFields ρ0 n r)) kAs1) ρ1, .. } - | otherwise -> - Record{ fields = Fields (map (second (substituteFields ρ0 n r)) kAs0) ρ, .. } - where - kAs1 = kAs0 <> map (second (fmap (\_ -> location))) kτs - - Union{ alternatives = Alternatives kAs ρ, .. } -> - Union{ alternatives = Alternatives (map (second (substituteFields ρ0 n r)) kAs) ρ, .. } - - Scalar{..} -> - Scalar{..} +substituteFields :: Text -> Record s -> Type s -> Type s +substituteFields ρ0 (Fields kτs ρ1) = substitute ρ0 Domain.Fields \case + Record{ fields = Fields kAs0 ρ, .. } + | VariableFields ρ0 == ρ -> + Record{ fields = Fields kAs1 ρ1, .. } + where + kAs1 = kAs0 <> map (second (fmap (\_ -> location))) kτs + v -> v {-| Replace all occurrences of a variable within one `Type` with another `Type`, given the variable's label and index -} -substituteAlternatives :: Text -> Int -> Union s -> Type s -> Type s -substituteAlternatives ρ0 n r@(Alternatives kτs ρ1) type_ = - case type_ of - VariableType{..} -> - VariableType{..} - - UnsolvedType{..} -> - UnsolvedType{..} - - Exists{ type_ = oldType, .. } -> Exists{ type_ = newType, .. } - where - newType = substituteAlternatives ρ0 n' r oldType - - n' | ρ0 == name && domain == Domain.Alternatives = n + 1 - | otherwise = n - - Forall{ type_ = oldType, .. } -> Forall{ type_ = newType, .. } - where - newType = substituteAlternatives ρ0 n' r oldType - - n' | ρ0 == name && domain == Domain.Alternatives = n + 1 - | otherwise = n - - Function{ input = oldInput, output = oldOutput, .. } -> - Function{ input = newInput, output = newOutput, .. } - where - newInput = substituteAlternatives ρ0 n r oldInput - - newOutput = substituteAlternatives ρ0 n r oldOutput - - Optional{ type_ = oldType, .. } -> Optional{ type_ = newType, .. } - where - newType = substituteAlternatives ρ0 n r oldType - - List{ type_ = oldType, .. } -> List{ type_ = newType, .. } - where - newType = substituteAlternatives ρ0 n r oldType - - Record{ fields = Fields kAs ρ, .. } -> - Record{ fields = Fields (map (second (substituteAlternatives ρ0 n r)) kAs) ρ, .. } - - Union{ alternatives = Alternatives kAs0 ρ, .. } - | Monotype.VariableAlternatives ρ0 == ρ && n == 0 -> - Union{ alternatives = Alternatives (map (second (substituteAlternatives ρ0 n r)) kAs1) ρ1, .. } - | otherwise -> - Union{ alternatives = Alternatives (map (second (substituteAlternatives ρ0 n r)) kAs0) ρ, .. } - where - kAs1 = kAs0 <> map (second (fmap (\_ -> location))) kτs - - Scalar{..} -> - Scalar{..} +substituteAlternatives :: Text -> Union s -> Type s -> Type s +substituteAlternatives ρ0 (Alternatives kτs ρ1) = substitute ρ0 Domain.Alternatives \case + Union{ alternatives = Alternatives kAs0 ρ, .. } + | Monotype.VariableAlternatives ρ0 == ρ -> + Union{ alternatives = Alternatives kAs1 ρ1, .. } + where + kAs1 = kAs0 <> map (second (fmap (\_ -> location))) kτs + v -> v {-| Count how many times the given `Existential` `Type` variable appears within a `Type` From 61d73ec9064c4a3962220838f0caa5c9d22691aa Mon Sep 17 00:00:00 2001 From: Gardanta Spirito Date: Sat, 19 Aug 2023 20:05:24 +0300 Subject: [PATCH 2/2] Unique TypeVariables --- src/Grace/Context.hs | 189 +++++++++--------- src/Grace/Existential.hs | 25 +-- src/Grace/Infer.hs | 114 +++++++---- src/Grace/Interpret.hs | 3 +- src/Grace/Monotype.hs | 40 +++- src/Grace/Parser.hs | 8 +- src/Grace/Type.hs | 27 ++- .../error/type/conflicting-names-input.ffg | 6 + .../error/type/conflicting-names-stderr.txt | 19 ++ 9 files changed, 248 insertions(+), 183 deletions(-) create mode 100644 tasty/data/error/type/conflicting-names-input.ffg create mode 100644 tasty/data/error/type/conflicting-names-stderr.txt diff --git a/src/Grace/Context.hs b/src/Grace/Context.hs index 5507d36..4dec803 100644 --- a/src/Grace/Context.hs +++ b/src/Grace/Context.hs @@ -10,12 +10,14 @@ module Grace.Context ( -- * Types Entry(..) - , Context + , Context(..) -- * Utilities + , _Entries , lookup , splitOnUnsolvedType , splitOnUnsolvedFields , splitOnUnsolvedAlternatives + , uniqueVariableId , discardUpTo , solveType , solveRecord @@ -23,18 +25,22 @@ module Grace.Context , complete ) where +import Control.Lens.Lens (Lens', lens) +import Control.Lens.Tuple (_1, _2) +import Control.Lens.Zoom (zoom) +import Control.Monad.State.Strict (State) import Data.Text (Text) import Grace.Domain (Domain) import Grace.Existential (Existential) -import Grace.Monotype (Monotype) +import Grace.Monotype (Monotype, VariableId(..)) import Grace.Pretty (Pretty(..), label, operator, punctuation) import Grace.Type (Type) import Prelude hiding (lookup) import Prettyprinter (Doc) import Prettyprinter.Render.Terminal (AnsiStyle) -import qualified Control.Monad as Monad import qualified Control.Monad.State.Strict as State +import qualified Data.Map as Map import qualified Grace.Domain as Domain import qualified Grace.Existential as Existential import qualified Grace.Monotype as Monotype @@ -50,7 +56,7 @@ import qualified Prettyprinter as Pretty -- | An element of the `Context` list data Entry s - = Variable Domain Text + = Variable Domain VariableId -- ^ Universally quantified variable -- -- >>> pretty @(Entry ()) (Variable Domain.Type "a") @@ -120,7 +126,7 @@ data Entry s instance Pretty (Entry s) where pretty = prettyEntry -{-| A `Context` is an ordered list of `Entry`s +{-| A `Context` is an ordered list of `Entry`s with a Map of occupied `VariableId`s Note that this representation stores the `Context` entries in reverse order, meaning that the beginning of the list represents the entries that @@ -147,7 +153,11 @@ instance Pretty (Entry s) where in the context past a certain entry to reflect the end of their \"lifetime\" -} -type Context s = [Entry s] +data Context s = Context [Entry s] (Map.Map Text Int) + deriving Show + +_Entries :: Lens' (Context s) [Entry s] +_Entries = lens (\(Context x _) -> x) (\(Context _ y) x -> Context x y) prettyEntry :: Entry s -> Doc AnsiStyle prettyEntry (Variable domain a) = @@ -246,8 +256,8 @@ prettyAlternativeType (k, τ) = >>> pretty @(Type ()) (solveType [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] original) Bool -} -solveType :: Context s -> Type s -> Type s -solveType context type_ = foldl snoc type_ context +solveType :: [Entry s] -> Type s -> Type s +solveType entries type_ = foldl snoc type_ entries where snoc t (SolvedType a τ) = Type.solveType a τ t snoc t (SolvedFields a r) = Type.solveFields a r t @@ -267,18 +277,17 @@ solveType context type_ = foldl snoc type_ context >>> pretty @(Record ()) (solveRecord [ entry ] original) { x: Bool } -} -solveRecord :: Context s -> Type.Record s -> Type.Record s -solveRecord context oldFields = newFields +solveRecord :: [Entry s] -> Type.Record s -> Type.Record s +solveRecord entries oldFields = newFields where location = error "Grace.Context.solveRecord: Internal error - Missing location field" -- TODO: Come up with total solution Type.Record{ fields = newFields } = - solveType context Type.Record{ fields = oldFields, .. } + solveType entries Type.Record{ fields = oldFields, .. } {-| Substitute a t`Type.Union` using the solved entries of a `Context` - `Context` >>> original = Type.Alternatives [("A", Type.Scalar () Monotype.Bool)] (Monotype.UnsolvedAlternatives 0) >>> pretty @(Union ()) original @@ -291,15 +300,21 @@ solveRecord context oldFields = newFields >>> pretty @(Union ()) (solveUnion [ entry ] original) < A: Bool > -} -solveUnion :: Context s -> Type.Union s -> Type.Union s -solveUnion context oldAlternatives = newAlternatives +solveUnion :: [Entry s] -> Type.Union s -> Type.Union s +solveUnion entries oldAlternatives = newAlternatives where location = error "Grace.Context.solveUnion: Internal error - Missing location field" -- TODO: Come up with total solution Type.Union{ alternatives = newAlternatives } = - solveType context Type.Union{ alternatives = oldAlternatives, .. } + solveType entries Type.Union{ alternatives = oldAlternatives, .. } + +{-| Create an unique `VariableId` out of `Text` given a `Map` of existing variables -} +uniqueVariableId :: Text -> Int -> State (Map.Map Text Int) VariableId +uniqueVariableId name atLeast = State.state (\m -> + let newId = max atLeast $ maybe 0 (+1) (Map.lookup name m) in + (VariableId name newId, Map.insert name newId m)) {-| This function is used at the end of the bidirectional type-checking algorithm to complete the inferred type by: @@ -312,79 +327,54 @@ solveUnion context oldAlternatives = newAlternatives >>> pretty @(Type ()) original b? -> a? - >>> pretty @(Type ()) (complete [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] original) - forall (a : Type) . a -> Bool + >>> context = Context [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] (Map.fromList [ ("a", 3) ]) + >>> pretty @(Type ()) (complete context original) + forall (a3 : Type) . a3 -> Bool -} complete :: Context s -> Type s -> Type s -complete context type0 = do - State.evalState (Monad.foldM snoc type0 context) 0 +complete (Context entries variables) = + applyUpdates $ State.evalState (traverse updateFor $ reverse entries) (variables, 0) where - numUnsolved = fromIntegral (length (filter predicate context)) - 1 - where - predicate (UnsolvedType _) = True - predicate (UnsolvedFields _) = True - predicate (UnsolvedAlternatives _) = True - predicate _ = False - - snoc t (SolvedType a τ) = do return (Type.solveType a τ t) - snoc t (SolvedFields a r) = do return (Type.solveFields a r t) - snoc t (SolvedAlternatives a r) = do return (Type.solveAlternatives a r t) - snoc t (UnsolvedType a) | a `Type.typeFreeIn` t = do - n <- State.get - - State.put $! n + 1 - - let name = Existential.toVariable (numUnsolved - n) - - let domain = Domain.Type - - let type_ = Type.solveType a (Monotype.VariableType name) t - - let location = Type.location t - - let nameLocation = location - - return Type.Forall{..} - snoc t (UnsolvedFields p) | p `Type.fieldsFreeIn` t = do - n <- State.get - - State.put $! n + 1 - - let name = Existential.toVariable (numUnsolved - n) - - let domain = Domain.Fields - - let type_ = Type.solveFields p (Monotype.Fields [] (Monotype.VariableFields name)) t - - let location = Type.location t - - let nameLocation = location - - return Type.Forall{..} - snoc t (UnsolvedAlternatives p) | p `Type.alternativesFreeIn` t = do - n <- State.get - - State.put $! n + 1 - - let name = Existential.toVariable (numUnsolved - n) - - let domain = Domain.Alternatives - - let type_ = Type.solveAlternatives p (Monotype.Alternatives [] (Monotype.VariableAlternatives name)) t - - let location = Type.location t - - let nameLocation = location - - return Type.Forall{..} - snoc t _ = do - return t - -{-| Split a `Context` into two `Context`s before and after the given - `UnsolvedType` variable. Neither `Context` contains the variable + applyUpdates :: [Type s -> Type s] -> Type s -> Type s + applyUpdates = flip $ foldr ($) + + updateFor :: Entry s -> State (Map.Map Text Int, Int) (Type s -> Type s) + updateFor (SolvedType a τ) = return (Type.solveType a τ) + updateFor (SolvedFields a r) = return (Type.solveFields a r) + updateFor (SolvedAlternatives a r) = return (Type.solveAlternatives a r) + updateFor (UnsolvedType a) = + newVariable (Type.typeFreeIn a) Domain.Type (Type.solveType a . Monotype.VariableType) + updateFor (UnsolvedFields a) = + newVariable (Type.fieldsFreeIn a) Domain.Fields (Type.solveFields a . Monotype.Fields [] . Monotype.VariableFields) + updateFor (UnsolvedAlternatives a) = + newVariable (Type.alternativesFreeIn a) Domain.Alternatives (Type.solveAlternatives a . Monotype.Alternatives [] . Monotype.VariableAlternatives) + updateFor _ = return id + + newVariable check domain solve = do + n <- zoom _2 (do + n <- State.get + State.put $! n + 1 + return n) + + let (internalName, _) = Existential.internalName n + + name <- zoom _1 $ uniqueVariableId internalName 0 + + return (\t -> + if check t + then + let + location = Type.location t + nameLocation = location + type_ = solve name t + in Type.Forall {..} + else t) + +{-| Split a `[Entry s]` into two `[Entry s]`s before and after the given + `UnsolvedType` variable. Neither `[Entry s]` contains the variable Returns `Nothing` if no such `UnsolvedType` variable is present within the - `Context` + `[Entry s]` >>> splitOnUnsolvedType 1 [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] Just ([],[SolvedType 0 (Scalar Bool)]) @@ -394,8 +384,8 @@ complete context type0 = do splitOnUnsolvedType :: Existential Monotype -- ^ `UnsolvedType` variable to split on - -> Context s - -> Maybe (Context s, Context s) + -> [Entry s] + -> Maybe ([Entry s], [Entry s]) splitOnUnsolvedType a0 (UnsolvedType a1 : entries) | a0 == a1 = return ([], entries) splitOnUnsolvedType a (entry : entries) = do @@ -403,11 +393,10 @@ splitOnUnsolvedType a (entry : entries) = do return (entry : prefix, suffix) splitOnUnsolvedType _ [] = Nothing -{-| Split a `Context` into two `Context`s before and after the given - `UnsolvedFields` variable. Neither `Context` contains the variable +{-| Split a `[Entry s]` into two `[Entry s]`s before and after the given + `UnsolvedFields` variable. Neither `[Entry s]` contains the variable - Returns `Nothing` if no such `UnsolvedFields` variable is present within the - `Context` + Returns `Nothing` if no such `UnsolvedFields` variable is present within the `[Entry s]` >>> splitOnUnsolvedFields 1 [ UnsolvedFields 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] Just ([],[SolvedType 0 (Scalar Bool)]) @@ -417,8 +406,8 @@ splitOnUnsolvedType _ [] = Nothing splitOnUnsolvedFields :: Existential Monotype.Record -- ^ `UnsolvedFields` variable to split on - -> Context s - -> Maybe (Context s, Context s) + -> [Entry s] + -> Maybe ([Entry s], [Entry s]) splitOnUnsolvedFields p0 (UnsolvedFields p1 : entries) | p0 == p1 = return ([], entries) splitOnUnsolvedFields p (entry : entries) = do @@ -426,11 +415,11 @@ splitOnUnsolvedFields p (entry : entries) = do return (entry : prefix, suffix) splitOnUnsolvedFields _ [] = Nothing -{-| Split a `Context` into two `Context`s before and after the given - `UnsolvedAlternatives` variable. Neither `Context` contains the variable +{-| Split a `[Entry s]` into two `[Entry s]`s before and after the given + `UnsolvedAlternatives` variable. Neither `[Entry s]` contains the variable Returns `Nothing` if no such `UnsolvedAlternatives` variable is present - within the `Context` + within the `[Entry s]` >>> splitOnUnsolvedAlternatives 1 [ UnsolvedAlternatives 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] Just ([],[SolvedType 0 (Scalar Bool)]) @@ -440,8 +429,8 @@ splitOnUnsolvedFields _ [] = Nothing splitOnUnsolvedAlternatives :: Existential Monotype.Union -- ^ `UnsolvedAlternatives` variable to split on - -> Context s - -> Maybe (Context s, Context s) + -> [Entry s] + -> Maybe ([Entry s], [Entry s]) splitOnUnsolvedAlternatives p0 (UnsolvedAlternatives p1 : entries) | p0 == p1 = return ([], entries) splitOnUnsolvedAlternatives p (entry : entries) = do @@ -449,7 +438,7 @@ splitOnUnsolvedAlternatives p (entry : entries) = do return (entry : prefix, suffix) splitOnUnsolvedAlternatives _ [] = Nothing -{-| Retrieve a variable's annotated type from a `Context`, given the variable's +{-| Retrieve a variable's annotated type from a `[Entry s]`, given the variable's label and index >>> lookup "x" 0 [ Annotation "x" (Type.Scalar () Monotype.Bool), Annotation "y" (Type.Scalar () Monotype.Natural) ] @@ -460,7 +449,7 @@ lookup -- ^ Variable label -> Int -- ^ Variable index (See the documentation of `Value.Variable`) - -> Context s + -> [Entry s] -> Maybe (Type s) lookup _ _ [] = Nothing @@ -474,12 +463,12 @@ lookup x0 n (Annotation x1 _A : _Γ) = lookup x n (_ : _Γ) = lookup x n _Γ -{-| Discard all entries from a `Context` up to and including the given `Entry` +{-| Discard all entries from a `[Entry s]` up to and including the given `Entry` >>> discardUpTo (MarkerType 1) [ UnsolvedType 1, MarkerType 1, UnsolvedType 0 ] [UnsolvedType 0] -} -discardUpTo :: Eq s => Entry s -> Context s -> Context s +discardUpTo :: Eq s => Entry s -> [Entry s] -> [Entry s] discardUpTo entry0 (entry1 : _Γ) | entry0 == entry1 = _Γ | otherwise = discardUpTo entry0 _Γ diff --git a/src/Grace/Existential.hs b/src/Grace/Existential.hs index f55e1c0..9cf802d 100644 --- a/src/Grace/Existential.hs +++ b/src/Grace/Existential.hs @@ -13,7 +13,7 @@ module Grace.Existential ( -- * Types Existential -- * Utilities - , toVariable + , internalName ) where import Data.Text (Text) @@ -38,23 +38,14 @@ newtype Existential a = UnsafeExistential Int deriving newtype (Eq, Num, Show) instance Pretty (Existential a) where - pretty x = label (pretty (toVariable x)) + pretty (UnsafeExistential n) = label $ pretty $ prefix <> suffix + where + (prefix, q) = internalName n + suffix = if q == 0 then "" else Text.pack (show (q - 1)) -{-| Convert an existential variable to a user-friendly `Text` - representation - - >>> toVariable 0 - "a" - >>> toVariable 1 - "b" - >>> toVariable 26 - "a0" --} -toVariable :: Existential a -> Text -toVariable (UnsafeExistential n) = Text.cons prefix suffix +internalName :: Int -> (Text, Int) +internalName n = (prefix, q) where (q, r) = n `quotRem` 26 - prefix = Char.chr (Char.ord 'a' + r) - - suffix = if q == 0 then "" else Text.pack (show (q - 1)) + prefix = Text.singleton $ Char.chr (Char.ord 'a' + r) diff --git a/src/Grace/Infer.hs b/src/Grace/Infer.hs index 2d32a30..f676410 100644 --- a/src/Grace/Infer.hs +++ b/src/Grace/Infer.hs @@ -30,6 +30,8 @@ module Grace.Infer import Control.Applicative ((<|>)) import Control.Exception.Safe (Exception(..)) +import Control.Lens (view, (%~), (.~)) +import Control.Lens.Lens (Lens', lens) import Control.Monad (when) import Control.Monad.Except (MonadError(..)) import Control.Monad.State.Strict (MonadState) @@ -37,17 +39,17 @@ import Data.Foldable (traverse_) import Data.Sequence (ViewL(..)) import Data.Text (Text) import Data.Void (Void) -import Grace.Context (Context, Entry) +import Grace.Context (Context(..), Entry) import Grace.Existential (Existential) import Grace.Location (Location(..)) -import Grace.Monotype (Monotype) +import Grace.Monotype (Monotype, VariableId(..)) import Grace.Pretty (Pretty(..)) import Grace.Syntax (Syntax) import Grace.Type (Type(..)) import Grace.Value (Value) import qualified Control.Monad as Monad -import qualified Control.Monad.State as State +import qualified Control.Monad.State.Strict as State import qualified Data.Map as Map import qualified Data.Sequence as Seq import qualified Data.Text as Text @@ -83,22 +85,28 @@ fresh = do return (fromIntegral n) +_Context :: Lens' Status (Context Location) +_Context = lens context (\s context -> s { context }) + +_Entries :: Lens' Status [Entry Location] +_Entries = _Context . Context._Entries + -- Unlike the original paper, we don't explicitly thread the `Context` around. -- Instead, we modify the ambient state using the following utility functions: -- | Push a new `Context` `Entry` onto the stack push :: MonadState Status m => Entry Location -> m () -push entry = State.modify (\s -> s { context = entry : context s }) +push entry = State.modify (_Entries %~ (entry:)) --- | Retrieve the current `Context` -get :: MonadState Status m => m (Context Location) -get = State.gets context +-- | Retrieve the current `Context` `Entry`s +get :: MonadState Status m => m [Entry Location] +get = State.gets (view _Entries) --- | Set the `Context` to a new value -set :: MonadState Status m => Context Location -> m () -set context = State.modify (\s -> s{ context }) +-- | Set the `Context` `Entry`s to a new value +set :: MonadState Status m => [Entry Location] -> m () +set entries = State.modify (_Entries .~ entries) -{-| This is used to temporarily add a `Context` entry that is discarded at the +{-| This is used to temporarily add a `Context` `Entry` that is discarded at the end of the entry's scope, along with any downstream entries that were created within that same scope -} @@ -108,7 +116,27 @@ scoped entry k = do r <- k - State.modify (\s -> s{ context = Context.discardUpTo entry (context s) }) + State.modify (_Entries %~ Context.discardUpTo entry) + + return r + +{-| Scoped `VariableType`, which possible requires substitutions to preserve uniqueness -} +scopedVariableType :: MonadState Status m => s -> Domain.Domain -> VariableId -> ((Type.Type s -> Type.Type s) -> m a) -> m a +scopedVariableType location domain oldId@(VariableId name ind) k = do + (newId, oldVariables) <- do + Context entries oldVariables <- State.gets (view _Context) + let (newInd, variables) = State.runState (Context.uniqueVariableId name ind) oldVariables + State.modify (_Context .~ Context entries variables) + return (newInd, oldVariables) + + r <- scoped (Context.Variable domain newId) $ k $ if newId == oldId + then id + else case domain of + Domain.Type -> Type.substituteType oldId (Type.VariableType { name = newId, ..}) + Domain.Fields -> Type.substituteFields oldId (Type.Fields [] $ Monotype.VariableFields newId) + Domain.Alternatives -> Type.substituteAlternatives oldId (Type.Alternatives [] $ Monotype.VariableAlternatives newId) + State.modify (_Context %~ (\(Context entries _) -> + Context entries oldVariables)) return r @@ -148,7 +176,7 @@ scopedUnsolvedAlternatives k = do -} wellFormedType :: MonadError TypeInferenceError m - => Context Location -> Type Location -> m () + => [Entry Location] -> Type Location -> m () wellFormedType _Γ type0 = case type0 of -- UvarWF @@ -315,13 +343,13 @@ subtype _A0 _B0 = do -- <:∃L (Type.Exists{..}, _) -> do - scoped (Context.Variable domain name) do - subtype type_ _B0 + scopedVariableType nameLocation domain name \substitute -> + subtype (substitute type_) _B0 -- <:∀R (_, Type.Forall{..}) -> do - scoped (Context.Variable domain name) do - subtype _A0 type_ + scopedVariableType nameLocation domain name \substitute-> + subtype _A0 (substitute type_) -- <:∃R (_, Type.Exists{ domain = Domain.Type, .. }) -> do @@ -817,8 +845,8 @@ instantiateTypeL a _A0 = do -- InstLAllR Type.Forall{..} -> do - scoped (Context.Variable domain name) do - instantiateTypeL a type_ + scopedVariableType nameLocation domain name \substitute -> + instantiateTypeL a (substitute type_) -- This case is the first example of a general pattern we have to -- follow when solving unsolved variables. @@ -960,8 +988,8 @@ instantiateTypeR _A0 a = do -- InstRExtL Type.Exists{..} -> do - scoped (Context.Variable domain name) do - instantiateTypeL a type_ + scopedVariableType nameLocation domain name \substitute -> + instantiateTypeL a (substitute type_) -- InstRAllL Type.Forall{ domain = Domain.Type, .. } -> do @@ -1900,8 +1928,8 @@ check e Type.Exists{ domain = Domain.Alternatives, .. } = do -- ∀I check e Type.Forall{..} = do - scoped (Context.Variable domain name) do - check e type_ + scopedVariableType nameLocation domain name \substitute -> + check e (substitute type_) check Syntax.Operator{ operator = Syntax.Times, .. } _B@Type.Scalar{ scalar } | scalar `elem` ([ Monotype.Natural, Monotype.Integer, Monotype.Real ] :: [Monotype.Scalar])= do @@ -2032,8 +2060,8 @@ inferApplication Type.Forall{ domain = Domain.Alternatives, .. } e = do -- ∃App inferApplication Type.Exists{..} e = do - scoped (Context.Variable domain name) do - inferApplication type_ e + scopedVariableType nameLocation domain name \substitute -> + inferApplication (substitute type_) e -- αApp inferApplication Type.UnsolvedType{ existential = a, .. } e = do @@ -2062,9 +2090,9 @@ inferApplication _A _ = do typeOf :: Syntax Location (Type Location, Value) -> Either TypeInferenceError (Type Location) -typeOf = typeWith [] +typeOf = typeWith (Context [] Map.empty) --- | Like `typeOf`, but accepts a custom type-checking `Context` +-- | Like `typeOf`, but accepts a custom type-checking `[Entry Location]` typeWith :: Context Location -> Syntax Location (Type Location, Value) @@ -2078,9 +2106,9 @@ typeWith context syntax = do -- | A data type holding all errors related to type inference data TypeInferenceError - = IllFormedAlternatives Location (Existential Monotype.Union) (Context Location) - | IllFormedFields Location (Existential Monotype.Record) (Context Location) - | IllFormedType Location (Type Location) (Context Location) + = IllFormedAlternatives Location (Existential Monotype.Union) ([Entry Location]) + | IllFormedFields Location (Existential Monotype.Record) ([Entry Location]) + | IllFormedType Location (Type Location) ([Entry Location]) -- | InvalidOperands Location (Type Location) -- @@ -2088,14 +2116,14 @@ data TypeInferenceError | MergeInvalidHandler Location (Type Location) | MergeRecord Location (Type Location) -- - | MissingAllAlternatives (Existential Monotype.Union) (Context Location) - | MissingAllFields (Existential Monotype.Record) (Context Location) - | MissingOneOfAlternatives [Location] (Existential Monotype.Union) (Existential Monotype.Union) (Context Location) - | MissingOneOfFields [Location] (Existential Monotype.Record) (Existential Monotype.Record) (Context Location) - | MissingVariable (Existential Monotype) (Context Location) + | MissingAllAlternatives (Existential Monotype.Union) ([Entry Location]) + | MissingAllFields (Existential Monotype.Record) ([Entry Location]) + | MissingOneOfAlternatives [Location] (Existential Monotype.Union) (Existential Monotype.Union) ([Entry Location]) + | MissingOneOfFields [Location] (Existential Monotype.Record) (Existential Monotype.Record) ([Entry Location]) + | MissingVariable (Existential Monotype) ([Entry Location]) -- | NotFunctionType Location (Type Location) - | NotNecessarilyFunctionType Location Text + | NotNecessarilyFunctionType Location VariableId -- | NotAlternativesSubtype Location (Existential Monotype.Union) (Type.Union Location) | NotFieldsSubtype Location (Existential Monotype.Record) (Type.Record Location) @@ -2103,14 +2131,14 @@ data TypeInferenceError | NotUnionSubtype Location (Type Location) Location (Type Location) | NotSubtype Location (Type Location) Location (Type Location) -- - | UnboundAlternatives Location Text - | UnboundFields Location Text - | UnboundTypeVariable Location Text + | UnboundAlternatives Location VariableId + | UnboundFields Location VariableId + | UnboundTypeVariable Location VariableId | UnboundVariable Location Text Int -- | RecordTypeMismatch (Type Location) (Type Location) (Map.Map Text (Type Location)) (Map.Map Text (Type Location)) | UnionTypeMismatch (Type Location) (Type Location) (Map.Map Text (Type Location)) (Map.Map Text (Type Location)) - deriving (Eq, Show) + deriving Show instance Exception TypeInferenceError where displayException (IllFormedAlternatives location a0 _Γ) = @@ -2363,17 +2391,17 @@ instance Exception TypeInferenceError where \" <> Text.unpack (Location.renderError "" locB0) displayException (UnboundAlternatives location a) = - "Unbound alternatives variable: " <> Text.unpack a <> "\n\ + "Unbound alternatives variable: " <> Text.unpack (prettyToText a) <> "\n\ \\n\ \" <> Text.unpack (Location.renderError "" location) displayException (UnboundFields location a) = - "Unbound fields variable: " <> Text.unpack a <> "\n\ + "Unbound fields variable: " <> Text.unpack (prettyToText a) <> "\n\ \\n\ \" <> Text.unpack (Location.renderError "" location) displayException (UnboundTypeVariable location a) = - "Unbound type variable: " <> Text.unpack a <> "\n\ + "Unbound type variable: " <> Text.unpack (prettyToText a) <> "\n\ \\n\ \" <> Text.unpack (Location.renderError "" location) diff --git a/src/Grace/Interpret.hs b/src/Grace/Interpret.hs index 8c90125..fc7fece 100644 --- a/src/Grace/Interpret.hs +++ b/src/Grace/Interpret.hs @@ -30,6 +30,7 @@ import Grace.Value (Value) import qualified Control.Exception.Safe as Exception import qualified Control.Lens as Lens import qualified Control.Monad.Except as Except +import qualified Data.Map as Map import qualified Grace.Context as Context import qualified Grace.HTTP as HTTP import qualified Grace.Import as Import @@ -98,7 +99,7 @@ interpretWith bindings maybeAnnotation manager input = do return (Context.Annotation variable type_) - case Infer.typeWith typeContext annotatedExpression of + case Infer.typeWith (Context.Context typeContext Map.empty) annotatedExpression of Left message -> do Except.throwError (TypeInferenceError message) diff --git a/src/Grace/Monotype.hs b/src/Grace/Monotype.hs index ca8a743..232c15b 100644 --- a/src/Grace/Monotype.hs +++ b/src/Grace/Monotype.hs @@ -9,28 +9,54 @@ -} module Grace.Monotype ( -- * Types - Monotype(..) + VariableId(..) + , Monotype(..) , Scalar(..) , Record(..) , RemainingFields(..) , Union(..) , RemainingAlternatives(..) + -- * Utilities + , variableId ) where +import Data.List (elemIndex) import Data.String (IsString(..)) -import Data.Text (Text) +import Data.Text (Text, pack, unsnoc) import GHC.Generics (Generic) import Grace.Existential (Existential) -import Grace.Pretty (Pretty(..), builtin) +import Grace.Pretty (Pretty(..), builtin, label) import Language.Haskell.TH.Syntax (Lift) +data VariableId = VariableId Text Int + deriving (Eq, Show, Lift) + +{-| Construct non-unique VariableId out of raw Text. -} +variableId :: Text -> VariableId +variableId = \text -> + let (name, ind) = takeIntFromEnd 0 1 text + in VariableId name ind + where + takeIntFromEnd :: Int -> Int -> Text -> (Text, Int) + takeIntFromEnd curr mult text + | Just (xs, x) <- unsnoc text + , Just ind <- x `elemIndex` "0123456789" + = takeIntFromEnd (curr + ind*mult) (10*mult) xs + takeIntFromEnd curr _ xs = (xs, curr) + +instance IsString VariableId where + fromString = variableId . pack + +instance Pretty VariableId where + pretty (VariableId text ind) = label $ pretty (text <> if ind == 0 then "" else fromString $ show (ind - 1)) + {-| A monomorphic type This is same type as `Grace.Type.Type`, except without the `Grace.Type.Forall` and `Grace.Type.Exists` constructors -} data Monotype - = VariableType Text + = VariableType VariableId | UnsolvedType (Existential Monotype) | Function Monotype Monotype | Optional Monotype @@ -41,7 +67,7 @@ data Monotype deriving stock (Eq, Generic, Show) instance IsString Monotype where - fromString string = VariableType (fromString string) + fromString = VariableType . fromString -- | A scalar type data Scalar @@ -97,7 +123,7 @@ data RemainingFields -- ^ The record type is open, meaning that some fields are known and there -- is an unsolved fields variable that is a placeholder for other fields -- that may or may not be present - | VariableFields Text + | VariableFields VariableId -- ^ Same as `UnsolvedFields`, except that the user has given the fields -- variable an explicit name in the source code deriving stock (Eq, Generic, Lift, Show) @@ -114,7 +140,7 @@ data RemainingAlternatives -- ^ The union type is open, meaning that some alternatives are known and -- there is an unsolved alternatives variable that is a placeholder for -- other alternatives that may or may not be present - | VariableAlternatives Text + | VariableAlternatives VariableId -- ^ Same as `UnsolvedAlternatives`, except that the user has given the -- alternatives variable an explicit name in the source code deriving stock (Eq, Generic, Lift, Show) diff --git a/src/Grace/Parser.hs b/src/Grace/Parser.hs index 5089b0b..85357df 100644 --- a/src/Grace/Parser.hs +++ b/src/Grace/Parser.hs @@ -510,7 +510,7 @@ grammar = mdo quantifiedType <- rule do let quantify (forallOrExists, location, (typeVariableOffset, typeVariable), domain_) type_ = - forallOrExists location typeVariableOffset typeVariable domain_ type_ + forallOrExists location typeVariableOffset (Monotype.variableId typeVariable) domain_ type_ fss <- many ( do location <- locatedToken Lexer.Forall @@ -573,7 +573,7 @@ grammar = mdo <|> do location <- locatedToken Lexer.Text return Type.Scalar{ scalar = Monotype.Text, .. } <|> do ~(location, name) <- locatedLabel - return Type.VariableType{..} + return Type.VariableType{ name = Monotype.variableId name, ..} <|> do let record location fields = Type.Record{..} locatedOpenBrace <- locatedToken Lexer.OpenBrace @@ -584,7 +584,7 @@ grammar = mdo toFields <- ( do text_ <- recordLabel - pure (\fs -> Type.Fields fs (Monotype.VariableFields text_)) + pure (\fs -> Type.Fields fs (Monotype.VariableFields $ Monotype.variableId text_)) <|> do pure (\fs -> Type.Fields fs Monotype.EmptyFields) <|> do f <- fieldType pure (\fs -> Type.Fields (fs <> [ f ]) Monotype.EmptyFields) @@ -605,7 +605,7 @@ grammar = mdo toAlternatives <- ( do text_ <- label - return (\as -> Type.Alternatives as (Monotype.VariableAlternatives text_)) + return (\as -> Type.Alternatives as (Monotype.VariableAlternatives $ Monotype.variableId text_)) <|> do pure (\as -> Type.Alternatives as Monotype.EmptyAlternatives) <|> do a <- alternativeType return (\as -> Type.Alternatives (as <> [ a ]) Monotype.EmptyAlternatives) diff --git a/src/Grace/Type.hs b/src/Grace/Type.hs index cf8a381..c8143e0 100644 --- a/src/Grace/Type.hs +++ b/src/Grace/Type.hs @@ -53,7 +53,12 @@ import Prettyprinter (Doc) import Prettyprinter.Render.Terminal (AnsiStyle) import Grace.Monotype - (Monotype, RemainingAlternatives(..), RemainingFields(..), Scalar(..)) + ( Monotype + , RemainingAlternatives(..) + , RemainingFields(..) + , Scalar(..) + , VariableId + ) import qualified Control.Lens as Lens import qualified Data.Text as Text @@ -70,7 +75,7 @@ import qualified Prettyprinter as Pretty -- | A potentially polymorphic type data Type s - = VariableType { location :: s, name :: Text } + = VariableType { location :: s, name :: VariableId } -- ^ Type variable -- -- >>> pretty @(Type ()) (VariableType () "a") @@ -80,12 +85,12 @@ data Type s -- -- >>> pretty @(Type ()) (UnsolvedType () 0) -- a? - | Exists { location :: s, nameLocation :: s, name :: Text, domain :: Domain, type_ :: Type s } + | Exists { location :: s, nameLocation :: s, name :: VariableId, domain :: Domain, type_ :: Type s } -- ^ Existentially quantified type -- -- >>> pretty @(Type ()) (Exists () () "a" Domain.Type "a") -- exists (a : Type) . a - | Forall { location :: s, nameLocation :: s, name :: Text, domain :: Domain, type_ :: Type s } + | Forall { location :: s, nameLocation :: s, name :: VariableId, domain :: Domain, type_ :: Type s } -- ^ Universally quantified type -- -- >>> pretty @(Type ()) (Forall () () "a" Domain.Type "a") @@ -266,7 +271,7 @@ solveAlternatives unsolved (Monotype.Alternatives alternativeMonotypes alternati {-| Helper function for traversing the tree during `Type` substitutions. -} -substitute :: Text -> Domain -> (Type s -> Type s) -> Type s -> Type s +substitute :: VariableId -> Domain -> (Type s -> Type s) -> Type s -> Type s substitute a aDomain substituter = sub where sub x = @@ -280,18 +285,18 @@ substitute a aDomain substituter = sub allowed _ = True {-| Replace all occurrences of a variable within one `Type` with another `Type`, - given the variable's label + given the variable's id -} -substituteType :: Text -> Type s -> Type s -> Type s +substituteType :: VariableId -> Type s -> Type s -> Type s substituteType a _A = substitute a Domain.Type \case VariableType {..} | a == name -> _A v -> v {-| Replace all occurrences of a variable within one `Type` with another `Type`, - given the variable's label + given the variable's id -} -substituteFields :: Text -> Record s -> Type s -> Type s +substituteFields :: VariableId -> Record s -> Type s -> Type s substituteFields ρ0 (Fields kτs ρ1) = substitute ρ0 Domain.Fields \case Record{ fields = Fields kAs0 ρ, .. } | VariableFields ρ0 == ρ -> @@ -301,9 +306,9 @@ substituteFields ρ0 (Fields kτs ρ1) = substitute ρ0 Domain.Fields \case v -> v {-| Replace all occurrences of a variable within one `Type` with another `Type`, - given the variable's label and index + given the variable's id -} -substituteAlternatives :: Text -> Union s -> Type s -> Type s +substituteAlternatives :: VariableId -> Union s -> Type s -> Type s substituteAlternatives ρ0 (Alternatives kτs ρ1) = substitute ρ0 Domain.Alternatives \case Union{ alternatives = Alternatives kAs0 ρ, .. } | Monotype.VariableAlternatives ρ0 == ρ -> diff --git a/tasty/data/error/type/conflicting-names-input.ffg b/tasty/data/error/type/conflicting-names-input.ffg new file mode 100644 index 0000000..88d6974 --- /dev/null +++ b/tasty/data/error/type/conflicting-names-input.ffg @@ -0,0 +1,6 @@ +# This test checks that matching variable names do not trick the typechecker +# into unifying two different type variables. +let test + : exists (a : Type). a + = 3 +in test : forall (a : Type). a diff --git a/tasty/data/error/type/conflicting-names-stderr.txt b/tasty/data/error/type/conflicting-names-stderr.txt new file mode 100644 index 0000000..53aa896 --- /dev/null +++ b/tasty/data/error/type/conflicting-names-stderr.txt @@ -0,0 +1,19 @@ +Not a subtype + +The following type: + + a0 + +tasty/data/error/type/conflicting-names-input.ffg:4:15: + │ +4 │ : exists (a : Type). a + │ ↑ + +… cannot be a subtype of: + + a + +tasty/data/error/type/conflicting-names-input.ffg:6:30: + │ +6 │ in test : forall (a : Type). a + │ ↑ \ No newline at end of file