Most of the standard library is working at compile-time and there are checks to ensure that.
Unfortunately from https://github.com/nim-lang/Nim/pull/14417#issuecomment-632125417, lent is not supported in the VM.
For example the JSON test will fail with
Error: limited VM support for 'addr'
This would prevent using lent to optimize arrays, sequences, options, "result" types, object variants. All of which have a typical no-copy use-case.
The easy fallback would be to make lent a no-op in the VM.
Please provide a self contained test so I can have a look
You can revert this commit: https://github.com/nim-lang/Nim/pull/14417/commits/0964462d8d6849723c8facb433f2b17eed311759
i.e. in the option module change
proc get*[T](self: Option[T]): T {.inline.} =
to
proc get*[T](self: Option[T]): lent T {.inline.} =
And the test suite will crash in many places like Json.
still, a minimal example would've been nice;
which of these since you've already ran this?
lib/pure/json.nim
lib/pure/parsejson.nim
tests/stdlib/mjsonexternproc.nim
tests/stdlib/tjson_unmarshall.nim
tests/stdlib/tjsonexternproc.nim
tests/stdlib/tjsonmacro.nim
tests/stdlib/tjsonmacro_reject.nim
tests/stdlib/tjsontestsuite.nim
I'll cook up one this evening it was tjsonmacro.nim
Here is an example
type MyOption[T] = object
case has: bool
of true:
value: T
of false:
discard
func some[T](val: T): MyOption[T] =
result = MyOption[T](has: true, value: val)
func get[T](opt: MyOption[T]): lent T =
assert opt.has
opt.value
const x = some(10)
echo x.get()
static:
echo x.get()
As a potential impact, I tried to use options in my RayTracer, but they were 5x slower (not just a couple dozen percents like the iterator issue in #14421) than passing mutable parameter + bool
Well nobody claimed Option[T] is a lightweight abstraction.
Well nobody claimed
Option[T]is a lightweight abstraction.
I was misled by Rust code :/
Issue turned out completely different. Can use stmtListExpr to assign lent T/var T or you get addr of temporary variable. Just changed get to use stmtListExpr to assign to result
indeed: reduced case:
when true: # D20200525T015650
type Foo = object
x: float
proc fn(a: var Foo): var float =
## discard <- turn this into a comment (or a `discard`) and error disappears
# result = a.x # this would work
a.x # Error: limited VM support for 'addr'
template main =
var a = Foo()
discard fn(a)
main() # works
static: main() # fails
So using a return statement or result work.
type MyOption[T] = object
case has: bool
of true:
value: T
of false:
discard
func some[T](val: T): MyOption[T] =
result = MyOption[T](has: true, value: val)
func get[T](opt: MyOption[T]): lent T =
assert opt.has
return opt.value
const x = some(10)
echo x.get()
static:
echo x.get()
Can we change the implicit return _expression_ (not sure how to call that) to be the same as implicit result or explicit return statement?
At the very least the error message should be changed to "Maybe try an explicit return or the implicit result variable".
Furthermore this is a pattern that we favor in nim-beacon-chain. We are already fighting stack issues and genericResets and if return expression introduced non-optimized away temporaries, they might contribute to that.
At the very least the error message should be changed to "Maybe try an explicit return or the implicit result variable".
that's fine as an improvement, but obviously this issue should be kept open until this bug is fixed
Maybe try an explicit return or the implicit result variable
er, why should that be a recommendation? ie they're supposed to be equivalent, so it seems counterproductive to implement a feature that suggests a workaround instead of focusing on the actual bug?
we should reopen this issue (refs https://github.com/nim-lang/Nim/pull/14442#issuecomment-633576965)
Maybe try an explicit return or the implicit result variable
er, why should that be a recommendation? ie they're supposed to be equivalent, so it seems counterproductive to implement a feature that suggests a workaround instead of focusing on the actual bug?
If it's possible to fix this in a timely manner sure, if not we need to tell users how to workaround that.
IMO, this issue is not VM specific. All backends introduce a temporary variable in this case. IMO, solution is to introduce lowering in transf phase:
Transform:
x = block:
stmt1
stmt2
expr
Into
```nim
block:
stmt1
stmt2
x = expr
````
Similar lowering for if, block, case, stmtList expressions.
injectdestuctors already does this lowering. It needs to be moved from injectdestructors to transf.
solution is to introduce lowering in transf phase:
@cooldome do you think this could fix https://github.com/nim-lang/Nim/issues/9230 as well?
proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
if not (body.kind == nkForStmt and body[^2].kind in nkCallKinds):
IMO, this issue is not VM specific
indeed, just got hit by this in https://github.com/nim-lang/Nim/pull/14875
nim c -r main # ok
nim js -r main # bug
proc byLent2[T](a: T): lent T =
runnableExamples: discard
a
proc byLent3[T](a: T): lent T =
runnableExamples: discard
result = a
block:
var a = 10
# let x = byLent3(a) # works
let x = byLent2(a) # Error: internal error: genAddr: nkStmtListExpr
Most helpful comment
IMO, this issue is not VM specific. All backends introduce a temporary variable in this case. IMO, solution is to introduce lowering in
transfphase:Transform:
Into
```nim
block:
stmt1
stmt2
x = expr
````
Similar lowering for if, block, case, stmtList expressions.
injectdestuctors already does this lowering. It needs to be moved from injectdestructors to transf.