Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Toplevel rust translation #1905

Merged
merged 12 commits into from
Aug 16, 2023
87 changes: 87 additions & 0 deletions heapster-saw/examples/rust_just_translation.saw
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// This file demonstrates how to use the `heapster_trans_rust_type`
// command to translate rust signatures to heapster.
enable_experimental;

// Integer types This is wrong... we don't need an environment.
env <- heapster_init_env_from_file "rust_data.sawcore" "rust_data.bc";

print "Define Rust types.";
/***
*** Types
***/

// Integer types
heapster_define_perm env "int64" " " "llvmptr 64" "exists x:bv 64.eq(llvmword(x))";
heapster_define_perm env "int32" " " "llvmptr 32" "exists x:bv 32.eq(llvmword(x))";
heapster_define_perm env "int8" " " "llvmptr 8" "exists x:bv 8.eq(llvmword(x))";
heapster_define_perm env "int1" " " "llvmptr 1" "exists x:bv 1.eq(llvmword(x))";

heapster_define_llvmshape env "u64" 64 "" "fieldsh(int64<>)";
heapster_define_llvmshape env "u32" 64 "" "fieldsh(32,int32<>)";
heapster_define_llvmshape env "u8" 64 "" "fieldsh(8,int8<>)";

heapster_define_llvmshape env "usize" 64 "" "fieldsh(int64<>)";
heapster_define_llvmshape env "char" 64 "" "fieldsh(32,int32<>)";

// bool type
heapster_define_llvmshape env "bool" 64 "" "fieldsh(1,int1<>)";

// Box type
heapster_define_llvmshape env "Box" 64 "T:llvmshape 64" "ptrsh(T)";

// Result type
heapster_define_rust_type env "pub enum Result<X,Y> { Ok (X), Err (Y) }";

// Infallable type
heapster_define_llvmshape env "Infallible" 64 "" "falsesh";

// Sum type
heapster_define_rust_type env "pub enum Sum<X,Y> { Left (X), Right (Y) }";

// The Option type
heapster_define_rust_type env "pub enum Option<X> { None, Some (X) }";


print "";
print "-----------------------------------------------";
print "Translate 'unit'";
print "Rust: \n<> fn () -> ()";
print "Heapster:";
heapster_trans_rust_type env "<> fn () -> ()";

print "";
print "-----------------------------------------------";
print "Translate 'add'";
print "Rust: \n<> fn (x:u64, y:u64) -> u64";
print "Heapster:";
heapster_trans_rust_type env "<> fn (x:u64, y:u64) -> u64";


print "";
print "-----------------------------------------------";
print "Translate 'Ptr add'";
print "Rust: \n<'a,'b> fn (x:&'a u64, y:&'a u64) -> u64";
print "Heapster:";
heapster_trans_rust_type env "<'a,'b> fn (x:&'a u64, y:&'a u64) -> u64";

print "";
print "-----------------------------------------------";
print "Translate 'array length'";
print "Rust: \n<'a> fn (x:&'a [u64]) -> u64";
print "Heapster:";
heapster_trans_rust_type env "<'a> fn (x:&'a [u64]) -> u64";


print "";
print "-----------------------------------------------";
print "Translate 'add two array'";
print "Rust: \n<'a, 'b, 'c> fn (l1:&'a [u64], l2:&'b [u64]) -> &'c [u64]";
print "Heapster:";
heapster_trans_rust_type env "<'a, 'b, 'c> fn (l1:&'a [u64], l2:&'b [u64]) -> &'c [u64]";

print "";
print "-----------------------------------------------";
print "Translate 'add two array in place'";
print "Rust: \n<'a, 'b> fn (l1:&'a mut[u64], l2:&'b [u64]) -> ()";
print "Heapster:";
heapster_trans_rust_type env "<'a, 'b> fn (l1:&'a mut[u64], l2:&'b [u64]) -> ()";
14 changes: 14 additions & 0 deletions heapster-saw/src/Verifier/SAW/Heapster/PermParser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,20 @@ parseFunPermStringMaybeRust nm w env args ret str =
Just '<' -> parseFunPermFromRust env w args ret str
_ -> parseFunPermString nm env args ret str

-- -- | Just like `parseFunPermStringMaybeRust` but usese `parseSome3FunFromRust`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't leave these comments in the code.

