Skip to content

Commit 88fcd15

Browse files
--terminate-on-thunk option to stop execution on thunk (#834)
This PR adds `--terminate-on-thunk` option to the `kmir` cli. Enabling this option will mean that execution will terminate if a `thunk` is applied. The technical details of how this applied is a cut point rule for `thunk` is applied which means `thunk` will be at the top of the `<k>` cell. Then the terminal rule is able to match that `thunk`. --------- Co-authored-by: Tamás Tóth <tothtamas28@users.noreply.github.com>
1 parent 8ef12df commit 88fcd15

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed

kmir/src/kmir/__main__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,12 @@ def _arg_parser() -> ArgumentParser:
263263
prove_args.add_argument(
264264
'--break-on-thunk', dest='break_on_thunk', action='store_true', help='Break on thunk evaluation'
265265
)
266+
prove_args.add_argument(
267+
'--terminate-on-thunk',
268+
dest='terminate_on_thunk',
269+
action='store_true',
270+
help='Terminate proof when reaching a thunk',
271+
)
266272
prove_args.add_argument(
267273
'--break-every-statement',
268274
dest='break_every_statement',
@@ -507,6 +513,7 @@ def _parse_args(ns: Namespace) -> KMirOpts:
507513
break_on_terminator_unreachable=ns.break_on_terminator_unreachable,
508514
break_every_terminator=ns.break_every_terminator,
509515
break_every_step=ns.break_every_step,
516+
terminate_on_thunk=ns.terminate_on_thunk,
510517
)
511518
case 'link':
512519
return LinkOpts(

kmir/src/kmir/kmir.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from pyk.cli.utils import bug_report_arg
1111
from pyk.cterm import CTerm, cterm_symbolic
12-
from pyk.kast.inner import KApply, KSequence, KSort, KToken, KVariable, Subst
12+
from pyk.kast.inner import KApply, KLabel, KSequence, KSort, KToken, KVariable, Subst
1313
from pyk.kast.manip import abstract_term_safely, split_config_from
1414
from pyk.kcfg import KCFG
1515
from pyk.kcfg.explore import KCFGExplore
@@ -128,13 +128,14 @@ def from_kompiled_kore(
128128

129129
class Symbols:
130130
END_PROGRAM: Final = KApply('#EndProgram_KMIR-CONTROL-FLOW_KItem')
131+
THUNK: Final = KLabel('thunk(_)_RT-DATA_Value_Evaluation')
131132

132133
@cached_property
133134
def parser(self) -> Parser:
134135
return Parser(self.definition)
135136

136137
@contextmanager
137-
def kcfg_explore(self, label: str | None = None) -> Iterator[KCFGExplore]:
138+
def kcfg_explore(self, label: str | None = None, terminate_on_thunk: bool = False) -> Iterator[KCFGExplore]:
138139
with cterm_symbolic(
139140
self.definition,
140141
self.definition_dir,
@@ -143,7 +144,7 @@ def kcfg_explore(self, label: str | None = None) -> Iterator[KCFGExplore]:
143144
id=label if self.bug_report is not None else None, # NB bug report arg.s must be coherent
144145
simplify_each=30,
145146
) as cts:
146-
yield KCFGExplore(cts, kcfg_semantics=KMIRSemantics())
147+
yield KCFGExplore(cts, kcfg_semantics=KMIRSemantics(terminate_on_thunk=terminate_on_thunk))
147148

148149
def run_smir(
149150
self,
@@ -247,7 +248,7 @@ def prove_rs(opts: ProveRSOpts) -> APRProof:
247248
break_on_calls=opts.break_on_calls,
248249
break_on_function_calls=opts.break_on_function_calls,
249250
break_on_intrinsic_calls=opts.break_on_intrinsic_calls,
250-
break_on_thunk=opts.break_on_thunk,
251+
break_on_thunk=opts.break_on_thunk or opts.terminate_on_thunk, # must break for terminal rule
251252
break_every_statement=opts.break_every_statement,
252253
break_on_terminator_goto=opts.break_on_terminator_goto,
253254
break_on_terminator_switch_int=opts.break_on_terminator_switch_int,
@@ -260,15 +261,26 @@ def prove_rs(opts: ProveRSOpts) -> APRProof:
260261
break_every_step=opts.break_every_step,
261262
)
262263

263-
with kmir.kcfg_explore(label) as kcfg_explore:
264+
with kmir.kcfg_explore(label, terminate_on_thunk=opts.terminate_on_thunk) as kcfg_explore:
264265
prover = APRProver(kcfg_explore, execute_depth=opts.max_depth, cut_point_rules=cut_point_rules)
265266
prover.advance_proof(apr_proof, max_iterations=opts.max_iterations)
266267
return apr_proof
267268

268269

269270
class KMIRSemantics(DefaultSemantics):
271+
terminate_on_thunk: bool
272+
273+
def __init__(self, terminate_on_thunk: bool = False) -> None:
274+
self.terminate_on_thunk = terminate_on_thunk
275+
270276
def is_terminal(self, cterm: CTerm) -> bool:
271277
k_cell = cterm.cell('K_CELL')
278+
279+
if self.terminate_on_thunk: # terminate on `thunk ( ... )` rule
280+
match k_cell:
281+
case KApply(label, _) | KSequence((KApply(label, _), *_)) if label == KMIR.Symbols.THUNK:
282+
return True
283+
272284
# <k> #EndProgram </k>
273285
if k_cell == KMIR.Symbols.END_PROGRAM:
274286
return True

kmir/src/kmir/options.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ProveOpts(KMirOpts):
5555
break_on_terminator_unreachable: bool
5656
break_every_terminator: bool
5757
break_every_step: bool
58+
terminate_on_thunk: bool
5859

5960
def __init__(
6061
self,
@@ -77,6 +78,7 @@ def __init__(
7778
break_on_terminator_unreachable: bool = False,
7879
break_every_terminator: bool = False,
7980
break_every_step: bool = False,
81+
terminate_on_thunk: bool = False,
8082
) -> None:
8183
self.proof_dir = Path(proof_dir).resolve() if proof_dir is not None else None
8284
self.bug_report = bug_report
@@ -97,6 +99,7 @@ def __init__(
9799
self.break_on_terminator_unreachable = break_on_terminator_unreachable
98100
self.break_every_terminator = break_every_terminator
99101
self.break_every_step = break_every_step
102+
self.terminate_on_thunk = terminate_on_thunk
100103

101104

102105
@dataclass
@@ -131,6 +134,7 @@ def __init__(
131134
break_on_terminator_unreachable: bool = False,
132135
break_every_terminator: bool = False,
133136
break_every_step: bool = False,
137+
terminate_on_thunk: bool = False,
134138
) -> None:
135139
self.rs_file = rs_file
136140
self.proof_dir = Path(proof_dir).resolve() if proof_dir is not None else None
@@ -155,6 +159,7 @@ def __init__(
155159
self.break_on_terminator_unreachable = break_on_terminator_unreachable
156160
self.break_every_terminator = break_every_terminator
157161
self.break_every_step = break_every_step
162+
self.terminate_on_thunk = terminate_on_thunk
158163

159164

160165
@dataclass

0 commit comments

Comments
 (0)