Skip to content

Commit

Permalink
Merge pull request #1365 from GaloisInc/rust-heapster-translation
Browse files Browse the repository at this point in the history
Improvements to the Heapster Rust translation
  • Loading branch information
mergify[bot] authored Jul 1, 2021
2 parents aaa27a9 + 6f21d67 commit 759aed0
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 87 deletions.
3 changes: 2 additions & 1 deletion heapster-saw/examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Makefile.coq: _CoqProject

include Makefile.coq

SAW=saw
# If the saw executable is available, use it, otherwise fall back on cabal run saw
SAW=$(shell which saw || echo cabal run saw)

%.bc: %.c
clang -emit-llvm -g -c $<
Expand Down
1 change: 1 addition & 0 deletions heapster-saw/examples/_CoqProject
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ rust_data.v
rust_data_proofs.v
rust_lifetimes.v
rust_lifetimes_proofs.v
arrays.v
106 changes: 70 additions & 36 deletions heapster-saw/examples/arrays.v

Large diffs are not rendered by default.

Binary file modified heapster-saw/examples/rust_data.bc
Binary file not shown.
28 changes: 28 additions & 0 deletions heapster-saw/examples/rust_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,31 @@ pub fn hash_map_insert_gt_to_le (m: &mut HashMap<u64,u64>, x:u64, y:u64) -> () {
m.insert (y, x);
}
}

/* A tree whose internal nodes contain vectors of children */
pub enum Tree<X> {
Leaf (X),
Node (Vec<Tree<X>>)
}

pub fn tree_is_leaf (t: &Tree<u64>) -> bool {
match *t {
Tree::Leaf (_) => true,
Tree::Node (_) => false
}
}

/* Sum all leaves in a tree */
/*
pub fn tree_sum (t: &Tree<u64>) -> u64 {
let mut acc = 0;
match *t {
Tree::Leaf (x) => x,
Tree::Node (children) =>
for u in children {
acc += tree_sum (u);
}
acc;
}
}
*/
8 changes: 7 additions & 1 deletion heapster-saw/examples/rust_data.saw
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,17 @@ heapster_define_rust_type env "pub struct MixedStruct { pub s: String, pub i1: u
heapster_define_rust_type env "pub enum TrueEnum { Foo, Bar, Baz }";

// Opaque type for Vec<T>
//heapster_define_opaque_llvmshape env "Vec" 64 "T:llvmshape 64" "24" "\\ (T:sort 0) -> List T";
heapster_define_opaque_llvmshape env "Vec" 64 "T:llvmshape 64" "24" "\\ (T:sort 0) -> List T";

// Opaque type for HashMap<T,U>
heapster_define_opaque_llvmshape env "HashMap" 64 "T:llvmshape 64, U:llvmshape 64" "56" "\\ (T:sort 0) (U:sort 0) -> List (T * U)";

// Tree<X> type
// FIXME: this does not work yet because Heapster cannot yet handle recursive types
// where the type being defined is passed to an opaque or recursvie type
//heapster_define_rust_type env "pub enum Tree<X> { Leaf (X), Node (Vec<Tree<X>>) }";



