1- -- |
2- -- Functions relating to skolemization used during typechecking
3- --
1+ -- | Functions relating to skolemization used during typechecking
42module Language.PureScript.TypeChecker.Skolems
53 ( newSkolemConstant
64 , introduceSkolemScope
@@ -14,115 +12,119 @@ import Prelude.Compat
1412
1513import Control.Monad.Error.Class (MonadError (.. ))
1614import Control.Monad.State.Class (MonadState (.. ), gets , modify )
17-
15+ import Data.Foldable ( traverse_ )
1816import Data.Functor.Identity (Identity (), runIdentity )
19- import Data.List (nub , (\\) )
2017import Data.Monoid
18+ import Data.Set (Set , fromList , notMember )
2119import Data.Text (Text )
22-
2320import Language.PureScript.AST
2421import Language.PureScript.Crash
2522import Language.PureScript.Errors
2623import Language.PureScript.Traversals (defS )
2724import Language.PureScript.TypeChecker.Monad
2825import Language.PureScript.Types
2926
30- -- |
31- -- Generate a new skolem constant
32- --
33- newSkolemConstant :: (MonadState CheckState m ) => m Int
27+ -- | Generate a new skolem constant
28+ newSkolemConstant :: MonadState CheckState m => m Int
3429newSkolemConstant = do
3530 s <- gets checkNextSkolem
3631 modify $ \ st -> st { checkNextSkolem = s + 1 }
3732 return s
3833
39- -- |
40- -- Introduce skolem scope at every occurence of a ForAll
41- --
42- introduceSkolemScope :: (MonadState CheckState m ) => Type -> m Type
34+ -- | Introduce skolem scope at every occurence of a ForAll
35+ introduceSkolemScope :: MonadState CheckState m => Type -> m Type
4336introduceSkolemScope = everywhereOnTypesM go
4437 where
4538 go (ForAll ident ty Nothing ) = ForAll ident ty <$> (Just <$> newSkolemScope)
4639 go other = return other
4740
48- -- |
49- -- Generate a new skolem scope
50- --
51- newSkolemScope :: (MonadState CheckState m ) => m SkolemScope
41+ -- | Generate a new skolem scope
42+ newSkolemScope :: MonadState CheckState m => m SkolemScope
5243newSkolemScope = do
5344 s <- gets checkNextSkolemScope
5445 modify $ \ st -> st { checkNextSkolemScope = s + 1 }
5546 return $ SkolemScope s
5647
57- -- |
58- -- Skolemize a type variable by replacing its instances with fresh skolem constants
59- --
48+ -- | Skolemize a type variable by replacing its instances with fresh skolem constants
6049skolemize :: Text -> Int -> SkolemScope -> Maybe SourceSpan -> Type -> Type
6150skolemize ident sko scope ss = replaceTypeVars ident (Skolem ident sko scope ss)
6251
63- -- |
64- -- This function has one purpose - to skolemize type variables appearing in a
65- -- DeferredDictionary placeholder. These type variables are somewhat unique since they are the
66- -- only example of scoped type variables.
67- --
52+ -- | This function skolemizes type variables appearing in any type signatures or
53+ -- 'DeferredDictionary' placeholders. These type variables are the only places
54+ -- where scoped type variables can appear in expressions.
6855skolemizeTypesInValue :: Text -> Int -> SkolemScope -> Maybe SourceSpan -> Expr -> Expr
6956skolemizeTypesInValue ident sko scope ss =
70- let
71- (_, f, _, _, _) = everywhereWithContextOnValuesM [] defS onExpr onBinder defS defS
72- in runIdentity . f
57+ runIdentity . onExpr'
7358 where
74- onExpr :: [Text ] -> Expr -> Identity ([Text ], Expr )
75- onExpr sco (DeferredDictionary c ts)
76- | ident `notElem` sco = return (sco, DeferredDictionary c (map (skolemize ident sko scope ss) ts))
77- onExpr sco (TypedValue check val ty)
78- | ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedValue check val (skolemize ident sko scope ss ty))
79- onExpr sco other = return (sco, other)
80-
81- onBinder :: [Text ] -> Binder -> Identity ([Text ], Binder )
82- onBinder sco (TypedBinder ty b)
83- | ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedBinder (skolemize ident sko scope ss ty) b)
84- onBinder sco other = return (sco, other)
85-
86- peelTypeVars :: Type -> [Text ]
87- peelTypeVars (ForAll i ty _) = i : peelTypeVars ty
88- peelTypeVars _ = []
89-
90- -- |
91- -- Ensure skolem variables do not escape their scope
59+ onExpr' :: Expr -> Identity Expr
60+ (_, onExpr', _, _, _) = everywhereWithContextOnValuesM [] defS onExpr onBinder defS defS
61+
62+ onExpr :: [Text ] -> Expr -> Identity ([Text ], Expr )
63+ onExpr sco (DeferredDictionary c ts)
64+ | ident `notElem` sco = return (sco, DeferredDictionary c (map (skolemize ident sko scope ss) ts))
65+ onExpr sco (TypedValue check val ty)
66+ | ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedValue check val (skolemize ident sko scope ss ty))
67+ onExpr sco other = return (sco, other)
68+
69+ onBinder :: [Text ] -> Binder -> Identity ([Text ], Binder )
70+ onBinder sco (TypedBinder ty b)
71+ | ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedBinder (skolemize ident sko scope ss ty) b)
72+ onBinder sco other = return (sco, other)
73+
74+ peelTypeVars :: Type -> [Text ]
75+ peelTypeVars (ForAll i ty _) = i : peelTypeVars ty
76+ peelTypeVars _ = []
77+
78+ -- | Ensure skolem variables do not escape their scope
9279--
93- skolemEscapeCheck :: (MonadError MultipleErrors m ) => Expr -> m ()
80+ -- Every skolem variable is created when a 'ForAll' type is skolemized.
81+ -- This determines the scope of that skolem variable, which is copied from
82+ -- the 'SkolemScope' field of the 'ForAll' constructor.
83+ --
84+ -- This function traverses the tree top-down, and collects any 'SkolemScope's
85+ -- introduced by 'ForAll's. If a 'Skolem' is encountered whose 'SkolemScope' is
86+ -- not in the current list, then we have found an escaped skolem variable.
87+ skolemEscapeCheck :: MonadError MultipleErrors m => Expr -> m ()
9488skolemEscapeCheck (TypedValue False _ _) = return ()
95- skolemEscapeCheck root@ TypedValue {} =
96- -- Every skolem variable is created when a ForAll type is skolemized.
97- -- This determines the scope of that skolem variable, which is copied from the SkolemScope
98- -- field of the ForAll constructor.
99- -- We traverse the tree top-down, and collect any SkolemScopes introduced by ForAlls.
100- -- If a Skolem is encountered whose SkolemScope is not in the current list, we have found
101- -- an escaped skolem variable.
102- let (_, f, _, _, _) = everythingWithContextOnValues [] [] (++) def go def def def
103- in case f root of
104- [] -> return ()
105- ((binding, val) : _) -> throwError . singleError $ ErrorMessage [ ErrorInExpression val ] $ EscapedSkolem binding
89+ skolemEscapeCheck expr@ TypedValue {} =
90+ traverse_ (throwError . singleError) (toSkolemErrors expr)
10691 where
107- def s _ = (s, [] )
108-
109- go :: [(SkolemScope , Expr )] -> Expr -> ([(SkolemScope , Expr )], [(Maybe Expr , Expr )])
110- go scos val@ (TypedValue _ _ (ForAll _ _ (Just sco))) = ((sco, val) : scos, [] )
111- go scos val@ (TypedValue _ _ ty) = case collectSkolems ty \\ map fst scos of
112- (sco : _) -> (scos, [(findBindingScope sco, val)])
113- _ -> (scos, [] )
114- where
115- collectSkolems :: Type -> [SkolemScope ]
116- collectSkolems = nub . everythingOnTypes (++) collect
92+ toSkolemErrors :: Expr -> [ErrorMessage ]
93+ (_, toSkolemErrors, _, _, _) = everythingWithContextOnValues (mempty , Nothing ) [] (<>) def go def def def
94+
95+ def s _ = (s, [] )
96+
97+ go :: (Set SkolemScope , Maybe SourceSpan )
98+ -> Expr
99+ -> ((Set SkolemScope , Maybe SourceSpan ), [ErrorMessage ])
100+ go (scopes, _) (PositionedValue ss _ _) = ((scopes, Just ss), [] )
101+ go (scopes, ssUsed) val@ (TypedValue _ _ ty) =
102+ ( (allScopes, ssUsed)
103+ , [ ErrorMessage (maybe id ((:) . PositionedError ) ssUsed [ ErrorInExpression val ]) $
104+ EscapedSkolem name ssBound ty
105+ | (name, scope, ssBound) <- collectSkolems ty
106+ , notMember scope allScopes
107+ ]
108+ )
117109 where
118- collect (Skolem _ _ scope _) = [scope]
119- collect _ = []
120- go scos _ = (scos, [] )
121- findBindingScope :: SkolemScope -> Maybe Expr
122- findBindingScope sco =
123- let (_, f, _, _, _) = everythingOnValues mappend (const mempty ) go' (const mempty ) (const mempty ) (const mempty )
124- in getFirst $ f root
125- where
126- go' val@ (TypedValue _ _ (ForAll _ _ (Just sco'))) | sco == sco' = First (Just val)
127- go' _ = mempty
128- skolemEscapeCheck _ = internalError " Untyped value passed to skolemEscapeCheck"
110+ -- Any new skolem scopes introduced by universal quantifiers
111+ newScopes :: [SkolemScope ]
112+ newScopes = collectScopes ty
113+
114+ -- All scopes, including new scopes
115+ allScopes :: Set SkolemScope
116+ allScopes = fromList newScopes <> scopes
117+
118+ -- Collect any scopes appearing in quantifiers at the top level
119+ collectScopes :: Type -> [SkolemScope ]
120+ collectScopes (ForAll _ t (Just sco)) = sco : collectScopes t
121+ collectScopes ForAll {} = internalError " skolemEscapeCheck: No skolem scope"
122+ collectScopes _ = []
123+
124+ -- Collect any skolem variables appearing in a type
125+ collectSkolems :: Type -> [(Text , SkolemScope , Maybe SourceSpan )]
126+ collectSkolems = everythingOnTypes (++) collect where
127+ collect (Skolem name _ scope srcSpan) = [(name, scope, srcSpan)]
128+ collect _ = []
129+ go scos _ = (scos, [] )
130+ skolemEscapeCheck _ = internalError " skolemEscapeCheck: untyped value"
0 commit comments