-- parseSome3FunPermStringMaybeRust ::
-- (1 <= w, KnownNat w, Fail.MonadFail m) =>
-- String {- ^ object name -} ->
-- prx w {- ^ pointer bit-width proxy -} ->
-- PermEnv {- ^ permission environment -} ->
-- String {- ^ input text -} ->
-- m Some3FunPerm
-- parseSome3FunPermStringMaybeRust nm w env str =
-- case find (\c -> c == '<' || c == '(') str of
-- Just '<' -> parseSome3FunPermFromRust env w str
-- _ -> parseFunPermString nm env args ret str


-- | Parse a 'SomeNamedShape' from the given 'String'. This 'SomeNamedShape'
-- must be a valid Rust @struct@ or @enum@ declaration given in Rust syntax.
-- The @w@ argument gives the bit width of pointers in the current\
Expand Down
28 changes: 28 additions & 0 deletions heapster-saw/src/Verifier/SAW/Heapster/RustTypes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1573,6 +1573,34 @@ parseFunPermFromRust env w args ret str
parseFunPermFromRust _ _ _ _ str =
fail ("Malformed Rust type: " ++ str)


-- | Just like `parseFunPermFromRust`, but doesn't take `args` and `ret`.
-- usefull to translate rust when there is no LLVM module (e.g. from command line)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that there is no LLVM module, it's that there is no specified LLVM function.

Also, it seems like the only place where args and ret are used in parseFunPermFromRust is in the call to un3SomeFunPerm, so, in order to remove all the code duplication between that function and this one, you should redefine parseFunPermFromRust to just call parseSome3FunPermFromRust and then pass the result to un3SomeFunPerm.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

I removed the duplication and got it to typecheck. It looks right to me, but it's now doing runLiftRustConvM twice. That seems ok to me, but please make sure it's not introducing any efficiency problems.

Alternatively, I can make parseSome3FunPermFromRust take "lifting" function which can be instantiated to id and for un3SomeFunPerm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, I didn't notice that un3SomeFunPerm was inside RustConvM. It looks like that function could instead be changed to return an m (SomeFunPerm args ret) in any monad with a MonadFail instance, by using emptyPPInfo in place of rsPPInfo, since un3SomeFunPerm is only ever run at the top level, where the context is empty.

parseSome3FunPermFromRust :: (Fail.MonadFail m, 1 <= w, KnownNat w) =>
PermEnv -> prx w ->
String -> m Some3FunPerm
parseSome3FunPermFromRust env w str
| Just i <- findIndex (== '>') str
, (gen_str, fn_str) <- splitAt (i+1) str
, Right (Generics rust_ls1 rust_tvars wc span) <-
parse (inputStreamFromString gen_str)
, Right (BareFn _ abi rust_ls2 fn_tp _) <-
parse (inputStreamFromString fn_str) =
runLiftRustConvM (mkRustConvInfo env) $
rsConvertFun w abi (Generics (rust_ls1 ++ rust_ls2) rust_tvars wc span) fn_tp

| Just i <- findIndex (== '>') str
, (gen_str, _) <- splitAt (i+1) str
, Left err <- parse @(Generics Span) (inputStreamFromString gen_str) =
fail ("Error parsing generics: " ++ show err)

| Just i <- findIndex (== '>') str
, (_, fn_str) <- splitAt (i+1) str
, Left err <- parse @(Ty Span) (inputStreamFromString fn_str) =
fail ("Error parsing generics: " ++ show err)
parseSome3FunPermFromRust _ _ str =
fail ("Malformed Rust type: " ++ str)

