Skip to content

Commit 943e6a1

Browse files
committed
Remove Immut constraint, leaning on the monads for safety instead.
The safe-names system has been working well. Now that we have some experience with it we can reevaluate some of the choices. Specifically, we can ask where the `Immut` constraint, which add a fair bit of clutter, is actually necessary. It turns out that `Immut n` served three purposes: (1) It gated access to `getEnv` within env-carrying monads (2) It gated access to fresh binder creation, like `withFreshBinder` (3) It allowed us to work with non-sinkable `DistinctAbs` in some places Regarding (1), it turned out that we only used `getEnv` in order to immediately pass it to `runEnvReader` or similar, doing a little dance with `liftImmut` along the way. It's simpler to just use `liftEnvReader` instead. Instead of gating `getEnv` with `Immut`, we can just write it as `unsafeGetEnv` and be careful in the few places we need to use it. It turns out that (2) is more conservative than necessary. A `Binder n l` isn't injectable in `l` but it *is* injectable in `n`. So it should be legal to have a `withFreshBinder` that doesn't require `Immut n`. We still have to make sure that the `Distinct l` constraint doesn't leak back to the caller, but we can do that by hiding the distinctness constraint in the monad, just like we hide the scope/env. (However, this is not currently implemented! See below). Finally, (3) became a non-issue when we stopped using `DistinctAbs`. We only used `DistinctAbs` for performance reasons, to save needless refreshing. We replaced it with a dynamic distinctness check to trigger the fast path when possible. On top of all that, `Immut` was already broken, with `liftImmut` not providing the guarantees I'd originally thought (for example, it makes it very easy to have both `Immut n` and `Mut n` at the same time!). The reason to make this change now is that I want to add a bilevel version of InplaceT that emits to two different levels. We can reason about `Mut n` for each level, but `Immut` is harder. There is still one vulnerability: if you reify the `Distinct n` constraint by capturing it in a GADT, you can return it from functions like `withFreshBinder`, but it will no longer be valid if you subsequently emit more names. We could solve this by hiding `Distinct n` in the monad, just like we hide the scope/env. But this would mean we couldn't use the pure `sink` anymore. We'd have to use the monadic version, `sinkM` instead. But you have to be a bit devious to capture `Distinct n` in a GADT (especially now that `DistinctAbs` isn't a thing anymore). So maybe this is a reasonable convenience-security trade-off?
1 parent b9e6874 commit 943e6a1

File tree

16 files changed

+125
-325
lines changed

16 files changed

+125
-325
lines changed

src/lib/Builder.hs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class (EnvReader m, EnvExtender m, Fallible1 m)
8989

9090
class Builder m => ScopableBuilder (m::MonadKind1) where
9191
buildScoped
92-
:: (SinkableE e, Immut n)
92+
:: SinkableE e
9393
=> (forall l. (Emits l, DExt n l) => m l (e l))
9494
-> m n (Abs (Nest Decl) e n)
9595

@@ -138,7 +138,7 @@ class (EnvReader m, MonadFail1 m)
138138
emitBinding :: Mut n => Color c => NameHint -> Binding c n -> m n (Name c n)
139139
emitEnv :: (Mut n, SinkableE e, SubstE Name e) => Abs TopEnvFrag e n -> m n (e n)
140140
emitNamelessEnv :: TopEnvFrag n n -> m n ()
141-
localTopBuilder :: (Immut n, SinkableE e)
141+
localTopBuilder :: SinkableE e
142142
=> (forall l. (Mut l, DExt n l) => m l (e l))
143143
-> m n (Abs TopEnvFrag e n)
144144

@@ -216,7 +216,6 @@ runTopBuilderT
216216
-> TopBuilderT m n a
217217
-> m a
218218
runTopBuilderT bindings cont = do
219-
Immut <- return $ toImmutEvidence bindings
220219
liftM snd $ runInplaceT bindings $ runTopBuilderT' $ cont
221220

