Skip to content

Commit 37d60f4

Browse files
authored
Merge pull request #1936 from GaloisInc/T1676
MIR: Achieve feature (+ documentation) parity between SAWScript and Python
2 parents 5b6db1f + 278878f commit 37d60f4

File tree

8 files changed

+159
-16
lines changed

8 files changed

+159
-16
lines changed

saw-remote-api/python/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
an error.
2525
* Add a `tuple_value()` function for constructing MIR tuples. Using this
2626
function with LLVM or JVM verification will raise an error.
27+
* The `proclaim` function (which is the Python counterpart to to
28+
`{llvm,jvm,mir}_assert` in SAWScript) is no longer deprecated.
29+
* Add a `proclaim_f` function. This behaves like the `proclaim` function, except
30+
that it takes a `cry_f`-style format string as an argument. That is,
31+
`proclaim_f(...)` is equivalent to `proclaim(cry_f(...))`.
2732

2833
## 1.0.1 -- YYYY-MM-DD
2934

saw-remote-api/python/saw_client/crucible.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -516,10 +516,15 @@ def ghost_value(self, var: GhostVariable, value: cryptoltypes.CryptolJSON) -> No
516516
else:
517517
raise Exception("wrong state")
518518

519-
@deprecated
520519
def proclaim(self, proposition : Union[str, CryptolTerm, cryptoltypes.CryptolJSON]) -> None:
521-
"""DEPRECATED: Use ``precondition`` or ``postcondition`` instead. This method will
522-
eventually be removed."""
520+
"""Asserts ``proposition`` for the function ``Contract`` being
521+
specified.
522+
523+
Usable either before or after ``execute_func`` in the contract
524+
specification. If this is used before ``execute_func``, then
525+
``proposition`` is asserted as a precondition. If this is used after
526+
``execute_func``, then ``proposition`` is asserted as a postcondition.
527+
"""
523528
if not isinstance(proposition, CryptolTerm):
524529
condition = Condition(CryptolTerm(proposition))
525530
else:
@@ -531,6 +536,12 @@ def proclaim(self, proposition : Union[str, CryptolTerm, cryptoltypes.CryptolJSO
531536
else:
532537
raise Exception("wrong state")
533538

539+
def proclaim_f(self, s : str) -> None:
540+
"""Proclaims an assertion using a ``cry_f``-style format string, i.e.
541+
``proclaim_f(...)`` is equivalent to ``proclaim(cry_f(...))``"""
542+
expression = to_cryptol_str_customf(s, frames=1, filename="<proclaim_f>")
543+
return self.proclaim(expression)
544+
534545
def precondition(self, proposition : Union[str, CryptolTerm, cryptoltypes.CryptolJSON]) -> None:
535546
"""Establishes ``proposition`` as a precondition for the function ```Contract```
536547
being specified.
@@ -572,6 +583,8 @@ def postcondition_f(self, s : str) -> None:
572583
return self.postcondition(expression)
573584

574585
def returns(self, val : Union[Void,SetupVal]) -> None:
586+
"""Declare the return value for the function ``Contract`` being
587+
specified."""
575588
if self.__state == 'post':
576589
if self.__returns is None:
577590
self.__returns = val

saw-remote-api/python/saw_client/mir.py

+55-13
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,78 @@
99
##################################################
1010

1111
bool_ty = MIRBoolType()
12+
"""A MIR boolean type."""
13+
1214
char_ty = MIRCharType()
13-
str_ty = MIRStrType()
15+
"""A MIR character type."""
16+
17+
str_ty = MIRStrType()
18+
"""A MIR string type. Currently, SAW can only handle references to strings
19+
(``&str``)."""
20+
21+
i8 = MIRI8Type()
22+
"""A MIR 8-bit signed integer type."""
23+
24+
i16 = MIRI16Type()
25+
"""A MIR 16-bit signed integer type."""
26+
27+
i32 = MIRI32Type()
28+
"""A MIR 32-bit signed integer type."""
29+
30+
i64 = MIRI64Type()
31+
"""A MIR 64-bit signed integer type."""
32+
33+
i128 = MIRI128Type()
34+
"""A MIR 128-bit signed integer type."""
1435

15-
i8 = MIRI8Type()
16-
i16 = MIRI16Type()
17-
i32 = MIRI32Type()
18-
i64 = MIRI64Type()
19-
i128 = MIRI128Type()
2036
isize = MIRIsizeType()
37+
"""A MIR signed integer type that is pointer-sized."""
2138

2239
f32 = MIRF32Type()
40+
"""A MIR single-precision floating-point type."""
41+
2342
f64 = MIRF64Type()
43+
"""A MIR double-precision floating-point type."""
44+
45+
u8 = MIRU8Type()
46+
"""A MIR 8-bit unsigned integer type."""
47+
48+
u16 = MIRU16Type()
49+
"""A MIR 16-bit unsigned integer type."""
50+
51+
u32 = MIRU32Type()
52+
"""A MIR 32-bit unsigned integer type."""
53+
54+
u64 = MIRU64Type()
55+
"""A MIR 64-bit unsigned integer type."""
56+
57+
u128 = MIRU128Type()
58+
"""A MIR 128-bit unsigned integer type."""
2459

25-
u8 = MIRU8Type()
26-
u16 = MIRU16Type()
27-
u32 = MIRU32Type()
28-
u64 = MIRU64Type()
29-
u128 = MIRU128Type()
3060
usize = MIRUsizeType()
61+
"""A MIR unsigned integer type that is pointer-sized."""
3162

3263
def adt_ty(adt: MIRAdt) -> 'MIRAdtType':
33-
"""An algebraic data type (ADT), i.e., a struct or an enum."""
64+
"""A MIR algebraic data type (ADT), i.e., a struct or an enum."""
3465
return MIRAdtType(adt)
3566

3667
def array_ty(size : int, ty : 'MIRType') -> 'MIRArrayType':
3768
"""``[ty; size]``, i.e. a MIR array of ``size`` elements of type ``ty``."""
3869
return MIRArrayType(ty, size)
3970

71+
def ref_ty(ty : 'MIRType') -> 'MIRRefType':
72+
"""``&ty``, i.e., an immutable MIR reference type pointing to something of
73+
type ``ty``."""
74+
return MIRRefType(ty)
75+
76+
def ref_mut_ty(ty : 'MIRType') -> 'MIRRefMutType':
77+
"""``&mut ty``, i.e., a mutable MIR reference type pointing to something of
78+
type ``ty``."""
79+
return MIRRefMutType(ty)
80+
4081
def slice_ty(ty : MIRType) -> 'MIRSliceType':
41-
"""``[ty]``, i.e., a MIR slice to a type ``ty``."""
82+
"""``[ty]``, i.e., a MIR slice to a type ``ty``. Currently, SAW can only
83+
handle references to slices (``&[ty]``)"""
4284
return MIRSliceType(ty)
4385

4486
def tuple_ty(*tuple_types : MIRType) -> 'MIRTupleType':

saw-remote-api/python/saw_client/mir_type.py

+16
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ class MIRIsizeType(MIRType):
6969
def to_json(self) -> Any:
7070
return { 'type': 'isize' }
7171

72+
class MIRRefType(MIRType):
73+
def __init__(self, referent_type : 'MIRType') -> None:
74+
self.referent_type = referent_type
75+
76+
def to_json(self) -> Any:
77+
return { 'type': 'ref',
78+
'referent type': self.referent_type.to_json() }
79+
80+
class MIRRefMutType(MIRType):
81+
def __init__(self, referent_type : 'MIRType') -> None:
82+
self.referent_type = referent_type
83+
84+
def to_json(self) -> Any:
85+
return { 'type': 'ref mut',
86+
'referent type': self.referent_type.to_json() }
87+
7288
class MIRSliceType(MIRType):
7389
def __init__(self, slice_type : 'MIRType') -> None:
7490
self.slice_type = slice_type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"fns":[{"abi":{"kind":"Rust"},"args":[{"is_zst":false,"mut":{"kind":"Not"},"name":"_1","ty":"ty::i32"}],"body":{"blocks":[{"block":{"data":[{"kind":"Assign","lhs":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_2","ty":"ty::i32"}},"pos":"mir_proclaim.rs:2:5: 2:6","rhs":{"kind":"Use","usevar":{"data":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Not"},"name":"_1","ty":"ty::i32"}},"kind":"Copy"}}},{"kind":"Assign","lhs":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_3","ty":"ty::Tuple::423c819a0b13bfc4"}},"pos":"mir_proclaim.rs:2:5: 2:10","rhs":{"L":{"data":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_2","ty":"ty::i32"}},"kind":"Copy"},"R":{"data":{"rendered":{"kind":"int","size":4,"val":"1"},"ty":"ty::i32"},"kind":"Constant"},"kind":"CheckedBinaryOp","op":{"kind":"Add"}}}],"terminator":{"cleanup":null,"cond":{"data":{"data":[{"field":1,"kind":"Field","ty":"ty::bool"}],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_3","ty":"ty::Tuple::423c819a0b13bfc4"}},"kind":"Move"},"expected":false,"kind":"Assert","msg":"attempt to compute `move _2 + const 1_i32`, which would overflow","pos":"mir_proclaim.rs:2:5: 2:10","target":"bb1"}},"blockid":"bb0"},{"block":{"data":[{"kind":"Assign","lhs":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_0","ty":"ty::i32"}},"pos":"mir_proclaim.rs:2:5: 2:10","rhs":{"kind":"Use","usevar":{"data":{"data":[{"field":0,"kind":"Field","ty":"ty::i32"}],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_3","ty":"ty::Tuple::423c819a0b13bfc4"}},"kind":"Move"}}}],"terminator":{"kind":"Return","pos":"mir_proclaim.rs:3:2: 3:2"}},"blockid":"bb1"}],"vars":[{"is_zst":false,"mut":{"kind":"Mut"},"name":"_0","ty":"ty::i32"},{"is_zst":false,"mut":{"kind":"Mut"},"name":"_2","ty":"ty::i32"},{"is_zst":false,"mut":{"kind":"Mut"},"name":"_3","ty":"ty::Tuple::423c819a0b13bfc4"}]},"name":"mir_proclaim/87c4cc87::f","return_ty":"ty::i32","spread_arg":null}],"adts":[],"statics":[],"vtables":[],"traits":[],"intrinsics":[{"inst":{"def_id":"mir_proclaim/87c4cc87::f","kind":"Item","substs":[]},"name":"mir_proclaim/87c4cc87::f"}],"tys":[{"name":"ty::i32","ty":{"intkind":{"kind":"I32"},"kind":"Int"}},{"name":"ty::bool","ty":{"kind":"Bool"}},{"name":"ty::Tuple::423c819a0b13bfc4","ty":{"kind":"Tuple","tys":["ty::i32","ty::bool"]}}],"roots":["mir_proclaim/87c4cc87::f"]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn f(x: i32) -> i32 {
2+
x + 1
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from pathlib import Path
2+
import unittest
3+
from saw_client import *
4+
from saw_client.crucible import cry, cry_f
5+
from saw_client.mir import Contract, i32
6+
7+
8+
class FContract1(Contract):
9+
def specification(self):
10+
x = self.fresh_var(i32, 'x')
11+
self.proclaim(cry_f('{x} < 0x7fffffff'))
12+
13+
self.execute_func(x)
14+
15+
r = self.fresh_var(i32, 'r')
16+
self.proclaim(cry_f('{r} == {x} + 1'))
17+
self.proclaim(cry_f('{r} <= 0x7fffffff'))
18+
self.returns(r)
19+
20+
21+
# Like FContract1, but using `proclaim_f` instead of `proclaim`.
22+
class FContract2(Contract):
23+
def specification(self):
24+
x = self.fresh_var(i32, 'x')
25+
self.proclaim_f('{x} < 0x7fffffff')
26+
27+
self.execute_func(x)
28+
29+
r = self.fresh_var(i32, 'r')
30+
self.proclaim_f('{r} == {x} + 1')
31+
self.proclaim_f('{r} <= 0x7fffffff')
32+
self.returns(r)
33+
34+
35+
class ProclaimTest(unittest.TestCase):
36+
def test_proclaim(self):
37+
connect(reset_server=True)
38+
if __name__ == "__main__": view(LogResults())
39+
bcname = str(Path('tests','saw','test-files', 'mir_proclaim.linked-mir.json'))
40+
mod = mir_load_module(bcname)
41+
42+
result1 = mir_verify(mod, 'mir_proclaim::f', FContract1())
43+
self.assertIs(result1.is_success(), True)
44+
45+
result2 = mir_verify(mod, 'mir_proclaim::f', FContract2())
46+
self.assertIs(result2.is_success(), True)
47+
48+
49+
if __name__ == "__main__":
50+
unittest.main()

saw-remote-api/src/SAWServer/Data/MIRType.hs

+13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ data MIRTypeTag
1616
= TagAdt
1717
| TagArray
1818
| TagBool
19+
| TagChar
1920
| TagI8
2021
| TagI16
2122
| TagI32
@@ -24,6 +25,8 @@ data MIRTypeTag
2425
| TagIsize
2526
| TagF32
2627
| TagF64
28+
| TagRef
29+
| TagRefMut
2730
| TagSlice
2831
| TagStr
2932
| TagTuple
@@ -41,6 +44,7 @@ instance JSON.FromJSON MIRTypeTag where
4144
"adt" -> pure TagAdt
4245
"array" -> pure TagArray
4346
"bool" -> pure TagBool
47+
"char" -> pure TagChar
4448
"i8" -> pure TagI8
4549
"i16" -> pure TagI16
4650
"i32" -> pure TagI32
@@ -49,6 +53,8 @@ instance JSON.FromJSON MIRTypeTag where
4953
"isize" -> pure TagIsize
5054
"f32" -> pure TagF32
5155
"f64" -> pure TagF64
56+
"ref" -> pure TagRef
57+
"ref mut" -> pure TagRefMut
5258
"slice" -> pure TagSlice
5359
"str" -> pure TagStr
5460
"tuple" -> pure TagTuple
@@ -74,8 +80,10 @@ data JSONMIRType
7480
= JSONTyAdt !ServerName
7581
| JSONTyArray !JSONMIRType !Int
7682
| JSONTyBool
83+
| JSONTyChar
7784
| JSONTyFloat !Mir.FloatKind
7885
| JSONTyInt !Mir.BaseSize
86+
| JSONTyRef !JSONMIRType !Mir.Mutability
7987
| JSONTySlice !JSONMIRType
8088
| JSONTyStr
8189
| JSONTyTuple ![JSONMIRType]
@@ -93,6 +101,7 @@ instance JSON.FromJSON JSONMIRType where
93101
TagAdt -> JSONTyAdt <$> o .: "ADT server name"
94102
TagArray -> JSONTyArray <$> o .: "element type" <*> o .: "size"
95103
TagBool -> pure JSONTyBool
104+
TagChar -> pure JSONTyChar
96105
TagI8 -> pure $ JSONTyInt Mir.B8
97106
TagI16 -> pure $ JSONTyInt Mir.B16
98107
TagI32 -> pure $ JSONTyInt Mir.B32
@@ -101,6 +110,8 @@ instance JSON.FromJSON JSONMIRType where
101110
TagIsize -> pure $ JSONTyInt Mir.USize
102111
TagF32 -> pure $ JSONTyFloat Mir.F32
103112
TagF64 -> pure $ JSONTyFloat Mir.F64
113+
TagRef -> JSONTyRef <$> o .: "referent type" <*> pure Mir.Immut
114+
TagRefMut -> JSONTyRef <$> o .: "referent type" <*> pure Mir.Mut
104115
TagSlice -> JSONTySlice <$> o .: "slice type"
105116
TagStr -> pure JSONTyStr
106117
TagTuple -> JSONTyTuple <$> o .: "tuple types"
@@ -127,8 +138,10 @@ mirType sawenv = go
127138

128139
go (JSONTyArray ty n) = Mir.TyArray (go ty) n
129140
go JSONTyBool = Mir.TyBool
141+
go JSONTyChar = Mir.TyChar
130142
go (JSONTyFloat fk) = Mir.TyFloat fk
131143
go (JSONTyInt bs) = Mir.TyInt bs
144+
go (JSONTyRef ty mut) = Mir.TyRef (go ty) mut
132145
go (JSONTySlice ty) = Mir.TySlice (go ty)
133146
go JSONTyStr = Mir.TyStr
134147
go (JSONTyTuple ts) = Mir.TyTuple (map go ts)

0 commit comments

Comments
 (0)