@@ -23,6 +23,7 @@ import Data.Typeable hiding (typeRep)
2323import Type.Reflection (typeRep )
2424
2525import Control.Monad (when )
26+ import Data.Functor
2627import Data.List.Extra (unsnoc )
2728import Data.Function (on )
2829import Data.Foldable
@@ -41,6 +42,9 @@ import Act.Syntax.Untyped (makeIface)
4142import Act.Error
4243
4344import Data.Type.Equality (TestEquality (.. ))
45+ import Data.Singletons
46+
47+ import Data.Aeson.Types (typeMismatch )
4448
4549
4650type Err = Error String
@@ -352,7 +356,7 @@ checkDefn pn env@Env{contract} keyType vt@(FromVType valType) name (U.Defn k val
352356-- | Type checks a postcondition, returning typed versions of its storage updates and return expression.
353357checkPost :: Env -> U. Post -> Err ([StorageUpdate ], Maybe (TypedExp Timed ))
354358checkPost env (U. Post storage maybeReturn) = do
355- returnexp <- traverse (typedExp env) maybeReturn
359+ returnexp <- traverse (inferExpr env) maybeReturn
356360 storage' <- checkEntries storage
357361 pure (storage', returnexp)
358362 where
@@ -413,6 +417,10 @@ checkIffs env = foldr check (pure [])
413417 check (U. Iff _ exps) acc = mappend <$> traverse (checkExpr env SBoolean ) exps <*> acc
414418 check (U. IffIn _ typ exps) acc = mappend <$> (mconcat <$> traverse (fmap (genInRange typ) . checkExpr env SInteger ) exps) <*> acc
415419
420+
421+ -- | If an `inrange e` predicate appears in the source code, then the inrange
422+ -- predicate is propagated to all subexpressions of `e`. This elaboration step
423+ -- happens here.
416424genInRange :: AbiType -> Exp AInteger t -> [Exp ABoolean t ]
417425genInRange t e@ (LitInt _ _) = [InRange nowhere t e]
418426genInRange t e@ (TEntry _ _ _ _) = [InRange nowhere t e]
@@ -432,111 +440,111 @@ genInRange _ (ITE _ _ _ _) = error "Internal error: invalid range expression"
432440
433441-- | Attempt to construct a `TypedExp` whose type matches the supplied `ValueType`.
434442-- The target timing parameter will be whatever is required by the caller.
435- checkExprVType :: Typeable t => Env -> U. Expr -> ValueType -> Err (TypedExp t )
443+ checkExprVType :: forall t . Typeable t => Env -> U. Expr -> ValueType -> Err (TypedExp t )
436444checkExprVType env e (FromVType typ) = TExp typ <$> checkExpr env typ e
437445
438- -- | Attempt to typecheck an untyped expression as any possible type.
439- typedExp :: Typeable t => Env -> U. Expr -> Err (TypedExp t )
440- typedExp env e = findSuccess (throw (getPosn e, " Cannot find a valid type for expression " <> show e))
441- [ TExp SInteger <$> checkExpr env SInteger e
442- , TExp SBoolean <$> checkExpr env SBoolean e
443- , TExp SByteStr <$> checkExpr env SByteStr e
444- ]
445446
446- -- | Helper to create to create a conjunction out of a list of expressions
447- andExps :: [Exp ABoolean t ] -> Exp ABoolean t
448- andExps [] = LitBool nowhere True
449- andExps (c: cs) = foldr (And nowhere) c cs
447+ typeMismatchErr :: forall a b res . Pn -> SType a -> SType b -> Err res
448+ typeMismatchErr p t1 t2 = (throw (p, " Type " <> show t1 <> " should match type " <> show t2 <> " \n Env:\n " <> show env))
449+
450+ -- | Check is the given expression can be typed with the given type\\
451+ checkExpr :: forall t a . Typeable t => Env -> SType a -> U. Expr -> Err (Exp a t )
452+ checkExpr env t1 e =
453+ -- No idea why type annotation is required here
454+ (inferExpr env e :: Err (TypedExp t )) `bindValidation` (\ (TExp t2 te) ->
455+ maybe (typeMismatchErr (getPosn e) t1 t2) (\ Refl -> pure te) $ testEquality t1 t2)
450456
451- -- | Check the type of an expression and construct a typed expression
452- checkExpr :: forall a t . Typeable t => Env -> SType a -> U. Expr -> Err (Exp a t )
453- checkExpr env@ Env {constructors, calldata} typ e = case (typ, e) of
457+ -- | Attempt to infer a type of an expression. If succesfull returns an
458+ -- existential package of the infered typed together with the typed expression.
459+ inferExpr :: forall t . Typeable t => Env -> U. Expr -> Err (TypedExp t )
460+ inferExpr env@ Env {calldata, constructors} e = case e of
454461 -- Boolean expressions
455- ( SBoolean , U. ENot p v1) -> Neg p <$> checkExpr env SBoolean v1
456- ( SBoolean , U. EAnd p v1 v2) -> And p <$> checkExpr env SBoolean v1 <*> checkExpr env SBoolean v2
457- ( SBoolean , U. EOr p v1 v2) -> Or p <$> checkExpr env SBoolean v1 <*> checkExpr env SBoolean v2
458- ( SBoolean , U. EImpl p v1 v2) -> Impl p <$> checkExpr env SBoolean v1 <*> checkExpr env SBoolean v2
459- ( SBoolean , U. ELT p v1 v2) -> LT p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
460- ( SBoolean , U. ELEQ p v1 v2) -> LEQ p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
461- ( SBoolean , U. EGEQ p v1 v2) -> GEQ p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
462- ( SBoolean , U. EGT p v1 v2) -> GT p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
463- ( SBoolean , U. EEq p v1 v2) - > polycheck p Eq v1 v2
464- ( SBoolean , U. ENeq p v1 v2) - > polycheck p NEq v1 v2
465- ( SBoolean , U. BoolLit p v1) -> pure $ LitBool p v1
466- ( SBoolean , U. EInRange _ abityp v) -> andExps <$> genInRange abityp <$> checkExpr env SInteger v
462+ U. ENot p v1 -> wrapOp ( Neg p) <$> checkExpr env SBoolean v1
463+ U. EAnd p v1 v2 -> wrapOp2 ( And p) <$> checkExpr env SBoolean v1 <*> checkExpr env SBoolean v2
464+ U. EOr p v1 v2 -> wrapOp2 ( Or p) <$> checkExpr env SBoolean v1 <*> checkExpr env SBoolean v2
465+ U. EImpl p v1 v2 -> wrapOp2 ( Impl p) <$> checkExpr env SBoolean v1 <*> checkExpr env SBoolean v2
466+ U. ELT p v1 v2 -> wrapOp2 ( LT p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
467+ U. ELEQ p v1 v2 -> wrapOp2 ( LEQ p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
468+ U. EGEQ p v1 v2 -> wrapOp2 ( GEQ p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
469+ U. EGT p v1 v2 -> wrapOp2 ( GT p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
470+ U. EEq p v1 v2 -> TExp SBoolean <$ > polycheck p Eq v1 v2
471+ U. ENeq p v1 v2 -> TExp SBoolean <$ > polycheck p NEq v1 v2
472+ U. BoolLit p v1 -> pure $ TExp SBoolean ( LitBool p v1)
473+ U. EInRange _ abityp v -> TExp SBoolean . andExps <$> genInRange abityp <$> checkExpr env SInteger v
467474
468475 -- Arithemetic expressions
469- (SInteger , U. EAdd p v1 v2) -> Add p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
470- (SInteger , U. ESub p v1 v2) -> Sub p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
471- (SInteger , U. EMul p v1 v2) -> Mul p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
472- (SInteger , U. EDiv p v1 v2) -> Div p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
473- (SInteger , U. EMod p v1 v2) -> Mod p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
474- (SInteger , U. EExp p v1 v2) -> Exp p <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
475- (SInteger , U. IntLit p v1) -> pure $ LitInt p v1
476- -- Constructor calls
477- (SInteger , U. ECreate p c args) -> case Map. lookup c constructors of
478- Just ctrs ->
479- let (typs, ptrs) = unzip ctrs in
476+ U. EAdd p v1 v2 -> wrapOp2 (Add p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
477+ U. ESub p v1 v2 -> wrapOp2 (Sub p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
478+ U. EMul p v1 v2 -> wrapOp2 (Mul p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
479+ U. EDiv p v1 v2 -> wrapOp2 (Div p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
480+ U. EMod p v1 v2 -> wrapOp2 (Mod p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
481+ U. EExp p v1 v2 -> wrapOp2 (Exp p) <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
482+ U. IntLit p v1 -> pure $ TExp SInteger (LitInt p v1)
483+
484+ -- Constructor calls
485+ U. ECreate p c args -> case Map. lookup c constructors of
486+ Just sig ->
487+ let (typs, ptrs) = unzip sig in
488+ -- check the types of arguments to constructor call
480489 checkIxs env p args (fmap PrimitiveType typs) `bindValidation` (\ args' ->
481- Create p c args' <$ traverse_ (uncurry $ checkContractType env) (zip args' ptrs))
490+ -- then check that all arguments that need to be valid pointers to a contract have a contract type
491+ traverse_ (uncurry $ checkContractType env) (zip args' ptrs) $>
492+ TExp SInteger (Create p c args'))
482493 Nothing -> throw (p, " Unknown constructor " <> show c)
483494
484495 -- Control
485- (_, U. EITE p v1 v2 v3) ->
486- ((,) <$> checkExpr env typ v2 <*> checkExpr env typ v3) `bindValidation` (\ (e1, e2) -> do
487- b <- checkExpr env SBoolean v1
488- pure $ ITE p b e1 e2)
496+ U. EITE p e1 e2 e3 ->
497+ checkExpr env SBoolean e1 `bindValidation` \ b ->
498+ polycheck p (\ pn t te1 te2 -> TExp t (ITE pn b te1 te2)) e2 e3
489499
490500 -- Environment variables
491- (SInteger , U. EnvExp p v1) -> case lookup v1 globalEnv of
492- Just AInteger -> pure $ IntEnv p v1
493- Just AByteStr -> throw (p, " Environment variable " <> show v1 <> " has type bytestring but an expression of type integer is expected." )
494- _ -> throw (p, " Unknown environment variable " <> show v1)
495- (SByteStr , U. EnvExp p v1) -> case lookup v1 globalEnv of
496- Just AInteger -> throw (p, " Environment variable " <> show v1 <> " has type integer but an expression of type bytestring is expected." )
497- Just AByteStr -> pure $ ByEnv p v1
501+ U. EnvExp p v1 -> case lookup v1 globalEnv of
502+ Just AInteger -> pure $ TExp SInteger $ IntEnv p v1
503+ Just AByteStr -> pure $ TExp SByteStr $ ByEnv p v1
498504 _ -> throw (p, " Unknown environment variable " <> show v1)
499505
500506 -- Variable references
501507 -- Note: untimed entries in the untyped AST and in the typed AST have
502508 -- different meanings. Calldata variables are always untimed in the untimed
503509 -- AST but they become timed (with pre) in the typed AST whene they are used
504510 -- in a timed context.
505- (_, U. EUTEntry entry) | isCalldataEntry entry -> checkVar entry
506- (_, U. EPreEntry entry) | isCalldataEntry entry -> checkVar entry
507- (_, U. EPostEntry entry) | isCalldataEntry entry -> error $ " Internal error: Variables cannot be post" <> show e
511+ U. EUTEntry entry | isCalldataEntry entry -> checkVar entry
512+ U. EPreEntry entry | isCalldataEntry entry -> checkVar entry
513+ U. EPostEntry entry | isCalldataEntry entry -> error $ " Internal error: Variables cannot be post" <> show e
508514 -- Storage references
509- (_, U. EUTEntry entry) -> checkStorage entry Neither
510- (_, U. EPreEntry entry) -> checkStorage entry Pre
511- (_, U. EPostEntry entry) -> checkStorage entry Post
515+ U. EUTEntry entry -> checkStorage entry Neither
516+ U. EPreEntry entry -> checkStorage entry Pre
517+ U. EPostEntry entry -> checkStorage entry Post
512518
513- -- TODO other error for unimplemented
514- _ -> throw (getPosn e," Type mismatch. Expression does not have type " <> show typ)
519+ _ -> throw (getPosn e, " Internal error: Cannot type expression " <> show e)
515520
516521 where
517- checkVar :: U. Entry -> Err (Exp a t )
522+ wrapOp f e1 = TExp sing (f e1) -- use sign to let Haskell automatically derive the type here
523+ wrapOp2 f e1 e2 = TExp sing (f e1 e2)
524+
525+ polycheck :: forall z . Pn -> (forall y . Pn -> SType y -> Exp y t -> Exp y t -> z ) -> U. Expr -> U. Expr -> Err z
526+ polycheck pn cons e1 e2 =
527+ inferExpr env e1 `bindValidation` \ (TExp (t1 :: SType a1 ) (te1 :: Exp a1 t )) -> -- I don't know why type annotations are required here
528+ inferExpr env e2 `bindValidation` \ (TExp (t2 :: SType a2 ) (te2 :: Exp a2 t )) ->
529+ maybe (typeMismatchErr pn t1 t2) (\ Refl -> pure $ cons pn t1 te1 te2) $ testEquality t1 t2
530+
531+ checkVar :: U. Entry -> Err (TypedExp t )
518532 checkVar entry = case (eqT @ t @ Timed , eqT @ t @ Untimed ) of
519- (Just Refl , _) -> validateEntry env SCalldata entry `bindValidation` \ (vt @ ( FromVType typ'), ref) ->
520- TEntry (getPosEntry entry) Pre SCalldata (Item typ vt ref) <$ checkEq (getPosEntry entry) typ typ'
533+ (Just Refl , _) ->
534+ ( \ (vt @ ( FromVType typ), ref) -> TExp typ $ TEntry (getPosEntry entry) Pre SCalldata (Item typ vt ref)) <$> (validateEntry env SCalldata entry)
521535 (_, Just Refl ) -> validateEntry env SCalldata entry `bindValidation` \ (vt@ (FromVType typ'), ref) ->
522- TEntry (getPosEntry entry) Neither SCalldata (Item typ vt ref) <$ checkEq (getPosEntry entry) typ typ'
536+ ( \ (vt @ ( FromVType typ), ref) -> TExp typ $ TEntry (getPosEntry entry) Neither SCalldata (Item typ vt ref)) <$> (validateEntry env SCalldata entry)
523537 (_,_) -> error " Internal error: Timing should be either Timed or Untimed"
524538
525- checkStorage :: forall t0 . Typeable t0 => U. Entry -> Time t0 -> Err (Exp a t )
526- checkStorage entry time = validateEntry env SStorage entry `bindValidation` \ (vt@ (FromVType typ'), ref) ->
527- checkTime (getPosEntry entry) <*> (TEntry (getPosEntry entry) time SStorage (Item typ vt ref) <$ checkEq (getPosEntry entry) typ typ')
539+ -- Type check a storage variable
540+ checkStorage :: forall t0 . Typeable t0 => U. Entry -> Time t0 -> Err (TypedExp t )
541+ checkStorage entry time =
542+ -- check that the timing is correct
543+ checkTime (getPosEntry entry) <*>
544+ ((\ (vt@ (FromVType typ), ref) -> TExp typ $ TEntry (getPosEntry entry) time SStorage (Item typ vt ref)) <$> validateEntry env SStorage entry)
528545
529-
530- polycheck :: Pn -> (forall y . Pn -> SType y -> Exp y t -> Exp y t -> Exp x t ) -> U. Expr -> U. Expr -> Err (Exp x t )
531- polycheck pn cons v1 v2 = findSuccess (throw (pn, " Cannot match the type of expression " <> show v1 <> " with expression " <> show v2))
532-
533- -- TODO this is inefficient and produces really bad error messages. Do proper type inference instead
534- [ cons pn SInteger <$> checkExpr env SInteger v1 <*> checkExpr env SInteger v2
535- , cons pn SBoolean <$> checkExpr env SBoolean v1 <*> checkExpr env SBoolean v2
536- , cons pn SByteStr <$> checkExpr env SByteStr v1 <*> checkExpr env SByteStr v2
537- ]
538-
539- checkTime :: forall t0 . Typeable t0 => Pn -> Err (Exp a t0 -> Exp a t )
546+ -- Check that an expression is typed with the right timing
547+ checkTime :: forall t0 . Typeable t0 => Pn -> Err (TypedExp t0 -> TypedExp t )
540548 checkTime pn = case eqT @ t @ t0 of
541549 Just Refl -> pure id
542550 Nothing -> throw (pn, (tail . show $ typeRep @ t ) <> " variable needed here" )
@@ -547,11 +555,10 @@ checkExpr env@Env{constructors, calldata} typ e = case (typ, e) of
547555 isCalldataEntry (U. EMapping _ entry _) = isCalldataEntry entry
548556 isCalldataEntry (U. EField _ entry _) = isCalldataEntry entry
549557
550- checkEq :: forall b c . Pn -> SType b -> SType c -> Err ()
551- checkEq p t1 t2 = maybe err (\ Refl -> pure () ) $ testEquality t1 t2
552- where
553- err = (throw (p, " Type " <> show t1 <> " should match type " <> show t2 <> " \n Env:\n " <> show env))
554-
558+ -- | Helper to create to create a conjunction out of a list of expressions
559+ andExps :: [Exp ABoolean t ] -> Exp ABoolean t
560+ andExps [] = LitBool nowhere True
561+ andExps (c: cs) = foldr (And nowhere) c cs
555562
556563
557564-- | Find the contract id of an expression with contract type
0 commit comments