Skip to content

Commit bce0e5e

Browse files
committed
[Tolk] The magic lazy keyword
Lazy loading, partial loading, partial updating See pipe-lazy-load-insertions.cpp for comments
1 parent 37c9fcd commit bce0e5e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+5722
-70
lines changed

crypto/smartcont/tolk-stdlib/common.tolk

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,17 @@ struct PackOptions {
233233

234234
/// UnpackOptions allows you to control behavior of `MyStruct.fromCell(c)` and similar functions.
235235
struct UnpackOptions {
236-
// after finished reading all fields from a cell/slice, call [slice.assertEnd] to ensure no remaining data left;
237-
// it's the default behavior, it ensures that you've fully described data you're reading with a struct;
238-
// example: `struct Point { x: int8; y: int8 }`, input "0102" is ok, "0102FF" will throw excno 9;
239-
// note: setting this to false does not decrease gas (DROP from a stack and ENDS cost the same);
240-
// note: this option controls [T.fromCell] and [T.fromSlice], but is ignored by [slice.loadAny]
236+
/// after finished reading all fields from a cell/slice, call [slice.assertEnd] to ensure no remaining data left;
237+
/// it's the default behavior, it ensures that you've fully described data you're reading with a struct;
238+
/// example: `struct Point { x: int8; y: int8 }`, input "0102" is ok, "0102FF" will throw excno 9;
239+
/// note: setting this to false does not decrease gas (DROP from a stack and ENDS cost the same);
240+
/// note: this option controls [T.fromCell] and [T.fromSlice], but is ignored by [slice.loadAny];
241+
/// note: `lazy` ignores this option, because it reads fields on demand or even skips them
241242
assertEndAfterReading: bool = true,
242243

243244
/// this excNo is thrown if a prefix doesn't match, e.g. for `struct (0x01) A` given input "88...";
244-
/// similarly, for a union type, this is thrown when none of the opcodes match
245+
/// similarly, for a union type, this is thrown when none of the opcodes match;
246+
/// note: `lazy` ignores this option if you have `else` in `match` (you write custom logic there)
245247
throwIfOpcodeDoesNotMatch: int = 63,
246248
}
247249

@@ -327,6 +329,26 @@ fun T.getDeclaredPackPrefix(): int
327329
fun T.getDeclaredPackPrefixLen(): int
328330
builtin;
329331

332+
/// Forces an object created by `lazy` to load fully. Returns the remaining slice (having read all fields).
333+
/// Since `options.assertEndAfterReading` is ignored by `lazy` (fields are loaded on demand),
334+
/// this method can help you overcome this, if you really need to check input consistency.
335+
/// Example:
336+
/// ```
337+
/// val msg = lazy CounterMessage.fromSlice(s);
338+
/// match (msg) { // it's a lazy match, without creating a union on the stack
339+
/// CounterIncrement => {
340+
/// ...
341+
/// newCounter = curCounter + msg.incBy; // `incBy` loaded here, on demand
342+
/// msg.forceLoadLazyObject().assertEnd() // the purpose: get remainder
343+
/// }
344+
/// }
345+
/// ```
346+
/// Note: while [slice.assertEnd] may seem reasonable, these checks are avoided in practice,
347+
/// because the purpose of `lazy` is to auto-detect and load only necessary fields, not up to the end.
348+
@pure
349+
fun T.forceLoadLazyObject(self): slice
350+
builtin;
351+
330352
/// Cell<T> represents a typed cell reference (as opposed to untyped `cell`).
331353
/// Example:
332354
/// ```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct Point {
2+
x: int8;
3+
y: int8;
4+
}
5+
6+
fun main() {
7+
var p = lazy Point.fromSlice(stringHexToSlice("0A14"));
8+
p.asdf;
9+
}
10+
11+
/**
12+
@compilation_should_fail
13+
@stderr field `asdf` doesn't exist in type `Point`
14+
*/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
struct A { a1: int7; a2: int8; a3: int8; }
2+
struct B { b1: int7; b2: int8; b3: int8; }
3+
type AOrB = A | B;
4+
5+
fun f() {
6+
val o = lazy AOrB.fromSlice("");
7+
__expect_lazy("");
8+
return (o is A) ? o.a1 : o.b1;
9+
}
10+
11+
/**
12+
@compilation_should_fail
13+
@stderr `lazy` will not work here, because variable `o` it's used in a non-lazy manner
14+
@stderr hint: lazy union may be used only in `match` statement
15+
*/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
type Int32Or64 = int32 | int64;
2+
3+
fun f() {
4+
val o = lazy Int32Or64.fromSlice("");
5+
match (o) {
6+
int32 => return o + 1,
7+
int64 => throw o,
8+
}
9+
}
10+
11+
/**
12+
@compilation_should_fail
13+
@stderr `lazy` union should contain only structures, but it contains `int32`
14+
*/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct A{}
2+
type StructAndNot = A | int8;
3+
4+
fun f() {
5+
var o = lazy StructAndNot.fromSlice(""); // neg5
6+
__expect_lazy("");
7+
match (o) {
8+
A => {}
9+
int8 => {}
10+
}
11+
}
12+
13+
/**
14+
@compilation_should_fail
15+
@stderr `lazy` union should contain only structures, but it contains `int8`
16+
*/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
type PairInt8 = (int8, int8);
2+
3+
fun f() {
4+
var i = lazy PairInt8.fromSlice(stringHexToSlice("0102"));
5+
return (i.0, i.1);
6+
}
7+
8+
/**
9+
@compilation_should_fail
10+
@stderr `lazy` is applicable to structs, not to `PairInt8`
11+
*/
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
struct Counter7Increment { byValue: int7 }
2+
struct Counter7Decrement { byValue: int7 }
3+
type MsgEitherCounter = Counter7Increment | Counter7Decrement;
4+
5+
fun f(c: cell) {
6+
// because 2 matches
7+
var msg = lazy MsgEitherCounter.fromCell(c); // neg8
8+
var t = createEmptyTuple();
9+
match (msg) {
10+
Counter7Increment => {}
11+
Counter7Decrement => {}
12+
}
13+
match (msg) {
14+
Counter7Increment => {}
15+
Counter7Decrement => {}
16+
}
17+
}
18+
19+
/**
20+
@compilation_should_fail
21+
@stderr `lazy` will not work here, because variable `msg` it's used in a non-lazy manner
22+
*/
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct Counter7Increment { byValue: int7 }
2+
struct Counter7Decrement { byValue: int7 }
3+
type MsgEitherCounter = Counter7Increment | Counter7Decrement;
4+
5+
fun f() {
6+
// because .toCell()
7+
var msg = lazy MsgEitherCounter.fromSlice(""); // neg9
8+
match (msg) {
9+
Counter7Increment => {}
10+
Counter7Decrement => {}
11+
}
12+
return msg.toCell();
13+
}
14+
15+
/**
16+
@compilation_should_fail
17+
@stderr `lazy` will not work here, because variable `msg` it's used in a non-lazy manner
18+
@stderr lazy MsgEitherCounter
19+
*/
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
struct Counter7Increment { byValue: int7 }
2+
struct Counter7Decrement { byValue: int7 }
3+
type MsgEitherCounter = Counter7Increment | Counter7Decrement;
4+
5+
fun f() {
6+
// because in a complex expression
7+
var msg = lazy MsgEitherCounter.fromSlice("");
8+
match (msg) {
9+
Counter7Increment => 1 as int32,
10+
Counter7Decrement => 2 as int32,
11+
}.toCell()
12+
}
13+
14+
/**
15+
@compilation_should_fail
16+
@stderr `lazy` will not work here, because variable `msg` it's used in a non-lazy manner
17+
*/
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
struct Counter7Increment { byValue: int7 }
2+
struct Counter7Decrement { byValue: int7 }
3+
type MsgEitherCounter = Counter7Increment | Counter7Decrement;
4+
5+
fun f() {
6+
var msg = lazy MsgEitherCounter.fromSlice(""); // neg10
7+
match (msg) {
8+
Counter7Increment => 1 as int32,
9+
Counter7Decrement => 2 as int32,
10+
else => throw 123, // not allowed in either
11+
}
12+
}
13+
14+
/**
15+
@compilation_should_fail
16+
@stderr `else` is unreachable, because this `match` has only two options (0/1 prefixes)
17+
*/

0 commit comments

Comments
 (0)