Skip to content

Commit 464d444

Browse files
authored
529 pointer cast corrections (#632)
This became a rather large and pervasive change, adding metadata to the `Reference` value (not just to pointers). * `Reference`s now carry metadata which gives the size of the pointee in case of an array or slice. * The size of slices (and arrays) is important when dereferencing the references (or pointers, which is much less common). * Statically-determined sizes are also carried around to avoid looking up the size from the `TypeInfo` each time * when dereferencing arrays, only the expected size is copied. If the expected size is larger than the actual array, the execution gets stuck. A new test has been added which is expected to fail on undefined behaviour (`rustc`-compiled code will instead happily copy random data into the array, as can be observed by activating the `println`s in the new test).
1 parent 3bef37e commit 464d444

34 files changed

+5276
-148
lines changed

kmir/src/kmir/kast.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from pyk.kast.inner import KApply, KVariable, build_cons
77
from pyk.kast.prelude.collections import list_of
8-
from pyk.kast.prelude.kint import leInt
8+
from pyk.kast.prelude.kint import eqInt, leInt
99
from pyk.kast.prelude.ml import mlEqualsTrue
1010
from pyk.kast.prelude.utils import token
1111

@@ -133,7 +133,7 @@ def run(self, local_types: list[dict]) -> tuple[list[KInner], list[KInner]]:
133133
return (self.locals + self.pointees, self.constraints)
134134

135135
def _add_local(self, ty: Ty, mutable: bool) -> None:
136-
value, constraints = self._symbolic_value(ty, mutable)
136+
value, constraints, _ = self._symbolic_value(ty, mutable)
137137

138138
self.locals.append(_typed_value(value, ty, mutable))
139139
self.constraints += constraints
@@ -143,16 +143,20 @@ def _fresh_var(self, prefix: str) -> KVariable:
143143
self.counter += 1
144144
return KVariable(name)
145145

146-
def _symbolic_value(self, ty: Ty, mutable: bool) -> tuple[KInner, Iterable[KInner]]:
146+
def _symbolic_value(self, ty: Ty, mutable: bool) -> tuple[KInner, Iterable[KInner], KInner | None]:
147+
# returns: symbolic value of given type, related constraints, related pointer metadata
147148
match self.smir_info.types.get(ty):
148149
case Int(info):
149-
return int_var(self._fresh_var('ARG_INT'), info.value, True)
150+
val, constraints = int_var(self._fresh_var('ARG_INT'), info.value, True)
151+
return val, constraints, None
150152

151153
case Uint(info):
152-
return int_var(self._fresh_var('ARG_UINT'), info.value, False)
154+
val, constraints = int_var(self._fresh_var('ARG_UINT'), info.value, False)
155+
return val, constraints, None
153156

154157
case Bool():
155-
return bool_var(self._fresh_var('ARG_BOOL'))
158+
val, constraints = bool_var(self._fresh_var('ARG_BOOL'))
159+
return val, constraints, None
156160

157161
case EnumT(_, _, discriminants):
158162
variant_var = self._fresh_var('ARG_VARIDX')
@@ -163,51 +167,62 @@ def _symbolic_value(self, ty: Ty, mutable: bool) -> tuple[KInner, Iterable[KInne
163167
mlEqualsTrue(leInt(variant_var, token(max_variant))),
164168
]
165169
args = self._fresh_var('ENUM_ARGS')
166-
return KApply('Value::Aggregate', (KApply('variantIdx', (variant_var,)), args)), idx_range
170+
return KApply('Value::Aggregate', (KApply('variantIdx', (variant_var,)), args)), idx_range, None
167171

168172
case StructT(_, _, fields):
169173
field_vars: list[KInner] = []
170174
field_constraints: list[KInner] = []
171175
for _ty in fields:
172-
new_var, new_constraints = self._symbolic_value(_ty, mutable)
176+
new_var, new_constraints, _ = self._symbolic_value(_ty, mutable)
173177
field_vars.append(new_var)
174178
field_constraints += new_constraints
175179
return (
176180
KApply('Value::Aggregate', (KApply('variantIdx', (token(0),)), list_of(field_vars))),
177181
field_constraints,
182+
None,
178183
)
179184

180185
case UnionT():
181186
args = self._fresh_var('ARG_UNION')
182-
return KApply('Value::Aggregate', (KApply('variantIdx', (token(0),)), args)), []
187+
return KApply('Value::Aggregate', (KApply('variantIdx', (token(0),)), args)), [], None
183188

184189
case ArrayT(_, None):
185190
elems = self._fresh_var('ARG_ARRAY')
186-
return KApply('Value::Range', (elems,)), []
191+
len = self._fresh_var('ARG_ARRAY_LEN')
192+
return (
193+
KApply('Value::Range', (elems,)),
194+
[mlEqualsTrue(eqInt(KApply('sizeList', (elems,)), len))],
195+
KApply('dynamicSize', (len,)),
196+
)
187197

188198
case ArrayT(element_type, size) if size is not None:
189199
elem_vars: list[KInner] = []
190200
elem_constraints: list[KInner] = []
191201
for _ in range(size):
192-
new_var, new_constraints = self._symbolic_value(element_type, mutable)
202+
new_var, new_constraints, _ = self._symbolic_value(element_type, mutable)
193203
elem_vars.append(new_var)
194204
elem_constraints += new_constraints
195-
return KApply('Value::Range', (list_of(elem_vars),)), elem_constraints
205+
return (
206+
KApply('Value::Range', (list_of(elem_vars),)),
207+
elem_constraints,
208+
KApply('staticSize', (token(size),)),
209+
)
196210

197211
case TupleT(components):
198212
elem_vars = []
199213
elem_constraints = []
200214
for _ty in components:
201-
new_var, new_constraints = self._symbolic_value(_ty, mutable)
215+
new_var, new_constraints, _ = self._symbolic_value(_ty, mutable)
202216
elem_vars.append(new_var)
203217
elem_constraints += new_constraints
204218
return (
205219
KApply('Value::Aggregate', (KApply('variantIdx', (token(0),)), list_of(elem_vars))),
206220
elem_constraints,
221+
None,
207222
)
208223

209224
case RefT(pointee_ty):
210-
pointee_var, pointee_constraints = self._symbolic_value(pointee_ty, mutable)
225+
pointee_var, pointee_constraints, metadata = self._symbolic_value(pointee_ty, mutable)
211226
ref = self.ref_offset
212227
self.ref_offset += 1
213228
self.pointees.append(_typed_value(pointee_var, pointee_ty, mutable))
@@ -218,12 +233,14 @@ def _symbolic_value(self, ty: Ty, mutable: bool) -> tuple[KInner, Iterable[KInne
218233
token(0),
219234
KApply('place', (KApply('local', (token(ref),)), KApply('ProjectionElems::empty', ()))),
220235
KApply('Mutability::Mut', ()) if mutable else KApply('Mutability::Not', ()),
236+
metadata if metadata is not None else KApply('noMetadata', ()),
221237
),
222238
),
223239
pointee_constraints,
240+
None,
224241
)
225242
case PtrT(pointee_ty):
226-
pointee_var, pointee_constraints = self._symbolic_value(pointee_ty, mutable)
243+
pointee_var, pointee_constraints, metadata = self._symbolic_value(pointee_ty, mutable)
227244
ref = self.ref_offset
228245
self.ref_offset += 1
229246
self.pointees.append(_typed_value(pointee_var, pointee_ty, mutable))
@@ -234,12 +251,14 @@ def _symbolic_value(self, ty: Ty, mutable: bool) -> tuple[KInner, Iterable[KInne
234251
token(0),
235252
KApply('place', (KApply('local', (token(ref),)), KApply('ProjectionElems::empty', ()))),
236253
KApply('Mutability::Mut', ()) if mutable else KApply('Mutability::Not', ()),
254+
KApply('PtrEmulation', (metadata if metadata is not None else KApply('noMetadata', ()),)),
237255
),
238256
),
239257
pointee_constraints,
258+
None,
240259
)
241260
case other:
242261
_LOGGER.warning(f'Missing type information ({other}) for type {ty}')
243262
# missing type information, but can assert that this is a value
244263
var = self._fresh_var('ARG')
245-
return var, [mlEqualsTrue(KApply('isValue', (var,)))]
264+
return var, [mlEqualsTrue(KApply('isValue', (var,)))], None

