variable annotations
c.f. https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Common-Variable-Attributes.html
## local list = {}; for k, _ in pairs(typedefs.variable_annots) do table.insert(list, k) end table.sort(list) for _, v in ipairs(list) do print(v) end- aligned
- atomic
- cattribute
- cexport
- cimport
- cinclude
- close
- codename
- comptime
- const
- cpostqualifier
- cqualifier
- ctopinit
- deprecated
- nodce
- nodecl
- nogcscan
- noinit
- register
- restrict
- static
- threadlocal
- volatile
function annotations
## local list = {}; for k, _ in pairs(typedefs.function_annots) do table.insert(list, k) end table.sort(list) for _, v in ipairs(list) do print(v) end- alwayspoly
- cattribute
- cexport
- cimport
- cinclude
- codename
- cpostqualifier
- cqualifier
- deprecated
- entrypoint
- forwarddecl
- inline
- nodce
- nodecl
- noinline
- noreturn
- nosideeffect
- polymorphic
- volatile
type annotations
## local list = {}; for k, _ in pairs(typedefs.type_annots) do table.insert(list, k) end table.sort(list) for _, v in ipairs(list) do print(v) end- aligned
- cimport
- cinclude
- cincomplete
- codename
- ctypedef
- forwarddecl
- nickname
- nocopy
- nodecl
- packed
- using
aligned(integer)
Misaligned memory accesses can result in runtime faults (ARM) or severe performance costs (intel). See Raymond Chen
local T <aligned(1)> = @record{b: [3]byte}
local a: [3]T
## static_assert(a.type.size == 3 * 3)
local function f()
local a: byte <aligned(32)>
local b: byte <aligned(32)>
return (@isize)(&a) - (@isize)(&b)
end
local function g()
local a: byte <aligned(2)>
local b: byte <aligned(2)>
return (@isize)(&a) - (@isize)(&b)
end
print(f(), g()) -- 32, 2
alwayspoly
"Force a function to always be polymorphic (evaluate a new function for each call)."
local function f() <alwayspoly> return 1 end f() f() f()
Generated C:
int64_t example_f_1(void) {
return 1;
}
int64_t example_f_2(void) {
return 1;
}
int64_t example_f_3(void) {
return 1;
}
int nelua_main(int argc, char** argv) {
example_f_1();
example_f_2();
example_f_3();
return 0;
}atomic
Compiles to _Atomic if possible, or to volatile.
cattribute(string)
Compiles to __attribute__((...))
cexport(string:is_optional())
"If no name is supplied then the compiler will automatically generate a symbol name based on the file and variable name."
$ nelua -o example -i 'local x: cint <cexport "var_x">' $ nm example|grep var_x 0000000000004014 B var_x
cimport(string:is_optional())
"if no name is supplied then the same variable name is used in C"
$ nelua -i 'local function puts(s: cstring): cint <cimport> end puts("test")'
test
cinclude(string)
$ cat error.h
#error "an error"
$ nelua -i 'local function f() <cinclude "error.h"> end print(1)'
1
$ nelua -i 'local function f() <cinclude "error.h"> end print(1) f()'
./error.h:1:2: error: #error "an error"
1 | #error "an error"
| ^~~~~
cincomplete
If set, the type size and alignment are considered to be unknown. Useful for partially documented C structures, like struct termios
close
Lua to-be-closed variables, for resource management. The type's __close() method is called on scope termination.
codename(string)
"Custom name used for the type when generating the C code." Contrast nickname
comptime
"Whether the variable should be only available and used at compile time."
It seems more like an "not runtime" assertion - it ensures that runtime computation doesn't sneak into a value. But it's also very appropriate for named values that should not occupy a variable - i.e., constants, what would be implemented with #define in C.
const
This prevents casual reassignment and direct field mutation, and it's very light restraint from Lua 5.4, "a variable that cannot be assigned to after its initialization." Consider that plenty of mutation is possible without reassignment:
require 'vector'
local s: vector(integer) <const> = {}
s:push(1)
s:push(2)
assert(#s == 2)
assert(s:pop() == 2)
assert(s:pop() == 1)
assert(#s == 0)
-- s.size = 1 -- only this line is forbiddenand
local coord = @record{x: number, y: number}
local origin: coord <const> = {0, 0}
-- origin.x = 1 -- not allowed
local indirect <const> = &origin
-- indirect.x = 1 -- not allowed
local point: coord = {4, 4}
local indirect <const> = &point
-- indirect.x = 1 -- not allowed
point.x = 1
local function jitter(p: *coord)
p.x = p.x + 1
end
jitter(indirect)
jitter(&origin)
assert(indirect.x == 2)
assert(origin.x == 1)cqualifier(string) and cpostqualifier(string)
These place additional C qualifiers before and after the C type specifiers, respectively.
ctopinit
"Force a variable to be initialized in the C top scope, even if it contains runtime expressions."
I can't tell if this is implemented. It may sound like it'd be useful for regular expressions, as in
local function regcomp(s: string)
print('(fake) compiling regex:', s)
return s
end
local function regex_use()
local re <ctopinit> = regcomp('hello')
end
for i=1, 10 do
regex_use()
end
but typedefs.lua continues: "This is only useful when making some low level OS specific code."
And, that's not really initialization of re, but assignment?
Anyway, static lets you do what was attempted in this code.
ctypedef(string:is_optional())
If set, emits a typedef for an imported C struct. Never necessary except when you care about the readability of the generated C?
deprecated
If the associated symbol is used, Lua emits a warning about. issue/232 has a bug and a workaround, concerning this warning not always getting emitted.
entrypoint
"Whether to use the function as the entry point of the application (the C main), the entry point is called before evaluating any file and is responsible for calling nelua_main."
Example in the toplevel.
forwarddecl
Example in nelua caveats.
inline
Nelua emits the function with the NELUA_INLINE macro, which it tries to define to a compiler-supported inline attribute.
nickname(string)
Type names such as span(integer) come from this attribute. Contrast codename
nocopy
"Whether the type can be copied, that is, passed by value (experimental)."
require 'string'
local point <nocopy> = @record{x: number, y: number}
local p: point = {1, 2}
local function bad(p: point)
print(string.format('{%.1f,%.1f}', p.x, p.y))
end
-- bad(p) -- cannot pass non copyable type 'point' by value
function point:clone(): point return {self.x, self.y} end
bad(p:clone())nodce
Prevents Dead Code Elimination
nodecl
Prevents Nelua from emitting a declaration, for example when an included C header already has a declaration.
nogcscan
"Whether the GC should not scan the variable for registers even if it contains pointers."
noinit
Skips zero-initialization of a variable.
noinline
The opposite of inline.
noreturn
function os.exit(code: overload(integer,boolean,niltype)): void <noreturn> ... end
nosideeffect
Seems like a hint to permit lax reordering of functions for optimization, rather than a check.
packed
Probably similar to aligned(1)?
polymorphic
"Force a function to be polymorphic so it can be declared on demand." ?
register(string:is_optional())
Try to place the variable in a (optionally specific) CPU register.
restrict
Uses https://en.wikipedia.org/wiki/Restrict
local function f(a: cstring <restrict>, b: cstring <restrict>) assert(a ~= b) end local s: cstring = 'test' f(s, s)
Compile with -S/--sanitize for C to notice that f() is passed the same pointer twice (i.e., a and b are aliases):
warning: passing argument 1 to ‘restrict’-qualified parameter aliases with argument 2 [-Wrestrict]
static
"Static variables are stored in the application static storage, not in the function stack frame."
This permits what I thought ctopinit might permit:
local function regcomp(s: string)
print('(fake) compiling regex:', s)
return s
end
local function regex_use()
local re <static> = ''
if re == '' then re = regcomp('hello') end
end
for i=1, 10 do
regex_use()
endThe regex is only compiled once, and re in regex_use() is only equal to the empty string on the first call.
threadlocal
"Whether to declare variable per thread (requires C11 or C compiler support)."
using
"Whether to use enum fields in the declared scope."
local E1 = @enum{Up = 0, Down, Left, Right}
local E2 <using> = @enum{North = 0, South, West, East}
local d1 = E1.Up
-- local d2 = Up -- error: undeclared symbol 'Up'
local d3 = East
local E3 <using> = E1
local d4 = Upvolatile
As in C, https://en.wikipedia.org/wiki/Volatile_(computer_programming)