Skip to main content

Pointers and Unsupported Types

Normative: Marshal rules for unmanaged pointers, function pointers, and CLR forms that are unsupported or restricted by default in v1. Related: Default matrix → 01-OVERVIEW.md; IntPtr integer rules → 01-OVERVIEW.md; OpaqueValue contrast → 04-OPAQUE.md; Delegate contrast → 09-FUNCTION.md; ref struct05-STRUCT.md, ../05-LIB.md.

Platform principle: Mono and Il2Cpp share the same Lua-visible semantics.

1. Distinction from IntPtr / UIntPtr

In the overview, IntPtr / UIntPtr / nint / nuint use integer numeric Marshal (ToInt64 / new IntPtr), not Pointer in this section.

TypeLua default shapeScript can treat as integer math
IntPtr / UIntPtr / nint / nuintinteger / numberYes (by numeric value)
T* / void* unmanaged pointersPointer (lightuserdata)No (pass-through only)
Function pointer delegate*<…>Pointer (lightuserdata)No (pass-through only)

2. Unmanaged pointers (T*, void*, etc.)

Scope: CLR types with Type.IsPointer == true whose element is an unmanaged type, e.g. int*, byte*, void*, MyStruct* (MyStruct is an unmanaged struct).

Does not include IntPtr / UIntPtr (see §1).

2.1 Default Marshal

DirectionDefault shapeNotes
C# → LuaPointer (lightuserdata)Push pointer address value (uintptr_t / platform pointer width); no metatable
Lua → C#Pointer (lightuserdata)Pop must be Pointer shape; restore declared pointer type

2.2 Lua-side capabilities (intentionally limited)

AllowedForbidden
Pass as-is as an argument to the next C# call (pass-through within a sync chain)Dereference, read/write pointed-to memory
Distinguish from nil (non-null pointers are Pointer): / . member access, arithmetic, #, pairs, etc.
Store in globals / tables / upvalues then use async or across pcall (address may be invalid)

Rationale: Lua cannot safely express C# unmanaged pointer lifetime and aliasing; only opaque token pass-through is supported for native / low-level API glue.

2.3 [LuaMarshalAs]

Unmanaged pointers allow Default and OpaqueValue (C#→Lua only). UserData, Table, etc. remain illegal (see 02-MARSHAL-AS.md. OpaqueValue differs from default Pointer lightuserdata: it uses the Opaque slot and get_opaquevalue / set_opaquevalue lifetime rules.

3. Function pointers

Scope: CLR types with Type.IsFunctionPointer == true, e.g. C# 9+ delegate*<int, int>, delegate*<void>.

3.1 Default Marshal

DirectionDefault shapeNotes
C# → LuaPointer (lightuserdata)Push function entry address; no metatable
Lua → C#Pointer (lightuserdata)Pop restores the corresponding function pointer type

3.2 Lua-side capabilities

Same as §2 — pass-through only; Lua cannot call that address.

3.3 [LuaMarshalAs]

Same as §2.3: allow Default and OpaqueValue (C#→Lua only).

3.4 Contrast with Delegate

TypeLua default shapeCallable from Lua
Action / Func<…> etc. DelegateDelegateUserData or Lua functionYes (see 09-FUNCTION.md)
delegate*<…> function pointerPointer (lightuserdata)No

4. System.TypedReference

TypedReference is passed C# ↔ Lua only as OpaqueValue; default is already OpaqueValue, so [LuaMarshalAs(OpaqueValue)] is unnecessary. Other marshal shapes (UserData / Table / integer, etc.) are unsupported.

DirectionRules
C# → LuaDefault Push OpaqueValue (04-OPAQUE.md; scripts read/write via get_opaquevalue / set_opaquevalue
Lua → C#Only accept compatible OpaqueValue handles (bind address after type check); other Lua shapes → error
Other [LuaMarshalAs]Illegal (fall back or reject at bind; see 02-MARSHAL-AS.md)

Reason: TypedReference binds a typed slot on the managed stack and cannot map stably to ordinary Lua values or userdata; OpaqueValue only exposes the slot address for the current call, matching its semantics.

5. Other unsupported or restricted types

These appear briefly in 01-OVERVIEW.md; collected here.

5.1 decimal

DirectionRules
DefaultNot supported
[OpaqueValue] (C#→Lua)Legal
Pop/Push (Default)Not in v1 default path

5.2 ref struct (Span<T>, ReadOnlySpan<T>, etc.)

DirectionRules
by-val parameterCannot use ordinary default marshal
Controlled pathsOnly ref StructUserData / 04-OPAQUE.md OpaqueValue, etc.

Details: 05-STRUCT.md, ../05-LIB.md.

5.3 Nullable<T>

DirectionRules
Has valueSame marshal as T
nullnil
T is value typePop accepts nil

See 01-OVERVIEW.md, ../02-TYPE-SYSTEM.md §Nullable.

5.4 dynamic

Treated as object at compile time; no separate Lua shape.

5.5 Open generic parameters

E.g. void M<T>(T x) with T not instantiated: marshal decided by call-site type arguments; see ../02-TYPE-SYSTEM.md.

6. Signatures to reject at register / expose time

These are illegal signatures (not fine-grained marshal rules):

ConditionBehavior
ref struct by-val parameter combinationsReject
Unresolvable byref modifier combinationsReject

Allowed: ref/out/in on GetFunction-obtained delegate calls and delegate bridges (C#→Lua: 04-OPAQUE.md; Lua→C#: 03-BYREF.md.

7. Pointer Pop details

ItemRules
Accepted shapeOnly Pointer lightuserdata
Does not acceptImplicit conversion from integer / number, full userdata, or OpaqueValue handle
null pointerC#→Lua: Push Pointer or nil per implementation (must match across platforms); Lua→C#: whether nil maps to null pointer is implementation-documented
ErrorsType mismatch → luaL_error / equivalent exception

8. Three lightuserdata kinds compared

KindUsemetatableScript R/W
Pointer (§2, §3)Unmanaged / function pointer pass-throughNoneCannot dereference
OpaqueValue (04-OPAQUE.md)C# stack-frame parameter-slot handleNoneVia get_opaquevalue / set_opaquevalue
(not lightuserdata) ClassUserData etc.Managed objectsHas IMT: / . member access

9. Mono / Il2Cpp consistency

ItemRequirement
Pointer / function pointer Pushlightuserdata; address width = platform pointer
Pointer PopOnly Pointer; no implicit convert with integer / full userdata
TypedReferenceOnly OpaqueValue (default); semantics match
decimal / ref struct by-valConsistent unsupported or restricted behavior
Error messagesSame or equivalent
DocContents
01-OVERVIEW.mdDefault matrix, IntPtr
02-MARSHAL-AS.mdLegal annotation set for pointer types
04-OPAQUE.mdOpaqueValue vs Pointer
09-FUNCTION.mdDelegate vs function pointer
05-STRUCT.mdref struct
../02-TYPE-SYSTEM.mdNullable, special type families