222221
type TopBuilder2 (m :: MonadKind2) = forall i. TopBuilder (m i)
@@ -239,7 +238,6 @@ type BuilderM = BuilderT HardFailM
239238
liftBuilderT :: (Fallible m, EnvReader m') => BuilderT m n a -> m' n (m a)
240239
liftBuilderT cont = do
241240
env <- unsafeGetEnv
242-
Immut <- return $ toImmutEvidence env
243241
Distinct <- getDistinct
244242
return do
245243
(Empty, result) <- runInplaceT env $ runBuilderT' cont
@@ -345,7 +343,7 @@ buildBlock
345343
:: ScopableBuilder m
346344
=> (forall l. (Emits l, DExt n l) => m l (Atom l))
347345
-> m n (Block n)
348-
buildBlock cont = liftImmut do
346+
buildBlock cont = do
349347
Abs decls results <- buildScoped do
350348
result <- cont
351349
ty <- cheapNormalize =<< getType result
@@ -411,10 +409,10 @@ buildNullaryPi effs cont =
411409
buildLamGeneral
412410
:: ScopableBuilder m
413411
=> NameHint -> Arrow -> Type n
414-
-> (forall l. (Immut l, DExt n l) => AtomName l -> m l (EffectRow l))
412+
-> (forall l. DExt n l => AtomName l -> m l (EffectRow l))
415413
-> (forall l. (Emits l, DExt n l) => AtomName l -> m l (Atom l))
416414
-> m n (Atom n)
417-
buildLamGeneral hint arr ty fEff fBody = liftImmut do
415+
buildLamGeneral hint arr ty fEff fBody = do
418416
withFreshBinder hint (LamBinding arr ty) \b -> do
419417
let v = binderName b
420418
effs <- fEff v
@@ -443,7 +441,7 @@ buildPi :: (Fallible1 m, Builder m)
443441
=> NameHint -> Arrow -> Type n
444442
-> (forall l. DExt n l => AtomName l -> m l (EffectRow l, Type l))
445443
-> m n (PiType n)
446-
buildPi hint arr ty body = liftImmut do
444+
buildPi hint arr ty body = do
447445
withFreshPiBinder hint (PiBinding arr ty) \b -> do
448446
(effs, resultTy) <- body $ binderName b
449447
return $ PiType b effs resultTy
@@ -465,7 +463,7 @@ buildNaryPi (Abs (Nest (b:>ty) bs) UnitE) cont = do
465463
buildNonDepPi :: EnvReader m
466464
=> NameHint -> Arrow -> Type n -> EffectRow n -> Type n
467465
-> m n (PiType n)
468-
buildNonDepPi hint arr argTy effs resultTy = liftImmut $ liftBuilder do
466+
buildNonDepPi hint arr argTy effs resultTy = liftBuilder do
469467
argTy' <- sinkM argTy
470468
buildPi hint arr argTy' \_ -> do
471469
resultTy' <- sinkM resultTy
@@ -477,9 +475,9 @@ buildAbs
477475
, SinkableE e, Color c, ToBinding binding c)
478476
=> NameHint
479477
-> binding n
480-
-> (forall l. (Immut l, DExt n l) => Name c l -> m l (e l))
478+
-> (forall l. DExt n l => Name c l -> m l (e l))
481479
-> m n (Abs (BinderP c binding) e n)
482-
buildAbs hint binding cont = liftImmut do
480+
buildAbs hint binding cont = do
483481
withFreshBinder hint binding \b -> do
484482
body <- cont $ binderName b
485483
return $ Abs (b:>binding) body
@@ -496,9 +494,9 @@ varsAsBinderNest (v:vs) = do
496494
return $ EmptyAbs (Nest (b:>ty) bs)
497495

498496
typesAsBinderNest :: EnvReader m => [Type n] -> m n (EmptyAbs (Nest Binder) n)
499-
typesAsBinderNest types = liftImmut $ liftEnvReaderM $ go types
497+
typesAsBinderNest types = liftEnvReaderM $ go types
500498
where
501-
go :: forall n. Immut n => [Type n] -> EnvReaderM n (EmptyAbs (Nest Binder) n)
499+
go :: forall n. [Type n] -> EnvReaderM n (EmptyAbs (Nest Binder) n)
502500
go tys = case tys of
503501
[] -> return $ Abs Empty UnitE
504502
ty:rest -> withFreshBinder NoHint ty \b -> do
@@ -514,12 +512,11 @@ singletonBinderNest hint ty = do
514512
buildNaryAbs
515513
:: (ScopableBuilder m, SinkableE e, SubstE Name e, SubstE AtomSubstVal e, HoistableE e)
516514
=> EmptyAbs (Nest Binder) n
517-
-> (forall l. (Immut l, Distinct l, DExt n l) => [AtomName l] -> m l (e l))
515+
-> (forall l. DExt n l => [AtomName l] -> m l (e l))
518516
-> m n (Abs (Nest Binder) e n)
519-
buildNaryAbs (EmptyAbs Empty) body =
520-
liftImmut do
521-
Distinct <- getDistinct
522-
Abs Empty <$> body []
517+
buildNaryAbs (EmptyAbs Empty) body = do
518+
Distinct <- getDistinct
519+
Abs Empty <$> body []
523520
buildNaryAbs (EmptyAbs (Nest (b:>ty) bs)) body = do
524521
Abs b' (Abs bs' body') <-
525522
buildAbs (getNameHint b) ty \v -> do
@@ -606,7 +603,7 @@ buildUnaryAtomAlt ty body = do
606603
buildNewtype :: ScopableBuilder m
607604
=> SourceName
608605
-> EmptyAbs (Nest Binder) n
609-
-> (forall l. (Immut l, DExt n l) => [AtomName l] -> m l (Type l))
606+
-> (forall l. DExt n l => [AtomName l] -> m l (Type l))
610607
-> m n (DataDef n)
611608
buildNewtype name paramBs body = do
612609
Abs paramBs' argBs <- buildNaryAbs paramBs \params -> do
@@ -848,7 +845,7 @@ zipPiBinders :: [Arrow] -> Nest Binder i i' -> Nest PiBinder i i'
848845
zipPiBinders = zipNest \arr (b :> ty) -> PiBinder b ty arr
849846

