on master help sign in
Merge pull request #2028 from david-christiansen/issue/1172

Prevent variable capture when desugaring custom syntax

commitcea78700e26329a7a9b4d1b91ad1d78718c3150a authorDavid Christiansen <david@davidchristiansen.dk> dateMon 23 Mar 2015, 10:06 (12 years ago) parent9dc710591acb3a
7 files changed+72−5
CHANGELOG+6 idris.cabal+3 libs/effects/Effect/File.idr+4−3 src/Idris/Parser.hs+47−2 test/reg061/Capture.idr+9 test/reg061/expected test/reg061/run+3
CHANGELOG+6
@@ -1,3 +1,9 @@
1+New in 0.9.18:
2+--------------
3+* Syntax rules no longer perform variable capture. Users of effects will
4+ need to explicitly name results in dependent effect signatures instead
5+ of using the default name "result".
6+
17New in 0.9.17:
28--------------
39* The --ideslave command line option has been replaced with a --ide-mode
idris.cabal+3
@@ -301,6 +301,9 @@ Extra-source-files:
301301 test/reg060/run
302302 test/reg060/*.idr
303303 test/reg060/expected
304+ test/reg061/run
305+ test/reg061/*.idr
306+ test/reg061/expected
304307
305308 test/basic001/run
306309 test/basic001/*.idr
libs/effects/Effect/File.idr+4−3
@@ -117,9 +117,10 @@ FILE_IO t = MkEff t FileIO
117117||| @ m The file mode.
118118open : (fname : String)
119119 -> (m : Mode)
120− -> { [FILE_IO ()] ==> [FILE_IO (if result
121− then OpenFile m
122− else ())] } Eff Bool
120+ -> { [FILE_IO ()] ==> {result}
121+ [FILE_IO (if result
122+ then OpenFile m
123+ else ())] } Eff Bool
123124open f m = call $ Open f m
124125
125126
src/Idris/Parser.hs+47−2
@@ -59,6 +59,7 @@ import Data.List
5959import Data.Monoid
6060import Data.Char
6161import Data.Ord
62+import Data.Generics.Uniplate.Data (descendM)
6263import qualified Data.Map as M
6364import qualified Data.HashSet as HS
6465import qualified Data.Text as T
@@ -276,7 +277,7 @@ syntaxRule syn
276277 when (length ns /= length (nub ns))
277278 $ unexpected "repeated variable in syntax rule"
278279 lchar '='
279− tm <- typeExpr (allowImp syn)
280+ tm <- typeExpr (allowImp syn) >>= uniquifyBinders [n | Binding n <- syms]
280281 terminator
281282 return (Rule (mkSimple syms) tm sty)
282283 where
@@ -297,7 +298,51 @@ syntaxRule syn
297298 where ts = dropWhile isSpace . dropWhileEnd isSpace $ s
298299 mkSimple' (e : es) = e : mkSimple' es
299300 mkSimple' [] = []
300−
301+
302+ -- Prevent syntax variable capture by making all binders under syntax unique
303+ -- (the ol' Common Lisp GENSYM approach)
304+ uniquifyBinders :: [Name] -> PTerm -> IdrisParser PTerm
305+ uniquifyBinders userNames = fixBind []
306+ where
307+ fixBind :: [(Name, Name)] -> PTerm -> IdrisParser PTerm
308+ fixBind rens (PRef fc n) | Just n' <- lookup n rens =
309+ return $ PRef fc n'
310+ fixBind rens (PPatvar fc n) | Just n' <- lookup n rens =
311+ return $ PPatvar fc n'
312+ fixBind rens (PLam fc n ty body)
313+ | n `elem` userNames = liftM2 (PLam fc n) (fixBind rens ty) (fixBind rens body)
314+ | otherwise =
315+ do ty' <- fixBind rens ty
316+ n' <- gensym n
317+ body' <- fixBind ((n,n'):rens) body
318+ return $ PLam fc n' ty' body'
319+ fixBind rens (PPi plic n argTy body)
320+ | n `elem` userNames = liftM2 (PPi plic n) (fixBind rens argTy) (fixBind rens body)
321+ | otherwise =
322+ do ty' <- fixBind rens argTy
323+ n' <- gensym n
324+ body' <- fixBind ((n,n'):rens) body
325+ return $ (PPi plic n' ty' body')
326+ fixBind rens (PLet fc n ty val body)
327+ | n `elem` userNames = liftM3 (PLet fc n)
328+ (fixBind rens ty)
329+ (fixBind rens val)
330+ (fixBind rens body)
331+ | otherwise =
332+ do ty' <- fixBind rens ty
333+ val' <- fixBind rens val
334+ n' <- gensym n
335+ body' <- fixBind ((n,n'):rens) body
336+ return $ (PLet fc n' ty' val' body')
337+ fixBind rens (PMatchApp fc n) | Just n' <- lookup n rens =
338+ return $ PMatchApp fc n'
339+ fixBind rens x = descendM (fixBind rens) x
340+
341+ gensym :: Name -> IdrisParser Name
342+ gensym n = do ist <- get
343+ let idx = idris_name ist
344+ put ist { idris_name = idx + 1 }
345+ return $ sMN idx (show n)
301346
302347{- | Parses a syntax symbol (either binding variable, keyword or expression)
303348
test/reg061/Capture.idrnew file+9
@@ -0,0 +1,9 @@
1+module Capture
2+
3+-- Test for variable capture in syntax
4+
5+syntax delay [x] = \z => case z of { () => x }
6+syntax delay' [z] = delay z
7+
8+capture : a -> () -> a
9+capture x = delay' x
test/reg061/expectednew file

No changes to the content.

test/reg061/runnew file+3
@@ -0,0 +1,3 @@
1+#!/usr/bin/env bash
2+idris $@ Capture.idr --check
3+rm -f *.ibc