From d059cf991b6b5d052683c49c6977e53ffe04641f Mon Sep 17 00:00:00 2001 From: Sander Spies Date: Tue, 24 Mar 2020 13:58:18 +0100 Subject: [PATCH 1/3] Give an error when nesting a big_map inside another big_map --- gitlab-pages/docs/reference/big_map.md | 38 +++++++------ src/bin/expect_tests/contract_tests.ml | 54 ++++++++++++++++++- ...t_limit.ml => entrypoints_length_limit.ml} | 0 .../self_ast_imperative.ml | 2 +- .../9-self_ast_typed/no_nested_big_map.ml | 53 ++++++++++++++++++ src/passes/9-self_ast_typed/self_ast_typed.ml | 1 + .../contracts/negative/nested_bigmap_1.religo | 10 ++++ .../contracts/negative/nested_bigmap_2.religo | 9 ++++ .../contracts/negative/nested_bigmap_3.religo | 15 ++++++ .../contracts/negative/nested_bigmap_4.religo | 9 ++++ 10 files changed, 172 insertions(+), 19 deletions(-) rename src/passes/3-self_ast_imperative/{entrypoints_lenght_limit.ml => entrypoints_length_limit.ml} (100%) create mode 100644 src/passes/9-self_ast_typed/no_nested_big_map.ml create mode 100644 src/test/contracts/negative/nested_bigmap_1.religo create mode 100644 src/test/contracts/negative/nested_bigmap_2.religo create mode 100644 src/test/contracts/negative/nested_bigmap_3.religo create mode 100644 src/test/contracts/negative/nested_bigmap_4.religo diff --git a/gitlab-pages/docs/reference/big_map.md b/gitlab-pages/docs/reference/big_map.md index e20bdc48c..dc5893df1 100644 --- a/gitlab-pages/docs/reference/big_map.md +++ b/gitlab-pages/docs/reference/big_map.md @@ -8,9 +8,13 @@ hide_table_of_contents: true import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; -A lazily deserialized map that's intended to store large amounts of data. +A lazily deserialized map that's intended to store large amounts of data. +Lazily means that storage is read or written per key on demand. Therefore +there are no `map`, `fold`, and `iter` operations as in +[Map](./map-reference). -The gast costs of deserialized maps are higher than standard maps as data is lazily deserialized. +The gast costs of big maps are higher than standard maps as data is lazily +deserialized. type big_map ('key, 'value) @@ -47,11 +51,11 @@ type register = (address, move) big_map The type of a big map from values of type `key` to -values of type `value` is `big_map (key, value)`. +values of type `value` is `big_map(key, value)`. ```reasonligo group=big_map type move = (int, int); -type register = big_map (address, move); +type register = big_map(address, move); ``` @@ -63,7 +67,7 @@ function empty : big_map ('key, 'value) val empty : ('key, 'value) big_map -let empty: big_map ('key, 'value) +let empty: big_map('key, 'value) Create an empty big_map. @@ -91,7 +95,7 @@ let empty : register = Big_map.empty ```reasonligo group=big_map -let empty : register = Big_map.empty +let empty: register = Big_map.empty ``` @@ -141,7 +145,7 @@ let moves : register = ```reasonligo group=big_map -let moves : register = +let moves: register = Big_map.literal ([ ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); @@ -156,7 +160,7 @@ function find_opt : 'key -> big_map ('key, 'value) -> option 'value val find_opt : 'key -> ('key, 'value) big_map -> 'value option -let find_opt : ('key, big_map ('key, 'value)) => option ('value) +let find_opt: ('key, big_map ('key, 'value)) => option ('value) Retrieve a value from a big map with the given key. @@ -191,8 +195,8 @@ let my_balance : move option = ```reasonligo group=big_map -let my_balance : option (move) = - Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, moves); +let my_balance: option (move) = + Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); ``` @@ -204,7 +208,7 @@ function update : 'key -> option 'value -> big_map ('key, 'value) -> big_map ('k val update: 'key -> 'value option -> ('key, 'value) big_map -> ('key, 'value) big_map -let update: ('key, option('value), big_map ('key, 'value)) => big_map ('key, 'value) +let update: ('key, option('value), big_map('key, 'value)) => big_map('key, 'value) Note: when `None` is used as a value, the value is removed from the big_map. @@ -255,9 +259,9 @@ let updated_map : register = ```reasonligo group=big_map -let updated_map : register = +let updated_map: register = Big_map.update - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some ((4,9)), moves); + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); ``` @@ -292,7 +296,7 @@ let add (m : register) : register = ```reasonligo group=big_map let add = (m: register): register => Big_map.add - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (4,9), m); + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (4,9), m); ``` @@ -305,7 +309,7 @@ function remove: 'key -> big_map ('key, 'value) -> big_map ('key, 'value) val remove: 'key -> ('key, 'value) big_map -> ('key, 'value) big_map -let remove: ('key, big_map ('key, 'value)) => big_map ('key, 'value) +let remove: ('key, big_map('key, 'value)) => big_map('key, 'value) @@ -340,8 +344,8 @@ let updated_map : register = ```reasonligo group=big_map -let updated_map : register = - Big_map.remove (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves) +let updated_map: register = + Big_map.remove(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves) ``` diff --git a/src/bin/expect_tests/contract_tests.ml b/src/bin/expect_tests/contract_tests.ml index 3785881be..984a0163b 100644 --- a/src/bin/expect_tests/contract_tests.ml +++ b/src/bin/expect_tests/contract_tests.ml @@ -1344,4 +1344,56 @@ let%expect_test _ = * 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' |}] + * Check the changelog by running 'ligo changelog' |}]; + + run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_1.religo"; "main"]; + [%expect {| + ligo: It looks like you have nested a big map inside another big map. This is not supported. : {} + + + 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' |}]; + + run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_2.religo"; "main"]; + [%expect {| + ligo: It looks like you have nested a big map inside another big map. This is not supported. : {} + + + 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' |}]; + + run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_3.religo"; "main"]; + [%expect {| + ligo: It looks like you have nested a big map inside another big map. This is not supported. : {} + + + 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' |}]; + + run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_4.religo"; "main"]; + [%expect {| + ligo: It looks like you have nested a big map inside another big map. This is not supported. : {} + + + 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' |}] \ No newline at end of file diff --git a/src/passes/3-self_ast_imperative/entrypoints_lenght_limit.ml b/src/passes/3-self_ast_imperative/entrypoints_length_limit.ml similarity index 100% rename from src/passes/3-self_ast_imperative/entrypoints_lenght_limit.ml rename to src/passes/3-self_ast_imperative/entrypoints_length_limit.ml diff --git a/src/passes/3-self_ast_imperative/self_ast_imperative.ml b/src/passes/3-self_ast_imperative/self_ast_imperative.ml index a10968c0c..b0270ebd0 100644 --- a/src/passes/3-self_ast_imperative/self_ast_imperative.ml +++ b/src/passes/3-self_ast_imperative/self_ast_imperative.ml @@ -6,7 +6,7 @@ let all_expression_mapper = [ Literals.peephole_expression ; ] let all_type_expression_mapper = [ - Entrypoints_lenght_limit.peephole_type_expression ; + Entrypoints_length_limit.peephole_type_expression ; ] let all_exp = List.map (fun el -> Helpers.Expression el) all_expression_mapper diff --git a/src/passes/9-self_ast_typed/no_nested_big_map.ml b/src/passes/9-self_ast_typed/no_nested_big_map.ml new file mode 100644 index 000000000..6e5ca3cd6 --- /dev/null +++ b/src/passes/9-self_ast_typed/no_nested_big_map.ml @@ -0,0 +1,53 @@ +open Ast_typed +open Trace + +type contract_pass_data = Contract_passes.contract_pass_data + +module Errors = struct + let no_nested_bigmap () = + let title = (thunk ("It looks like you have nested a big map inside another big map. This is not supported. ")) in + let message () = "" in + let data = [ + (* ("location" , fun () -> Format.asprintf "%a" Location.pp loc) TODO once types have an actual location *) + ] in + error ~data title message () +end + +let rec check_no_nested_bigmap is_in_bigmap e = + match e.type_content with + | T_operator (TC_big_map (_, _)) when is_in_bigmap -> + fail @@ Errors.no_nested_bigmap + | T_operator (TC_big_map (key, value)) -> + let%bind _ = check_no_nested_bigmap true key in + let%bind _ = check_no_nested_bigmap true value in + ok () + | T_operator (TC_contract t) + | T_operator (TC_option t) + | T_operator (TC_list t) + | T_operator (TC_set t) -> + let%bind _ = check_no_nested_bigmap is_in_bigmap t in + ok () + | T_operator (TC_map (a, b)) + | T_operator (TC_arrow (a, b)) -> + let%bind _ = check_no_nested_bigmap is_in_bigmap a in + let%bind _ = check_no_nested_bigmap is_in_bigmap b in + ok () + | T_sum s -> + let es = CMap.to_list s in + let%bind _ = bind_map_list (fun l -> check_no_nested_bigmap is_in_bigmap l) es in + ok () + | T_record elm -> + let es = LMap.to_list elm in + let%bind _ = bind_map_list (fun l -> check_no_nested_bigmap is_in_bigmap l) es in + ok () + | T_arrow { type1; type2 } -> + let%bind _ = check_no_nested_bigmap is_in_bigmap type1 in + let%bind _ = check_no_nested_bigmap is_in_bigmap type2 in + ok () + | T_variable _ + | T_constant _ -> + ok () + +let self_typing : contract_pass_data -> expression -> (bool * contract_pass_data * expression) result = fun dat el -> + let%bind _ = check_no_nested_bigmap false el.type_expression in + ok (true, dat, el) diff --git a/src/passes/9-self_ast_typed/self_ast_typed.ml b/src/passes/9-self_ast_typed/self_ast_typed.ml index 76bfbdf90..e8dfefdce 100644 --- a/src/passes/9-self_ast_typed/self_ast_typed.ml +++ b/src/passes/9-self_ast_typed/self_ast_typed.ml @@ -6,6 +6,7 @@ let all_passes = [ let contract_passes = [ Contract_passes.self_typing ; + No_nested_big_map.self_typing ; ] let all_program = diff --git a/src/test/contracts/negative/nested_bigmap_1.religo b/src/test/contracts/negative/nested_bigmap_1.religo new file mode 100644 index 000000000..b86e549b1 --- /dev/null +++ b/src/test/contracts/negative/nested_bigmap_1.religo @@ -0,0 +1,10 @@ +type bar = big_map (nat, int); + +/* this should result in an error as nested big_maps are not supported: */ +type storage = big_map (int, bar); + +type return = (list (operation), storage); + +let main = ((ignore, store): (unit, storage)): return => { + ([]: list(operation), store) +}; diff --git a/src/test/contracts/negative/nested_bigmap_2.religo b/src/test/contracts/negative/nested_bigmap_2.religo new file mode 100644 index 000000000..d8061f912 --- /dev/null +++ b/src/test/contracts/negative/nested_bigmap_2.religo @@ -0,0 +1,9 @@ +/* this should result in an error as nested big_maps are not supported: */ +type storage = big_map (nat, big_map (int, string)); + +type return = (list (operation), storage); + +let main = ((ignore, store): (unit, storage)): return => { + ([]: list(operation), store) +}; + \ No newline at end of file diff --git a/src/test/contracts/negative/nested_bigmap_3.religo b/src/test/contracts/negative/nested_bigmap_3.religo new file mode 100644 index 000000000..e8941f445 --- /dev/null +++ b/src/test/contracts/negative/nested_bigmap_3.religo @@ -0,0 +1,15 @@ +type bar = big_map (nat, int); + +type foo = { + a: int, + b: bar +}; + +/* this should result in an error as nested big_maps are not supported: */ +type storage = big_map(nat, foo); + +type return = (list (operation), storage); + +let main = ((ignore, store): (unit, storage)): return => { + ([]: list(operation), store) +}; diff --git a/src/test/contracts/negative/nested_bigmap_4.religo b/src/test/contracts/negative/nested_bigmap_4.religo new file mode 100644 index 000000000..653908636 --- /dev/null +++ b/src/test/contracts/negative/nested_bigmap_4.religo @@ -0,0 +1,9 @@ +/* this should result in an error as nested big_maps are not supported: */ +type storage = map (int, big_map (nat, big_map (int, string))); + +type return = (list (operation), storage); + +let main = ((ignore, store): (unit, storage)): return => { + ([]: list(operation), store) +}; + \ No newline at end of file From 715c3a8eac0990842f9fac175d85d30fa18217f3 Mon Sep 17 00:00:00 2001 From: Sander Spies Date: Tue, 24 Mar 2020 14:01:04 +0100 Subject: [PATCH 2/3] Inform that it's not possible to nest a big map inside another big map --- gitlab-pages/docs/reference/big_map.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gitlab-pages/docs/reference/big_map.md b/gitlab-pages/docs/reference/big_map.md index dc5893df1..104751667 100644 --- a/gitlab-pages/docs/reference/big_map.md +++ b/gitlab-pages/docs/reference/big_map.md @@ -60,6 +60,8 @@ type register = big_map(address, move); +Be aware that a `big_map` cannot appear inside another `big_map`. + function empty : big_map ('key, 'value) From cb54cd1210f5f8c8695834210a037186aa31eccc Mon Sep 17 00:00:00 2001 From: Sander Spies Date: Thu, 26 Mar 2020 09:23:43 +0100 Subject: [PATCH 3/3] Lambda's and keys shouldn't give errors for nesting big maps. --- src/passes/9-self_ast_typed/no_nested_big_map.ml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/passes/9-self_ast_typed/no_nested_big_map.ml b/src/passes/9-self_ast_typed/no_nested_big_map.ml index 6e5ca3cd6..e1a130ce9 100644 --- a/src/passes/9-self_ast_typed/no_nested_big_map.ml +++ b/src/passes/9-self_ast_typed/no_nested_big_map.ml @@ -18,7 +18,7 @@ let rec check_no_nested_bigmap is_in_bigmap e = | T_operator (TC_big_map (_, _)) when is_in_bigmap -> fail @@ Errors.no_nested_bigmap | T_operator (TC_big_map (key, value)) -> - let%bind _ = check_no_nested_bigmap true key in + let%bind _ = check_no_nested_bigmap false key in let%bind _ = check_no_nested_bigmap true value in ok () | T_operator (TC_contract t) @@ -27,11 +27,14 @@ let rec check_no_nested_bigmap is_in_bigmap e = | T_operator (TC_set t) -> let%bind _ = check_no_nested_bigmap is_in_bigmap t in ok () - | T_operator (TC_map (a, b)) - | T_operator (TC_arrow (a, b)) -> + | T_operator (TC_map (a, b)) -> let%bind _ = check_no_nested_bigmap is_in_bigmap a in let%bind _ = check_no_nested_bigmap is_in_bigmap b in ok () + | T_operator (TC_arrow (a, b)) -> + let%bind _ = check_no_nested_bigmap false a in + let%bind _ = check_no_nested_bigmap false b in + ok () | T_sum s -> let es = CMap.to_list s in let%bind _ = bind_map_list (fun l -> check_no_nested_bigmap is_in_bigmap l) es in @@ -41,8 +44,8 @@ let rec check_no_nested_bigmap is_in_bigmap e = let%bind _ = bind_map_list (fun l -> check_no_nested_bigmap is_in_bigmap l) es in ok () | T_arrow { type1; type2 } -> - let%bind _ = check_no_nested_bigmap is_in_bigmap type1 in - let%bind _ = check_no_nested_bigmap is_in_bigmap type2 in + let%bind _ = check_no_nested_bigmap false type1 in + let%bind _ = check_no_nested_bigmap false type2 in ok () | T_variable _ | T_constant _ ->