850847
makeSuperclassGetter :: EnvReader m => Name ClassNameC n -> Int -> m n (Atom n)
851-
makeSuperclassGetter classDefName methodIdx = liftImmut $ liftBuilder do
848+
makeSuperclassGetter classDefName methodIdx = liftBuilder do
852849
classDefName' <- sinkM classDefName
853850
ClassDef _ _ defName <- getClassDef classDefName'
854851
DataDef sourceName paramBs _ <- lookupDataDef defName
@@ -865,7 +862,7 @@ emitMethodType hint classDef explicit idx = do
865862
emitBinding hint $ MethodBinding classDef idx getter
866863

867864
makeMethodGetter :: EnvReader m => Name ClassNameC n -> [Bool] -> Int -> m n (Atom n)
868-
makeMethodGetter classDefName explicit methodIdx = liftImmut $ liftBuilder do
865+
makeMethodGetter classDefName explicit methodIdx = liftBuilder do
869866
classDefName' <- sinkM classDefName
870867
ClassDef _ _ defName <- getClassDef classDefName'
871868
DataDef sourceName paramBs _ <- lookupDataDef defName
@@ -1144,7 +1141,7 @@ reduceE monoid xs = liftEmitBuilder do
11441141
emitOp $ PrimEffect (sink $ Var ref) $ MExtend (fmap sink monoid) x
11451142

11461143
andMonoid :: EnvReader m => m n (BaseMonoid n)
1147-
andMonoid = liftM (BaseMonoid TrueAtom) $ liftImmut do
1144+
andMonoid = liftM (BaseMonoid TrueAtom) do
11481145
liftBuilder $
11491146
buildLam "_" PlainArrow BoolTy Pure \x ->
11501147
buildLam "_" PlainArrow BoolTy Pure \y -> do
@@ -1242,14 +1239,14 @@ telescopicCapture bs e = do
12421239
vs <- localVarsAndTypeVars bs e
12431240
vTys <- mapM (getType . Var) vs
12441241
let (vsSorted, tys) = unzip $ toposortAnnVars $ zip vs vTys
1245-
ty <- liftImmut $ liftEnvReaderM $ buildTelescopeTy vs tys
1242+
ty <- liftEnvReaderM $ buildTelescopeTy vs tys
12461243
result <- buildTelescopeVal (map Var vsSorted) ty
12471244
let ty' = ignoreHoistFailure $ hoist bs ty
12481245
let ab = ignoreHoistFailure $ hoist bs $ abstractFreeVarsNoAnn vsSorted e
12491246
return (result, ty', ab)
12501247

12511248
-- XXX: assumes arguments are toposorted
1252-
buildTelescopeTy :: (EnvReader m, EnvExtender m, Immut n)
1249+
buildTelescopeTy :: (EnvReader m, EnvExtender m)
12531250
=> [AtomName n] -> [Type n] -> m n (Type n)
12541251
buildTelescopeTy [] [] = return UnitTy
12551252
buildTelescopeTy (v:vs) (ty:tys) = do

src/lib/CheapReduction.hs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ newtype CheapReducerM (i :: S) (o :: S) (a :: *) =
6262
(EnvReaderT Identity)))) i o a)
6363
deriving ( Functor, Applicative, Monad, Alternative
6464
, EnvReader, ScopeReader, EnvExtender
65-
, SubstReader AtomSubstVal, AlwaysImmut )
65+
, SubstReader AtomSubstVal)
6666