kmir/src/kmir/kdist/mir-semantics/lemmas/kmir-lemmas.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,36 @@ The lists used in the semantics are cons-lists, so only rules with a head elemen
3131
rule N <Int size(ListItem(_) REST:List) => N -Int 1 <Int size(REST)
3232
requires 0 <Int N
3333
[simplification, symbolic(REST)]
34+
35+
rule 0 <=Int size(_LIST:List) => true [simplification]
36+
```
37+
38+
The hooked `range` function selects a segment from a list, by removing elements from front and back.
39+
If nothing is removed, the list remains the same. If all elements are removed, nothing remains.
40+
41+
```k
42+
rule range(L:List, 0, 0) => L [simplification]
43+
44+
rule range(L:List, size(L), 0) => .List [simplification]
45+
46+
rule range(L:List, 0, size(L)) => .List [simplification]
47+
48+
rule size(range(L, A, B)) => size(L) -Int A -Int B
49+
requires A +Int B <=Int size(L) [simplification, preserves-definedness]
50+
51+
rule #Ceil(range(L, A, B)) => #Ceil(L) #And #Ceil(A) #And #Ceil(B) #And {true #Equals A +Int B <=Int size(L)} [simplification]
52+
```
53+
54+
## Simplifications for Int
55+
56+
These are trivial simplifications driven by syntactic equality, which should be present upstream.
57+
58+
```k
59+
rule A <=Int A => true [simplification]
60+
61+
rule A ==Int A => true [simplification]
62+
63+
rule A -Int A => 0 [simplification]
3464
```
3565

3666
## Simplifications related to the `truncate` function

0 commit comments

Comments
 (0)