@@ -34,7 +34,6 @@ import Types.Core
3434import Types.Imp
3535import Types.Primitives
3636import Types.Source
37- import Util (forMZipped_ )
3837
3938-- === top-level API ===
4039
@@ -62,6 +61,7 @@ class ( Monad2 m, Fallible2 m, SubstReader Name m
6261 , EnvReader2 m , EnvExtender2 m )
6362 => Typer (m :: MonadKind2 ) (r :: IR ) | m -> r where
6463 affineUsed :: AtomName r o -> m i o ()
64+ parallelAffines_ :: [m i o () ] -> m i o ()
6565
6666newtype TyperT (m :: MonadKind ) (r :: IR ) (i :: S ) (o :: S ) (a :: * ) =
6767 TyperT { runTyperT' :: SubstReaderT Name (StateT1 (NameMap (AtomNameC r ) Int ) (EnvReaderT m )) i o a }
@@ -81,18 +81,7 @@ liftTyperT cont =
8181{-# INLINE liftTyperT #-}
8282
8383instance Fallible m => Typer (TyperT m r ) r where
84- -- TODO Should be able to use an affine variable in each branch of a `case`,
85- -- but this abstraction can't capture that. One solution could be to
86- -- - Add an -- `isolated` operation of type
87- -- isolated :: m i o () -> m i o <name-usage-map>
88- -- which doesn't change the state in the monad, but returns the delta that
89- -- the underlying action tried to add. (Maybe I can even implement this
90- -- generically if the state is a group?)
91- -- - Add a `mergeNameMap :: <name-usage-map> -> m i o ()` operation,
92- -- which would check each key for being used too many times.
93- -- - Then `case` checks each arm in isolation, zips the maps with maximum,
94- -- and then calls `mergeNameMap` on the result.
95- -- I also can't make up my mind whether a `Seq` loop should be allowed to
84+ -- I can't make up my mind whether a `Seq` loop should be allowed to
9685 -- close over a dest from an enclosing scope. Status quo permits this.
9786 affineUsed name = TyperT $ do
9887 affines <- get
@@ -102,6 +91,24 @@ instance Fallible m => Typer (TyperT m r) r where
10291 else
10392 put $ insertNameMap name (n + 1 ) affines
10493 Nothing -> put $ insertNameMap name 1 affines
94+ parallelAffines_ actions = TyperT $ do
95+ -- This method permits using an affine variable in each branch of a `case`.
96+ -- We check each `case` branch in isolation, detecting affine overuse within
97+ -- the branch; then we check whether the union of the variables used in the
98+ -- branches reuses a variable from outside that it shouldn't.
99+ -- This has the down-side of localizing such an error to the case rather
100+ -- than to the offending in-branch use, but that can be improved later.
101+ affines <- get
102+ isolateds <- forM actions \ act -> do
103+ put mempty
104+ runTyperT' act
105+ get
106+ put affines
107+ forM_ (toListNameMap $ unionsWithNameMap max isolateds) \ (name, ct) ->
108+ case ct of
109+ 0 -> return ()
110+ 1 -> runTyperT' $ affineUsed name
111+ _ -> error $ " Unexpected multi-used affine name " ++ show name ++ " from case branches."
105112
106113-- === typeable things ===
107114
@@ -721,8 +728,8 @@ checkCase :: (Typer m r, IRRep r) => Atom r i -> [Alt r i] -> Type r o -> Effect
721728checkCase scrut alts resultTy effs = do
722729 scrutTy <- getTypeE scrut
723730 altsBinderTys <- checkCaseAltsBinderTys scrutTy
724- forMZipped_ alts altsBinderTys \ alt bs ->
725- checkAlt resultTy bs effs alt
731+ parallelAffines_ $ zipWith ( \ alt bs ->
732+ checkAlt resultTy bs effs alt) alts altsBinderTys
726733
727734checkCaseAltsBinderTys :: (Fallible1 m , EnvReader m , IRRep r ) => Type r n -> m n [Type r n ]
728735checkCaseAltsBinderTys ty = case ty of
0 commit comments