Merge branch 'feature/subtle-nontail-fail' into 'dev'
Support always-failing conditionals Closes #119 See merge request ligolang/ligo!311
This commit is contained in:
commit
8f2ff058ec
@ -1012,3 +1012,27 @@ let%expect_test _ =
|
|||||||
let%expect_test _ =
|
let%expect_test _ =
|
||||||
run_ligo_good [ "dry-run" ; contract "double_main.ligo" ; "main" ; "unit" ; "0" ] ;
|
run_ligo_good [ "dry-run" ; contract "double_main.ligo" ; "main" ; "unit" ; "0" ] ;
|
||||||
[%expect {|( [] , 2 ) |}]
|
[%expect {|( [] , 2 ) |}]
|
||||||
|
|
||||||
|
let%expect_test _ =
|
||||||
|
run_ligo_good [ "compile-contract" ; contract "subtle_nontail_fail.mligo" ; "main" ] ;
|
||||||
|
[%expect {|
|
||||||
|
{ parameter unit ;
|
||||||
|
storage unit ;
|
||||||
|
code { PUSH bool True ;
|
||||||
|
IF { PUSH string "This contract always fails" ; FAILWITH }
|
||||||
|
{ PUSH string "This contract still always fails" ; FAILWITH } } } |}]
|
||||||
|
|
||||||
|
let%expect_test _ =
|
||||||
|
(* TODO should not be bad? *)
|
||||||
|
run_ligo_bad [ "dry-run" ; contract "subtle_nontail_fail.mligo" ; "main" ; "()" ; "()" ] ;
|
||||||
|
[%expect {|
|
||||||
|
ligo: error of execution
|
||||||
|
|
||||||
|
If you're not sure how to fix this error, you can
|
||||||
|
do one of the following:
|
||||||
|
|
||||||
|
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
|
||||||
|
* Ask a question on our Discord: https://discord.gg/9rhYaEt
|
||||||
|
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
|
||||||
|
* Check the changelog by running 'ligo changelog' |}]
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ let run_contract ?options (exp:Michelson.t) (exp_type:ex_ty) (input_michelson:Mi
|
|||||||
let exp = Michelson.strip_annots exp in
|
let exp = Michelson.strip_annots exp in
|
||||||
let%bind descr =
|
let%bind descr =
|
||||||
Trace.trace_tzresult_lwt (simple_error "error parsing program code") @@
|
Trace.trace_tzresult_lwt (simple_error "error parsing program code") @@
|
||||||
Memory_proto_alpha.parse_michelson ~top_level exp ty_stack_before ty_stack_after in
|
Memory_proto_alpha.parse_michelson_fail ~top_level exp ty_stack_before ty_stack_after in
|
||||||
let open! Memory_proto_alpha.Protocol.Script_interpreter in
|
let open! Memory_proto_alpha.Protocol.Script_interpreter in
|
||||||
let%bind (Item(output, Empty)) =
|
let%bind (Item(output, Empty)) =
|
||||||
Trace.trace_tzresult_lwt (simple_error "error of execution") @@
|
Trace.trace_tzresult_lwt (simple_error "error of execution") @@
|
||||||
@ -120,7 +120,7 @@ let run_expression ?options (exp:Michelson.t) (exp_type:ex_ty) : run_res result
|
|||||||
and ty_stack_after = Script_typed_ir.Item_t (exp_type', Empty_t, None) in
|
and ty_stack_after = Script_typed_ir.Item_t (exp_type', Empty_t, None) in
|
||||||
let%bind descr =
|
let%bind descr =
|
||||||
Trace.trace_tzresult_lwt (simple_error "error parsing program code") @@
|
Trace.trace_tzresult_lwt (simple_error "error parsing program code") @@
|
||||||
Memory_proto_alpha.parse_michelson ~top_level exp ty_stack_before ty_stack_after in
|
Memory_proto_alpha.parse_michelson_fail ~top_level exp ty_stack_before ty_stack_after in
|
||||||
let open! Memory_proto_alpha.Protocol.Script_interpreter in
|
let open! Memory_proto_alpha.Protocol.Script_interpreter in
|
||||||
let%bind res =
|
let%bind res =
|
||||||
Trace.trace_tzresult_lwt (simple_error "error of execution") @@
|
Trace.trace_tzresult_lwt (simple_error "error of execution") @@
|
||||||
|
@ -359,13 +359,30 @@ let opt_swap2 : peep2 = function
|
|||||||
thwarting the intent of the Michelson tail fail restriction -- the
|
thwarting the intent of the Michelson tail fail restriction -- the
|
||||||
LIGO _user_ might accidentally write dead code immediately after a
|
LIGO _user_ might accidentally write dead code immediately after a
|
||||||
failure, and we will simply erase it. *)
|
failure, and we will simply erase it. *)
|
||||||
|
let rec is_failing : michelson -> bool =
|
||||||
|
function
|
||||||
|
| Seq (_, []) -> false
|
||||||
|
| Seq (_, [arg]) -> is_failing arg
|
||||||
|
| Seq (l, _ :: args) -> is_failing (Seq (l, args))
|
||||||
|
| Prim (_, I_FAILWITH, _, _) -> true
|
||||||
|
| Prim (_, I_IF, [bt; bf], _)
|
||||||
|
| Prim (_, I_IF_CONS, [bt; bf], _)
|
||||||
|
| Prim (_, I_IF_LEFT, [bt; bf], _)
|
||||||
|
| Prim (_, I_IF_NONE, [bt; bf], _) ->
|
||||||
|
is_failing bt && is_failing bf
|
||||||
|
(* Note: the body of ITER, LOOP, LOOP_LEFT _can_ be
|
||||||
|
failing. However, the loop will _not_ be failing, because the
|
||||||
|
body might never be executed. The body of MAP _cannot_ be
|
||||||
|
failing. *)
|
||||||
|
| _ -> false
|
||||||
|
|
||||||
let rec opt_tail_fail : michelson -> michelson =
|
let rec opt_tail_fail : michelson -> michelson =
|
||||||
function
|
function
|
||||||
| Seq (l, args) ->
|
| Seq (l, args) ->
|
||||||
let rec aux args =
|
let rec aux args =
|
||||||
match args with
|
match args with
|
||||||
| [] -> []
|
| [] -> []
|
||||||
| Prim (l, I_FAILWITH, args, annot) :: _ -> [ Prim (l, I_FAILWITH, args, annot) ]
|
| arg :: _ when is_failing arg -> [arg]
|
||||||
| arg :: args -> arg :: aux args in
|
| arg :: args -> arg :: aux args in
|
||||||
let args = aux args in
|
let args = aux args in
|
||||||
Seq (l, List.map opt_tail_fail args)
|
Seq (l, List.map opt_tail_fail args)
|
||||||
|
4
src/test/contracts/subtle_nontail_fail.mligo
Normal file
4
src/test/contracts/subtle_nontail_fail.mligo
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
let main (ps : unit * unit) =
|
||||||
|
if true
|
||||||
|
then failwith "This contract always fails"
|
||||||
|
else failwith "This contract still always fails"
|
Loading…
Reference in New Issue
Block a user