Known limitations¶
A transpiler's contract is: produce correct output, or fail loudly. Silently emitting plausible-but-wrong code is the one unacceptable outcome. The cases below are where ktox does not yet meet that contract. They are grouped by how they surface today.
Legend:
- 🔴 Silent miscompilation — ktox emits output that transpiles and passes the output check
(
checkLuaOutputload /checkJsOutputnode --check/checkCppOutput-fsyntax-only) but is wrong at runtime. These are the priority gaps: a user gets no signal. - 🟠 Invalid output — ktox emits target code that does not load/compile. The output check catches it (a clear error), but the transpiler itself does not reject the input up front.
- 🟢 Rejected — ktox throws
KtoxUnsupportedExceptionwith a clickable location. Working as intended; listed so the boundary is explicit.
🔴 Silent miscompilation¶
vararg constructor parameter — trailing elements dropped¶
A constructor emits its signature with the vararg listed by name (function VP:new(name, items)),
not as ..., so the call VP:new("a", 1, 2, 3) binds items = 1 and discards 2, 3 — and items
is a scalar, not the expected array/table. The output loads cleanly, so no check fires.
- Backends: Lua (verified); JS/C++ constructors share the shape.
- Workaround: in a constructor take
items: List<Int>(orIntArray) instead ofvararg. - Note: a function vararg (
fun f(vararg xs: Int)) called positionally is handled correctly; only the constructor path and the vararg-plus-named case below are affected.
Non-literal parameter default — emitted verbatim (JS, C++)¶
JS and C++ carry a parameter default into the emitted signature as the raw Kotlin default text:
function f(a, xs = listOf(1, 2)). listOf is not a JS/C++ symbol, so the default throws
ReferenceError (JS) / fails to compile (C++). In JS the emitted default is syntactically valid, so
node --check passes — the failure only appears at runtime when the default is actually used.
- Backends: JS and C++. Lua is correct here — it transpiles the default expression
(
if xs == nil then xs = {1, 2} end), not raw text. - Workaround: give the parameter a default that is a literal in the target language, or always pass the argument explicitly.
vararg followed by another parameter (named-only) — argument misplaced¶
A parameter after a vararg can only be supplied by name. Argument reordering writes the positional
vararg element into the trailing parameter's slot and then overwrites it with the named argument, so a
vararg element is silently lost (f(1, "n") — the 2 is gone).
- Backends: core argument reordering (all). In Lua the emitted
function f(..., name)is additionally invalid (...must be last) and so failscheckLuaOutput; JS/C++ surface only the silent call-site misplacement. - Workaround: do not declare parameters after a
vararg.
Named arguments on a non-Kotlin classpath class — collapse positionally¶
// SomeJavaType comes from a plain Java jar compiled without -parameters
SomeJavaType(id = 1, label = "x")
Parameter names and default-presence for a jar-consumed class come from Kotlin @Metadata. A plain
Java class has none, so java reflection yields arg0, arg1, …; a named argument matches nothing and is
appended positionally. (Kotlin jar classes — the normal ktox dependency — are handled correctly.)
- Backends: core resolution (all).
- Workaround: pass arguments positionally, or compile the Java dependency with
-parameters.
🟠 Invalid output (caught by the output check, not by the transpiler)¶
C++ parameter default that references another parameter¶
Emits int32_t mk(int32_t a, int32_t b = a * 2). A C++ default argument may not reference another
parameter, so the signature is ill-formed and fails checkCppOutput / the user's compiler. This is
emitted even when the default is used normally (mk(3)). (Skipping b at a call site is instead
rejected up front — see below.)
- Backend: C++.
- Workaround: make the default a self-contained constant, or always pass the argument.
C++ classpath default typed as a class with no default constructor¶
A skipped default for a classpath callee value-initializes with {} (its true default is not in
source). {} is well-formed for scalars, std::string, std::optional, aggregates, and classes with
a default constructor — but ill-formed for a class type that has only a user-declared non-default
constructor, so it fails at C++ compile.
- Backend: C++.
- Workaround: pass that argument explicitly.
🟢 Rejected up front (KtoxUnsupportedException)¶
These inputs cannot be lowered correctly and are refused with a located error rather than miscompiled.
- Lua constructor default / super-call argument that references an instance member — a
member-function call or a non-parameter
@get:NativeNamegetter.selfdoes not exist yet at that point, soself:Foo()would run on the class table. (A sibling constructor parameter, including a@get:NativeNameone, is fine — it renders as the bare parameter.) - C++ call site that skips a parameter whose default references another parameter — the default cannot be reproduced in the caller's scope, so the argument must be passed explicitly.