@@ -233,15 +233,17 @@ struct PackOptions {
233233
234234/// UnpackOptions allows you to control behavior of `MyStruct.fromCell(c)` and similar functions.
235235struct 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
327329fun 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/// ```
0 commit comments