-- | Parse a polymorphic Rust type declaration and convert it to a Heapster
-- shape
-- Note: No CruCtx / TypeRepr as arguments for now
Expand Down
21 changes: 20 additions & 1 deletion src/SAWScript/HeapsterBuiltins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ module SAWScript.HeapsterBuiltins
, heapster_find_trait_method_symbol
, heapster_assume_fun
, heapster_assume_fun_rename
, heapster_translate_rust_type
, heapster_assume_fun_rename_prim
, heapster_assume_fun_multi
, heapster_set_event_type
Expand Down Expand Up @@ -124,6 +125,7 @@ import Verifier.SAW.Heapster.Permissions
import Verifier.SAW.Heapster.SAWTranslation
import Verifier.SAW.Heapster.IRTTranslation
import Verifier.SAW.Heapster.PermParser
import Verifier.SAW.Heapster.RustTypes (parseSome3FunPermFromRust, Some3FunPerm(..))
import Verifier.SAW.Heapster.ParsedCtx
import qualified Verifier.SAW.Heapster.IDESupport as HIDE
import Verifier.SAW.Heapster.LLVMGlobalConst
Expand Down Expand Up @@ -1020,7 +1022,7 @@ heapster_assume_fun_rename _bic _opts henv nm nm_to perms_string term_string =
do Some lm <- failOnNothing ("Could not find symbol: " ++ nm)
(lookupModContainingSym henv nm)
sc <- getSharedContext
let w = llvmModuleArchReprWidth lm
let w = llvmModuleArchReprWidth lm -- Should hardcode 64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't actually be hard coded to 64, since it could potentially be useful to have it match some other architecture with a different bit width. Currently, the way to achieve that is to specify it in the Heapster environment. I could imagine making this an argument instead of requiring a Heapster environment, but the current approach seems easiest for now, so maybe just remove this comment?

leq_proof <- case decideLeq (knownNat @1) w of
Left pf -> return pf
Right _ -> fail "LLVM arch width is 0!"
Expand All @@ -1040,6 +1042,23 @@ heapster_assume_fun_rename _bic _opts henv nm nm_to perms_string term_string =
(globalOpenTerm term_ident)
liftIO $ writeIORef (heapsterEnvPermEnvRef henv) env''

heapster_translate_rust_type :: BuiltinContext -> Options -> HeapsterEnv ->
String -> TopLevel ()
heapster_translate_rust_type _bic _opts henv perms_string =
do env <- liftIO $ readIORef $ heapsterEnvPermEnvRef henv
let w64 = (knownNat @64::NatRepr 64)
leq_proof <- case decideLeq (knownNat @1) w64 of
Left pf -> return pf
Right _ -> fail "LLVM arch width is 0!"
withKnownNat w64 $ withLeqProof leq_proof $ do
Some3FunPerm fun_perm <-
parseSome3FunPermFromRust env w64 perms_string
--parseFunPermStringMaybeRust "permissions" w64 env args ret perms_string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this commented out line from the code

liftIO $ putStrLn $ permPrettyString emptyPPInfo fun_perm

-- Lookup a how to print to io & and, looking into permissions.hs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove these comments from the code

-- for permPrettyString and pass an empty ppInfo to print 'fun_perm'

-- | Create a new SAW core primitive named @nm@ with type @tp@ in the module
-- associated with the supplied Heapster environment, and return its identifier
insPrimitive :: HeapsterEnv -> String -> Term -> TopLevel Ident
Expand Down
9 changes: 8 additions & 1 deletion src/SAWScript/Interpreter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4253,11 +4253,18 @@ primitives = Map.fromList
[ "Tell Heapster whether to perform its translation-time checks of the "
, "well-formedness of type-checking proofs" ]

, prim "heapster_trans_rust_type"
"HeapsterEnv -> String -> TopLevel ()"
(bicVal heapster_translate_rust_type)
Experimental
[ "Parse and print back a set of Heapster permissions for a function"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this description is slightly imprecise in a way that might confuse the user, because it sounds like the function is parsing Heapster permissions and printing them. The point of this function is to convert a Rust type to a Heapster type and print out the result. So maybe say something like that.

]

, prim "heapster_parse_test"
"LLVMModule -> String -> String -> TopLevel ()"
(bicVal heapster_parse_test)
Experimental
[ "Parse and print back a set of Heapster permissions for a function"
[ "Parse the Rust signature for a function and print the equivalent Heapser type. Ideal for learning how Rust types are translated into Heapster. "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh, it looks like the doc string for heapster_parse_test got switched with that for heapster_trans_rust_type. Slight tweak, though: technically, this isn't parsing the signature for a function, because there need not be any function associated with the type. Instead, it is just parsing a Rust function type.

]

, prim "heapster_dump_ide_info"
Expand Down
2 changes: 1 addition & 1 deletion src/SAWScript/Value.hs
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ instance FromValue a => FromValue (TopLevel a) where
v1 <- withPosition pos (fromValue m1)
m2 <- applyValue v2 v1
fromValue m2
fromValue _ = error "fromValue TopLevel"
fromValue v = error $ "fromValue TopLevel:" <> show v

instance IsValue a => IsValue (ProofScript a) where
toValue m = VProofScript (fmap toValue m)
Expand Down