Introduce contract-level compile-time constants (#432)

* Allow compile-time constants as toplevel declarations

* Remove the test that fails on toplevel consts

* Warn when shadowing a constant

* Allow records to be used as compile time constants

* Allow data constructors in compile-time constants

* Disable some warnings for toplevel constants

Since variables and functions cannot be used in the definition of
a compile time constants, the following warnings are not going to be
reported:

* Used/Unused variable
* Used/Unused function

* Do not reverse constants declarations

* Add tests for all valid expressions

* Add test for accessing const from namespace

* Revert "Do not reverse constants declarations"

This reverts commit c4647fadacd134866e4be9c2ab4b0d54870a35fd.

* Add test for assigining constant to a constant

* Show empty map or record error when assigning to const

* Report all invalid constant expressions before fail

* Allow accessing records fields in toplevel consts

* Undo a mistake

* Add test for warning on const shadowing

* Show error message when using pattern matching for consts

* Remove unused error

* Ban toplevel constants in contract interfaces

* Varibles rename

* Change the error message for invalid_const_id

* Make constants public in namespaces and private in contracts

* Add a warning about unused constants in contracts

* Use ban_when_const for function applications

* Test for qualified access of constants in functions

* Add failing tests

* Add test for the unused const warning

* Update CHANGELOG

* Update all_syntax test file

* Treat expr and type inside bound as bound

* Allow typed ids to be used for constants

* List valid exprs in the error message for invalid exprs

* Fix tests

* Update the docs about constants

* Update syntax docs

* Check validity of const exprs in a separate functions

* Call both resolve_const and resolve_fun from resolve_var
This commit is contained in:
Gaith Hallak
2023-04-12 14:20:41 +03:00
committed by GitHub
parent 99bb3fe1fb
commit 7b6eba5319
16 changed files with 511 additions and 45 deletions
+79 -6
View File
@@ -222,6 +222,7 @@ compilable_contracts() ->
"unapplied_contract_call",
"unapplied_named_arg_builtin",
"resolve_field_constraint_by_arity",
"toplevel_constants",
"test" % Custom general-purpose test file. Keep it last on the list.
].
@@ -286,7 +287,11 @@ warnings() ->
<<?PosW(48, 5)
"Unused return value.">>,
<<?PosW(60, 5)
"The function `dec` is defined but never used.">>
"The function `dec` is defined but never used.">>,
<<?PosW(73, 9)
"The definition of `const` shadows an older definition at line 70, column 3.">>,
<<?PosW(84, 7)
"The constant `c` is defined but never used.">>
]).
failing_contracts() ->
@@ -658,10 +663,6 @@ failing_contracts() ->
[<<?Pos(5, 28)
"Invalid call to contract entrypoint `Foo.foo`.\n"
"It must be called as `c.foo` for some `c : Foo`.">>])
, ?TYPE_ERROR(toplevel_let,
[<<?Pos(2, 7)
"Toplevel \"let\" definitions are not supported. "
"Value `this_is_illegal` could be replaced by 0-argument function.">>])
, ?TYPE_ERROR(empty_typedecl,
[<<?Pos(2, 8)
"Empty type declarations are not supported. "
@@ -859,7 +860,11 @@ failing_contracts() ->
<<?Pos(48, 5)
"Unused return value.">>,
<<?Pos(60, 5)
"The function `dec` is defined but never used.">>
"The function `dec` is defined but never used.">>,
<<?Pos(73, 9)
"The definition of `const` shadows an older definition at line 70, column 3.">>,
<<?Pos(84, 7)
"The constant `c` is defined but never used.">>
])
, ?TYPE_ERROR(polymorphism_contract_interface_recursive,
[<<?Pos(1,24)
@@ -1193,6 +1198,74 @@ failing_contracts() ->
<<?Pos(13,20)
"Found a hole of type `'a`">>
])
, ?TYPE_ERROR(toplevel_constants_contract_as_namespace,
[<<?Pos(5,13)
"Invalid use of the contract constant `G.const`.\n"
"Toplevel contract constants can only be used in the contracts where they are defined.">>,
<<?Pos(10,11)
"Record type `G` does not have field `const`">>,
<<?Pos(10,11)
"Unbound field const">>,
<<?Pos(11,11)
"Record type `G` does not have field `const`">>,
<<?Pos(11,11)
"Unbound field const">>
])
, ?TYPE_ERROR(toplevel_constants_cycles,
[<<?Pos(2,21)
"Unbound variable `selfcycle`">>,
<<?Pos(4,5)
"Mutual recursion detected between the constants\n"
" - `cycle1` at line 4, column 5\n"
" - `cycle2` at line 5, column 5\n"
" - `cycle3` at line 6, column 5">>
])
, ?TYPE_ERROR(toplevel_constants_in_interface,
[<<?Pos(2,10)
"The name of the compile-time constant cannot have pattern matching">>,
<<?Pos(3,5)
"Cannot define toplevel constants inside a contract interface">>,
<<?Pos(4,5)
"Cannot define toplevel constants inside a contract interface">>
])
, ?TYPE_ERROR(toplevel_constants_invalid_expr,
[<<?Pos(10,9)
"Invalid expression in the definition of the constant `c01`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(11,9)
"Invalid expression in the definition of the constant `c02`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(12,9)
"Invalid expression in the definition of the constant `c03`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(13,9)
"Invalid expression in the definition of the constant `c04`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(14,9)
"Invalid expression in the definition of the constant `c05`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(17,9)
"Invalid expression in the definition of the constant `c07`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(18,9)
"Invalid expression in the definition of the constant `c08`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(19,9)
"Invalid expression in the definition of the constant `c09`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(20,9)
"Invalid expression in the definition of the constant `c10`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
<<?Pos(21,9)
"Invalid expression in the definition of the constant `c11`\n"
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>
])
, ?TYPE_ERROR(toplevel_constants_invalid_id,
[<<?Pos(2,9)
"The name of the compile-time constant cannot have pattern matching">>,
<<?Pos(3,9)
"The name of the compile-time constant cannot have pattern matching">>
])
].
validation_test_() ->
+3
View File
@@ -6,6 +6,7 @@
namespace Ns =
datatype d('a) = D | S(int) | M('a, list('a), int)
private function fff() = 123
let const = 1
stateful entrypoint
f (1, x) = (_) => x
@@ -33,6 +34,8 @@ contract AllSyntax =
type state = shakespeare(int)
let cc = "str"
entrypoint init() = {
johann = 1000,
wolfgang = -10,
+64
View File
@@ -0,0 +1,64 @@
namespace N0 =
let nsconst = 1
namespace N =
let nsconst = N0.nsconst
contract C =
datatype event = EventX(int, string)
record account = { name : string,
balance : int }
let c01 = 2425
let c02 = -5
let c03 = ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
let c04 = true
let c05 = Bits.none
let c06 = #fedcba9876543210
let c07 = "str"
let c08 = [1, 2, 3]
let c09 = [(true, 24), (false, 19), (false, -42)]
let c10 = (42, "Foo", true)
let c11 = { name = "str", balance = 100000000 }
let c12 = {["foo"] = 19, ["bar"] = 42}
let c13 = Some(42)
let c14 = 11 : int
let c15 = EventX(0, "Hello")
let c16 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
let c17 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
let c18 = RelativeTTL(50)
let c19 = ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
let c20 = oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
let c21 = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ : C
let c22 = N.nsconst
let c23 = c01
let c24 = c11.name
let c25 : int = 1
entrypoint f01() = c01
entrypoint f02() = c02
entrypoint f03() = c03
entrypoint f04() = c04
entrypoint f05() = c05
entrypoint f06() = c06
entrypoint f07() = c07
entrypoint f08() = c08
entrypoint f09() = c09
entrypoint f10() = c10
entrypoint f11() = c11
entrypoint f12() = c12
entrypoint f13() = c13
entrypoint f14() = c14
entrypoint f15() = c15
entrypoint f16() = c16
entrypoint f17() = c17
entrypoint f18() = c18
entrypoint f19() = c19
entrypoint f20() = c20
entrypoint f21() = c21
entrypoint f22() = c22
entrypoint f23() = c23
entrypoint f24() = c24
entrypoint f25() = c25
entrypoint fqual() = C.c01
@@ -0,0 +1,11 @@
contract G =
let const = 1
main contract C =
let c = G.const
stateful entrypoint f() =
let g = Chain.create() : G
g.const
g.const()
@@ -0,0 +1,6 @@
contract C =
let selfcycle = selfcycle
let cycle1 = cycle2
let cycle2 = cycle3
let cycle3 = cycle1
@@ -0,0 +1,7 @@
contract interface I =
let (x::y::_) = [1,2,3]
let c = 10
let d = 10
contract C =
entrypoint init() = ()
@@ -0,0 +1,21 @@
main contract C =
record account = { name : string,
balance : int }
let one = 1
let opt = Some(5)
let acc = { name = "str", balance = 100000 }
let mpp = {["foo"] = 19, ["bar"] = 42}
let c01 = [x | x <- [1,2,3,4,5]]
let c02 = [x + k | x <- [1,2,3,4,5], let k = x*x]
let c03 = [x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
let c04 = if (one > 2) 3 else 4
let c05 = switch (opt)
Some(x) => x
None => 2
let c07 = acc{ balance = one }
let c08 = mpp["foo"]
let c09 = mpp["non" = 10]
let c10 = mpp{["foo"] = 20}
let c11 = (x) => x + 1
@@ -0,0 +1,3 @@
contract C =
let x::_ = [1,2,3,4]
let y::(p = z::_) = [1,2,3,4]
-3
View File
@@ -1,3 +0,0 @@
contract C =
let this_is_illegal = 2/0
entrypoint this_is_legal() = 2/0
+21
View File
@@ -65,3 +65,24 @@ contract Remote =
contract C =
payable stateful entrypoint
call_missing_con() : int = (ct_1111111111111111111111111111112JF6Dz72 : Remote).id(value = 1, 0)
namespace ShadowingConst =
let const = 1
function f() =
let const = 2
const
namespace UnusedConstNamespace =
// No warnings should be shown even though const is not used
let const = 1
contract UnusedConstContract =
// Only `c` should show a warning because it is never used in the contract
let a = 1
let b = 2
let c = 3
entrypoint f() =
// Both normal access and qualified access should prevent the unused const warning
a + UnusedConstContract.b