Lua Notes

nelua annotations
Login

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

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

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(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 forbidden

and

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()
end

The 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 = Up

volatile

As in C, https://en.wikipedia.org/wiki/Volatile_(computer_programming)