/***
*** Assumed Functions
Expand Down
3 changes: 3 additions & 0 deletions heapster-saw/examples/rust_data.v
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Definition foldList_IRT : forall (e0 : Type), forall (p0 : @SAWCorePrelude.Eithe
Definition unfoldList_IRT : forall (e0 : Type), forall (p0 : @List_IRT e0), @SAWCorePrelude.Either (prod unit unit) (prod unit (prod e0 (prod (@List_IRT e0) unit))) :=
fun (e0 : Type) => @SAWCorePrelude.unfoldIRT (@List_IRTTyVars e0) (@SAWCorePrelude.IRTs_Nil (@List_IRTTyVars e0)) (@List_IRTDesc e0).

Definition Vec0 : forall (e0 : Type), Type :=
fun (T : Type) => @Datatypes.list T.

Definition HashMap : forall (e0 : Type), forall (e1 : Type), Type :=
fun (T : Type) (U : Type) => @Datatypes.list (prod T U).

Expand Down
10 changes: 10 additions & 0 deletions heapster-saw/src/Verifier/SAW/Heapster/CruUtil.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ traverseRAssign :: Applicative m => (forall x. f x -> m (g x)) ->
traverseRAssign _ MNil = pure MNil
traverseRAssign f (xs :>: x) = (:>:) <$> traverseRAssign f xs <*> f x

-- FIXME HERE: this should move to Hobbits
instance Closable a => Closable (Maybe a) where
toClosed Nothing = $(mkClosed [| Nothing |])
toClosed (Just a) = $(mkClosed [| Just |]) `clApply` toClosed a

-- FIXME HERE: this should move to Hobbits
instance (Closable a, Closable b) => Closable (a,b) where
toClosed (a,b) =
$(mkClosed [| (,) |]) `clApply` toClosed a `clApply` toClosed b


----------------------------------------------------------------------
-- * Helper Functions for 'NatRepr' and 'KnownNat'
Expand Down
102 changes: 96 additions & 6 deletions heapster-saw/src/Verifier/SAW/Heapster/IRTTranslation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,100 @@ askExprCtxTerms = exprCtxToTerms <$> infoCtx <$> ask


----------------------------------------------------------------------
-- The monad for translating IRT type variables
-- * Names of the recursive permission or shape being defined
----------------------------------------------------------------------

-- | The name of the recursive permission or shape being defined
data IRTRecName args where
IRTRecPermName :: NamedPermName ns args tp -> IRTRecName args
IRTRecShapeName :: NatRepr w -> NamedShape 'True args w -> IRTRecName args

-- | Generic function to test if an object contains an 'IRTRecName'
class ContainsIRTRecName a where
containsIRTRecName :: IRTRecName args -> a -> Bool

instance ContainsIRTRecName a => ContainsIRTRecName [a] where
containsIRTRecName n = any (containsIRTRecName n)

instance ContainsIRTRecName a => ContainsIRTRecName (Mb ctx a) where
containsIRTRecName n = mbLift . fmap (containsIRTRecName n)

instance ContainsIRTRecName (PermExpr a) where
containsIRTRecName (IRTRecShapeName w nm_sh) (PExpr_NamedShape _ _ nm_sh' _)
| Just Refl <- testEquality w (natRepr nm_sh')
, Just _ <- namedShapeEq nm_sh nm_sh' = True
containsIRTRecName n (PExpr_NamedShape _ _ _ args) =
containsIRTRecName n args
containsIRTRecName n (PExpr_PtrShape _ _ sh) = containsIRTRecName n sh
containsIRTRecName n (PExpr_FieldShape fsh) = containsIRTRecName n fsh
containsIRTRecName n (PExpr_ArrayShape _ _ fshs) = containsIRTRecName n fshs
containsIRTRecName n (PExpr_SeqShape sh1 sh2) =
containsIRTRecName n sh1 || containsIRTRecName n sh2
containsIRTRecName n (PExpr_OrShape sh1 sh2) =
containsIRTRecName n sh1 || containsIRTRecName n sh2
containsIRTRecName n (PExpr_ExShape mb_sh) =
mbLift $ fmap (containsIRTRecName n) mb_sh
containsIRTRecName n (PExpr_ValPerm p) = containsIRTRecName n p
containsIRTRecName _ _ = False

instance ContainsIRTRecName (RAssign PermExpr tps) where
containsIRTRecName _ MNil = False
containsIRTRecName n (es :>: e) =
containsIRTRecName n es || containsIRTRecName n e

instance ContainsIRTRecName (LLVMFieldShape a) where
containsIRTRecName n (LLVMFieldShape p) = containsIRTRecName n p

instance ContainsIRTRecName (ValuePerm a) where
containsIRTRecName n (ValPerm_Eq e) = containsIRTRecName n e
containsIRTRecName n (ValPerm_Or p1 p2) =
containsIRTRecName n p1 || containsIRTRecName n p2
containsIRTRecName n (ValPerm_Exists mb_p) =
mbLift $ fmap (containsIRTRecName n) mb_p
containsIRTRecName (IRTRecPermName npn) (ValPerm_Named npn' _ _)
| Just _ <- testNamedPermNameEq npn npn' = True
containsIRTRecName n (ValPerm_Named _ args _) =
containsIRTRecName n args
containsIRTRecName _ (ValPerm_Var _ _) = False
containsIRTRecName n (ValPerm_Conj ps) = containsIRTRecName n ps

instance ContainsIRTRecName (RAssign ValuePerm tps) where
containsIRTRecName _ MNil = False
containsIRTRecName n (ps :>: p) =
containsIRTRecName n ps || containsIRTRecName n p

instance ContainsIRTRecName (AtomicPerm a) where
containsIRTRecName n (Perm_LLVMField fp) = containsIRTRecName n fp
containsIRTRecName n (Perm_LLVMArray arrp) =
containsIRTRecName n (llvmArrayFields arrp)
containsIRTRecName n (Perm_LLVMBlock bp) =
containsIRTRecName n (llvmBlockShape bp)
containsIRTRecName _ (Perm_LLVMFree _) = False
containsIRTRecName _ (Perm_LLVMFunPtr _ _) = False
containsIRTRecName n (Perm_LLVMBlockShape sh) = containsIRTRecName n sh
containsIRTRecName _ Perm_IsLLVMPtr = False
containsIRTRecName (IRTRecPermName npn) (Perm_NamedConj npn' _ _)
| Just _ <- testNamedPermNameEq npn npn' = True
containsIRTRecName n (Perm_NamedConj _ args _) = containsIRTRecName n args
containsIRTRecName n (Perm_LLVMFrame fperm) =
containsIRTRecName n (map fst fperm)
containsIRTRecName _ (Perm_LOwned _ _) = False
containsIRTRecName _ (Perm_LCurrent _) = False
containsIRTRecName n (Perm_Struct ps) = containsIRTRecName n ps
containsIRTRecName _ (Perm_Fun _) = False
containsIRTRecName _ (Perm_BVProp _) = False

instance ContainsIRTRecName (LLVMArrayField w) where
containsIRTRecName n (LLVMArrayField fp) = containsIRTRecName n fp

instance ContainsIRTRecName (LLVMFieldPerm w sz) where
containsIRTRecName n fp = containsIRTRecName n $ llvmFieldContents fp


----------------------------------------------------------------------
-- * The monad for translating IRT type variables
----------------------------------------------------------------------

data IRTTyVarsTransCtx args ext =
IRTTyVarsTransCtx
{
Expand Down Expand Up @@ -150,7 +237,7 @@ irtTSubstExt x =


----------------------------------------------------------------------
-- Trees for keeping track of IRT variable indices
-- * Trees for keeping track of IRT variable indices
----------------------------------------------------------------------

data IRTVarTree a = IRTVarsNil
Expand All @@ -174,7 +261,7 @@ setIRTVarIdxs tree = evalState (mapM (\_ -> nextIdx) tree) 0


----------------------------------------------------------------------
-- Translating IRT type variables
-- * Translating IRT type variables
----------------------------------------------------------------------

-- | Given the name of a recursive permission being defined and its argument
Expand Down Expand Up @@ -364,6 +451,9 @@ instance IRTTyVars (PermExpr (LLVMShapeType w)) where
_ -> case mbMatch $ namedShapeBody <$> nmsh of
[nuMP| DefinedShapeBody _ |] ->
irtTyVars (mbMap2 unfoldNamedShape nmsh args)
_ | containsIRTRecName n_rec mb_sh ->
throwError ("recursive shape passed to an opaque or"
++ " recursive shape in its definition!")
_ -> do sh' <- irtTSubstExt mb_sh
let sh_trans = transTupleTerm <$> translate sh'
return ([sh_trans], IRTVar ())
Expand Down Expand Up @@ -396,7 +486,7 @@ instance IRTTyVars (RAssign ValuePerm ps) where


----------------------------------------------------------------------
-- The IRTDesc translation monad
-- * The IRTDesc translation monad
----------------------------------------------------------------------

-- | Contextual info for translating IRT type descriptions
Expand Down Expand Up @@ -462,7 +552,7 @@ irtCtor c all_args =


----------------------------------------------------------------------
-- Translating IRT type descriptions
-- * Translating IRT type descriptions
----------------------------------------------------------------------

-- | Given an identifier whose definition in the shared context is the first
Expand Down Expand Up @@ -636,7 +726,7 @@ instance IRTDescs (RAssign ValuePerm ps) where


----------------------------------------------------------------------
-- Translating IRT definitions
-- * Translating IRT definitions
----------------------------------------------------------------------

-- | Given identifiers whose definitions in the shared context are the results
Expand Down
29 changes: 14 additions & 15 deletions heapster-saw/src/Verifier/SAW/Heapster/Permissions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1988,24 +1988,25 @@ data NamedShapeBody b args w where
-- | A recursive shape body has a one-step unfolding to a shape, which can
-- refer to the shape itself via the last bound variable; it also has
-- identifiers for the type it is translated to, along with fold and unfold
-- functions for mapping to and from this type
-- functions for mapping to and from this type. The fold and unfold functions
-- can be undefined if we are in the process of defining this recusive shape.
RecShapeBody :: Mb (args :> LLVMShapeType w) (PermExpr (LLVMShapeType w)) ->
Ident -> Ident -> Ident ->
Ident -> Maybe (Ident, Ident) ->
NamedShapeBody 'True args w

deriving instance Eq (NamedShapeBody b args w)

-- | Test if a 'NamedShape' is recursive
namedShapeIsRecursive :: NamedShape b args w -> Bool
namedShapeIsRecursive (NamedShape _ _ (RecShapeBody _ _ _ _)) = True
namedShapeIsRecursive (NamedShape _ _ (RecShapeBody _ _ _)) = True
namedShapeIsRecursive _ = False

-- | Get a 'BoolRepr' for the Boolean flag for whether a named shape can be
-- unfolded
namedShapeCanUnfoldRepr :: NamedShape b args w -> BoolRepr b
namedShapeCanUnfoldRepr (NamedShape _ _ (DefinedShapeBody _)) = TrueRepr
namedShapeCanUnfoldRepr (NamedShape _ _ (OpaqueShapeBody _ _)) = FalseRepr
namedShapeCanUnfoldRepr (NamedShape _ _ (RecShapeBody _ _ _ _)) = TrueRepr
namedShapeCanUnfoldRepr (NamedShape _ _ (RecShapeBody _ _ _)) = TrueRepr

-- | Whether a 'NamedShape' can be unfolded
namedShapeCanUnfold :: NamedShape b args w -> Bool
Expand Down Expand Up @@ -3245,7 +3246,7 @@ llvmShapeLength (PExpr_NamedShape _ _ (NamedShape _ _
(OpaqueShapeBody mb_len _)) args) =
Just $ subst (substOfExprs args) mb_len
llvmShapeLength (PExpr_NamedShape _ _ nmsh@(NamedShape _ _
(RecShapeBody _ _ _ _)) args) =
(RecShapeBody _ _ _)) args) =
-- FIXME: if the recursive shape contains itself *not* under a pointer, then
-- this could diverge
llvmShapeLength (unfoldNamedShape nmsh args)
Expand Down Expand Up @@ -3395,7 +3396,7 @@ unfoldNamedShape :: KnownNat w => NamedShape 'True args w -> PermExprs args ->
PermExpr (LLVMShapeType w)
unfoldNamedShape (NamedShape _ _ (DefinedShapeBody mb_sh)) args =
subst (substOfExprs args) mb_sh
unfoldNamedShape sh@(NamedShape _ _ (RecShapeBody mb_sh _ _ _)) args =
unfoldNamedShape sh@(NamedShape _ _ (RecShapeBody mb_sh _ _)) args =
subst (substOfExprs (args :>: PExpr_NamedShape Nothing Nothing sh args)) mb_sh

-- | Unfold a named shape and apply 'modalizeShape' to the result
Expand Down Expand Up @@ -4354,7 +4355,7 @@ shapeIsCopyable rw (PExpr_NamedShape maybe_rw' _ nmsh args) =
-- HACK: the real computation we want to perform is to assume nmsh is copyable
-- and prove it is under that assumption; to accomplish this, we substitute
-- the empty shape for the recursive shape
RecShapeBody mb_sh _ _ _ ->
RecShapeBody mb_sh _ _ ->
shapeIsCopyable rw $ subst (substOfExprs (args :>: PExpr_EmptyShape)) mb_sh
shapeIsCopyable _ (PExpr_EqShape _) = True
shapeIsCopyable rw (PExpr_PtrShape maybe_rw' _ sh) =
Expand Down Expand Up @@ -4640,7 +4641,7 @@ instance FreeVars (NamedShape b args w) where
instance FreeVars (NamedShapeBody b args w) where
freeVars (DefinedShapeBody mb_sh) = freeVars mb_sh
freeVars (OpaqueShapeBody mb_len _) = freeVars mb_len
freeVars (RecShapeBody mb_sh _ _ _) = freeVars mb_sh
freeVars (RecShapeBody mb_sh _ _) = freeVars mb_sh


-- | Test if an expression @e@ is a /determining/ expression, meaning that
Expand Down Expand Up @@ -4971,11 +4972,10 @@ genSubstNSB px s mb_body = case mbMatch mb_body of
DefinedShapeBody <$> genSubstMb px s mb_sh
[nuMP| OpaqueShapeBody mb_len trans_id |] ->
OpaqueShapeBody <$> genSubstMb px s mb_len <*> return (mbLift trans_id)
[nuMP| RecShapeBody mb_sh trans_id fold_id unfold_id |] ->
[nuMP| RecShapeBody mb_sh trans_id fold_ids |] ->
RecShapeBody <$> genSubstMb (px :>: Proxy) s mb_sh
<*> return (mbLift trans_id)
<*> return (mbLift fold_id)
<*> return (mbLift unfold_id)
<*> return (mbLift fold_ids)

instance SubstVar s m => Substable s (NamedPermName ns args a) m where
genSubst _ mb_rpn = return $ mbLift mb_rpn
Expand Down Expand Up @@ -5873,12 +5873,11 @@ instance AbstractVars (NamedShapeBody b args w) where
absVarsReturnH ns1 ns2 ($(mkClosed [| \i l -> OpaqueShapeBody l i |])
`clApply` toClosed trans_id)
`clMbMbApplyM` abstractPEVars ns1 ns2 mb_len
abstractPEVars ns1 ns2 (RecShapeBody mb_sh trans_id fold_id unfold_id) =
abstractPEVars ns1 ns2 (RecShapeBody mb_sh trans_id fold_ids) =
absVarsReturnH ns1 ns2 ($(mkClosed
[| \i1 i2 i3 l -> RecShapeBody l i1 i2 i3 |])
[| \i1 i2 l -> RecShapeBody l i1 i2 |])
`clApply` toClosed trans_id
`clApply` toClosed fold_id
`clApply` toClosed unfold_id)
`clApply` toClosed fold_ids)
`clMbMbApplyM` abstractPEVars ns1 ns2 mb_sh

instance AbstractVars (NamedPermName ns args a) where
Expand Down
12 changes: 7 additions & 5 deletions heapster-saw/src/Verifier/SAW/Heapster/RustTypes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,16 @@ namedTypeTable w =
-- @Foo<X>::Bar<Y,Z>::Baz@ just becomes @[Foo,Bar,Baz]@
newtype RustName = RustName [Ident] deriving (Eq)

-- | Convert a 'RustName' to a string by interspersing "::"
flattenRustName :: RustName -> String
flattenRustName (RustName ids) = concat $ intersperse "::" $ map name ids

instance Show RustName where
show (RustName ids) = concat $ intersperse "::" $ map show ids
show = show . flattenRustName

instance RsConvert w RustName (SomeShapeFun w) where
rsConvert w (RustName elems) =
-- FIXME: figure out how to actually resolve names; for now we just look at
-- the last string component...
do let str = name $ last elems
rsConvert w nm =
do let str = flattenRustName nm
env <- rciPermEnv <$> ask
case lookupNamedShape env str of
Just nmsh -> namedShapeShapeFun (natRepr w) nmsh
Expand Down
Loading

0 comments on commit 759aed0

Please sign in to comment.