6767
newtype FailedDictTypes (n::S) = FailedDictTypes (MaybeE (ESet Type) n)
6868
deriving (SinkableE, HoistableE)
@@ -76,7 +76,7 @@ instance Monoid (FailedDictTypes n) where
7676
instance FallibleMonoid1 FailedDictTypes where
7777
mfail = FailedDictTypes $ NothingE
7878

79-
class ( Alternative2 m, SubstReader AtomSubstVal m, AlwaysImmut2 m
79+
class ( Alternative2 m, SubstReader AtomSubstVal m
8080
, EnvReader2 m, EnvExtender2 m) => CheapReducer m where
8181
reportSynthesisFail :: Type o -> m i o ()
8282
updateCache :: AtomName o -> Maybe (Atom o) -> m i o ()
@@ -101,7 +101,7 @@ liftCheapReducerM subst (CheapReducerM m) = do
101101

102102
cheapReduceFromSubst
103103
:: forall e m i o .
104-
( SubstReader AtomSubstVal m, EnvReader2 m, AlwaysImmut2 m
104+
( SubstReader AtomSubstVal m, EnvReader2 m
105105
, SinkableE e, SubstE AtomSubstVal e, HoistableE e)
106106
=> e i -> m i o (e o)
107107
cheapReduceFromSubst e = traverseNames cheapSubstName =<< substM e
@@ -136,7 +136,6 @@ cheapReduceWithDeclsRec decls cont = case decls of
136136
optional (cheapReduceE expr) >>= \case
137137
Nothing -> do
138138
binding' <- substM binding
139-
Immut <- getImmut
140139
withFreshBinder (getNameHint b) binding' \b' -> do
141140
updateCache (binderName b') Nothing
142141
extendSubst (b@>Rename (binderName b')) do

src/lib/GenericTraversal.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@ class (ScopableBuilder2 m, SubstReader Name m)
2828
traverseExpr :: Emits o => Expr i -> m i o (Expr o)
2929
traverseExpr = traverseExprDefault
3030

31-
traverseAtom :: Immut o => Atom i -> m i o (Atom o)
31+
traverseAtom :: Atom i -> m i o (Atom o)
3232
traverseAtom = traverseAtomDefault
3333

3434
traverseExprDefault :: Emits o => GenericTraverser m => Expr i -> m i o (Expr o)
35-
traverseExprDefault expr = liftImmut $ case expr of
35+
traverseExprDefault expr = case expr of
3636
App g xs -> App <$> tge g <*> mapM tge xs
3737
Atom x -> Atom <$> tge x
3838
Op op -> Op <$> mapM tge op
3939
Hof hof -> Hof <$> mapM tge hof
4040
Case scrut alts resultTy effs ->
4141
Case <$> tge scrut <*> mapM traverseAlt alts <*> tge resultTy <*> substM effs
4242

43-
traverseAtomDefault :: Immut o => GenericTraverser m => Atom i -> m i o (Atom o)
43+
traverseAtomDefault :: GenericTraverser m => Atom i -> m i o (Atom o)
4444
traverseAtomDefault atom = case atom of
4545
Var v -> Var <$> substM v
4646
Lam (LamExpr (LamBinder b ty arr eff) body) -> do
@@ -81,12 +81,12 @@ traverseAtomDefault atom = case atom of
8181
ProjectElt _ _ -> substM atom
8282
_ -> error $ "not implemented: " ++ pprint atom
8383

84-
tge :: (Immut o, GenericallyTraversableE e, GenericTraverser m)
84+
tge :: (GenericallyTraversableE e, GenericTraverser m)
8585
=> e i -> m i o (e o)
8686
tge = traverseGenericE
8787

8888
class GenericallyTraversableE (e::E) where
89-
traverseGenericE :: Immut o => GenericTraverser m => e i -> m i o (e o)
89+
traverseGenericE :: GenericTraverser m => e i -> m i o (e o)
9090

9191
instance GenericallyTraversableE Atom where
9292
traverseGenericE = traverseAtom
@@ -121,7 +121,7 @@ traverseDeclNest (Nest (Let b (DeclBinding ann _ expr)) rest) cont = do
121121
extendSubst (b @> v) $ traverseDeclNest rest cont
122122

123123
traverseAlt
124-
:: (Immut o, GenericTraverser m)
124+
:: GenericTraverser m
125125
=> Alt i
126126
-> m i o (Alt o)
127127
traverseAlt (Abs Empty body) = Abs Empty <$> tge body

0 commit comments

Comments
 (0)