Comments (5)
Oh, that's brilliant! Thank you so much!
Also, speaking of docs, thanks for having generally so robust documentation. It's been essential during my journey learning Nelua. (I like the language a lot, too, obviously.)
from nelua-lang.
You example is valid, priv_recs
is not in the stack memory, but in static memory (top scope variables are always in static memory). So in your example using the values returned from get_recs
will always be valid.
But this new example wouldn't be valid:
require 'span'
global Rec = @record{a: int32}
global function get_recs(x: integer): span(Rec)
local priv_recs: []Rec = {{x}, {x}}
return priv_recs
end
print(get_recs(1)[0].a)
And indeed the above prints junk data when compiled with clang
, because after returning priv_recs
the address inside span isn't valid anymore. To catch such mistakes I usually warn users to run with --sanitize
, sadly even it could not catch such mistake. But well, the programmer should know that function is returning an address, and is responsible for checking the liveness of what he returns, but indeed would be handy if we could get a compile error in this case.
The motivation for having span.__convert
accepting array by value and getting is reference instead is this example:
require 'span'
global Rec = @record{a: int32}
global function use_recs(x: span(Rec))
print(x[0].a)
end
-- without syntax sugar (what span.__convert bellow is really doing does behind the scenes)
use_recs(&(@[]Rec){{a=1}, {a=2}})
-- syntax sugar relying in span.__convert
use_recs{{a=1}, {a=2}}
And that is a valid and useful feature to pass list of values, disallowing spanT.__convert
to accept array by values would mean dropping that feature, and I use that feature in my personal projects a lot already as it makes code more readable, without this feature the syntax is much more boilerplate.
Note: You could argue that using &
on a temporary constructed argument is invalid, but it's not for aggregate types, even in standard C, the compiler guarantees to keep its address valid until the call ends.
A raw array (not ptr to array) could be detected in spanT.__convert and the array could be automatically copied to the heap. This wouldn't cause crashes, but it would still be somewhat unexpected.
This workaround would be unexpected, implicit conversions to spans should never allocate memory (a semantic of the language), so this is not ideal.
Possible enhacement
The liveness of priv_recs
could be tracked by the compiler to then disallow the implicit conversion to a span in the return
, but this would just work just for misuse of return
, span
and array values, there are many other possible ways to bypass such checks and return addresses of variables in the stack, so I am not sure if having a check for that very specific case while having in mind there will be still holes is that motivating. Maybe if it becomes a common mistake it should.
from nelua-lang.
After some thought and experiments I've added a compile check for this specific misuse in a7b5173 , your example triggers now this compile error:
play/draft3.nelua:8:10: error: in call of function 'span(Rec).__convert' at argument 1: cannot perform implicit conversion from 'array(Rec, 2)' to 'span(Rec)' while inside a return statement
return priv_recs
Rationale: I have noticed nowhere using implicit conversions to spans in return statements, and is usually dangerous (this issue is an example), So I have disallowed implicit conversions to spans inside returns, that means the compiler is not really tracking the variable liveness, this still up to the user to be cautious and use --sanitize
to double check.
from nelua-lang.
Thanks for adding the check! It should help.
One thing I wanted to pick up on, to make sure I understand correctly:
You example is valid, priv_recs is not in the stack memory, but in static memory (top scope variables are always in static memory). So in your example using the values returned from get_recs will always be valid.
Please consider this example, where a static variable is picked directly:
require 'span'
global Rec = @record{
a: int32
}
local spanRec: type = @span(Rec)
local priv_recs: []Rec = {{1}, {2}}
global function get_recs(): span(Rec)
return spanRec.__convert(priv_recs)
end
print(&priv_recs[0])
print(get_recs().data)
local b: int32
print('A random static address for comparison: ', &b)
global function showstackaddr()
local z = 0
print('A random stack address for comparison: ', &z)
end
showstackaddr()
Output:
[leaf@alicorn tael]$ nelua nelua_span_stack.nelua
0x55f5a1538050
0x7fff2c585010
A random static address for comparison: 0x55f5a1538064
A random stack address for comparison: 0x7fff2c585020
i.e. Static arrays are copied to the stack during conversion.
I tried to make an example that would die with automatic scope variables e.g. use_recs{{1}, {2}}
, but without success. I could only cause a crash by passing e.g. use_recs((@[67108864]Rec){}) but that doesn't demonstrate anything, really.
from nelua-lang.
Please consider this example, where a static variable is picked directly
Thanks for pointing that out, and you were correct. I was assuming span.__convert
was taking the array value by reference implicitly (as it does when you use use_recs{}
syntax sugar), but reading the code it was actually doing an extra temporary copy, and I forgot to change that after I've implemented the mentioned syntax sugar (which recently added feature, even not in docs). Doing an extra copy is always a wrong thing to do on implicit conversions for spans, so I have changed conversion semantics to what I actually assumed in mind, so please reread my previous response only after commit 9840cc8 .
After that commit, this prints what you expected:
require 'span'
global Rec = @record{
a: int32
}
local spanRec: type = @span(Rec)
local priv_recs: []Rec = {{1}, {2}}
global function get_recs(): span(Rec)
local spn = spanRec.__convert(priv_recs)
return spn
end
print(&priv_recs[0])
print(get_recs().data)
assert(&priv_recs[0] == get_recs().data)
Output:
0x55d5a340f050
0x55d5a340f050
from nelua-lang.
Related Issues (20)
- (@span(byte)){...} ordered field cast permits invalid, rejects valid construction HOT 1
- Executables reflect compile-time isatty conditions HOT 3
- <comptime> record parameters can't be provided; fields can't be used in runtime code HOT 1
- Copyright timespan needs update HOT 3
- Invalid unitname generated for directories that look like numeric literals HOT 1
- No line number in error if a hashmap index has a type error
- Range check error messages point to library code (point of check) rather than user code (point of error) HOT 1
- Function variables can collide with C identifiers HOT 1
- Confusing errors with varargs
- Permit function returns in generic instantiation, not just types
- Broken preprocessor after 9faaa66e HOT 3
- Comparison of a pointer with nil generates unexpected C code HOT 2
- we need nelua debugger(vscode) or tell us how to debug my app? HOT 2
- Compatible with Lua C plug-in HOT 2
- LFS: can't change directory with `lfs.chdir` HOT 1
- error while compiling when a source file starts with a number HOT 1
- Compiler hangs when cgenerator.lua errors
- Can't compile fibonacci example
- Can't compile fibonacci example
- Support actual closures via struct
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nelua-lang.