Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/how/cmake_vulkan/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ target_link_directories(
${GCLIBPATH}
)

set (LIBS "gcmt-lib")
set (LIBS "gc")

target_link_libraries(
${PROJECT_NAME}
Expand Down
2 changes: 1 addition & 1 deletion docs/how/cmake_vulkan/config_debug.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pushd
mkdir "__build/debug"
cd "__build/debug"
cmake ../.. -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Debug -Wno-dev
cmake ../.. -G "Visual Studio 18 2026" -A x64 -DCMAKE_BUILD_TYPE=Debug -Wno-dev
cmake --build . --config Debug -j 1
popd
2 changes: 1 addition & 1 deletion docs/how/cmake_vulkan/config_release.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pushd
mkdir "__build/release"
cd "__build/release"
cmake ../.. -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release -Wno-dev
cmake ../.. -G "Visual Studio 18 2026" -A x64 -DCMAKE_BUILD_TYPE=Release -Wno-dev
cmake --build . --config Release -j 1
popd
2 changes: 1 addition & 1 deletion docs/how/cmake_vulkan/typescript.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set (ROOT_PATH "I:\\tslang\\57")
set (ROOT_PATH "I:\\tslang")
set (_3RD_PARTY_PATH "${ROOT_PATH}")
set (BUILD_PATH "${ROOT_PATH}")
set (TSLANGPATH "${BUILD_PATH}")
Expand Down
57 changes: 57 additions & 0 deletions tslang/docs/interface-vtable-simplification-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,3 +473,60 @@ are size-changing coercions (e.g. si32 -> f64 number) still shifts offsets
relative to already-compiled method bodies expecting the original layout;
that can only bite literals whose inferred field types differ in size from
the interface's, and is out of scope here. 722/722 suite (720 + 2 new).

### Newly found: multi-method cross-module vtable slot bug (2026-07-19)

Found while extending test coverage beyond this arc's fixes - every prior
test/fix in #256-#258 only ever exercised a **single-method** interface
cast cross-module (`Counter {count; inc()}`). Trying a genuinely
multi-method interface (`Accumulator {total; add(n); addTwice(n);
scaled(factor): number}`, canonical vtable order after
`assignCanonicalVirtualIndexes` = methods-first-in-declaration-order then
fields = `add`@0, `addTwice`@1, `scaled`@2, `total`@3) surfaced a clean,
reproducible pattern when casting a cross-module structurally-typed VALUE
to it and calling each method **in isolation** (bisected one at a time via
a temporary `test-runner.cpp` stdout-surfacing patch, same technique as
earlier bugs in this file - reverted before commit):

| method (canonical slot) | isolated result |
|---|---|
| `add(n)` (slot 0) | correct - mutates `total` as expected |
| `addTwice(n)` (slot 1) | WRONG VALUE, no crash - `total` ends up incorrect but the process completes and reports the mismatch cleanly |
| `scaled(factor)` (slot 2) | CRASH - silent, no assert/error text reaches output at all (raw access violation with buffered stdout lost, unlike the controlled assert failures elsewhere in this file) |

Slot 0 works, slot 1 is wrong-but-survives, slot 2 crashes outright -
consistent with SOMETHING going wrong specifically in how slots beyond 0
are constructed or addressed for a cross-module structurally-typed cast
(as opposed to the field-order bug from earlier in this file, which was a
uniform reversal affecting all slots equally and is already fixed). Not
yet root-caused - candidates worth checking first: whether
`getInterfaceCloneFields`/the vtable-patch loop in
`mlirGenCreateInterfaceVTableForObject` (MLIRGenInterfaces.cpp) iterates
methods needing patching in the right order relative to the CANONICAL
`virtualIndex` for interfaces with >1 method (an off-by-one or
wrong-iteration-source bug would explain "slot 0 fine, slot 1+ broken");
or whether the heap-cloned vtable's allocated SIZE is computed from a
stale/undersized type (a 2-slot allocation for a >2-slot vtable would also
match this exact crash-only-past-slot-N shape).

**Not fixed.** The regression test actually added for this session
(`export/import_object_literal_structural_typed_params.ts`) deliberately
stays within the single-method shape that's known to work, to avoid
committing a failing test; it extends coverage only along the
"zero-arg -> parameterized method" axis, not the "single-method ->
multi-method" axis. A genuinely multi-method cross-module test is blocked
on this bug and is the natural next thing to add once it's fixed.

Also worth noting for whoever investigates: a completely SEPARATE,
same-module-only finding surfaced while building the initial (broken)
version of this test - a value-returning method cannot `return
this.siblingMethod(...)` (using a sibling call's return value directly in
a `return` statement) within the same type-literal-annotated object
literal; calling the sibling as a bare statement (discarding its return
value) works fine. Confirmed same-module, unrelated to cross-module
casting at all - likely a self-referential type-inference ordering gap
(the caller's return type depends on resolving the callee's return type,
which depends on `this`, which is still being constructed). Not
investigated further; avoided in the committed tests
(`00object_annotated_method_params.ts` uses `setBase`/re-`scale`, not a
chained-return pattern).
6 changes: 6 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ add_test(NAME test-compile-00-interface-optional-cast-order COMMAND test-runner
add_test(NAME test-compile-00-interface-function-typed-field COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_function_typed_field.ts")
add_test(NAME test-compile-00-interface-captures COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_captures.ts")
add_test(NAME test-compile-00-object-annotated-method COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-compile-00-object-annotated-method-params COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-compile-00-interface-object-array COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_object_array.ts")
add_test(NAME test-compile-00-interface-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_generic.ts")
add_test(NAME test-compile-00-interface-new COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_new.ts")
add_test(NAME test-compile-00-interface-indexer COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_indexer.ts")
Expand Down Expand Up @@ -642,6 +644,8 @@ add_test(NAME test-jit-00-interface-optional-cast-order COMMAND test-runner -jit
add_test(NAME test-jit-00-interface-function-typed-field COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_function_typed_field.ts")
add_test(NAME test-jit-00-interface-captures COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_captures.ts")
add_test(NAME test-jit-00-object-annotated-method COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-jit-00-object-annotated-method-params COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-jit-00-interface-object-array COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_object_array.ts")
add_test(NAME test-jit-00-interface-generic COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_generic.ts")
add_test(NAME test-jit-00-interface-new COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_new.ts")
add_test(NAME test-jit-00-interface-indexer COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_indexer.ts")
Expand Down Expand Up @@ -850,6 +854,7 @@ add_test(NAME test-compile-shared-export-import-class-interface COMMAND test-run
add_test(NAME test-compile-shared-export-import-object-literal-with-class-types COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_with_class_types.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_with_class_types.ts")
add_test(NAME test-compile-shared-export-import-object-literal-with-interface COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_with_interface.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_with_interface.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-params COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_params.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_params.ts")
add_test(NAME test-compile-shared-export-import-vars COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars.ts")
add_test(NAME test-compile-shared-export-import-vars-2 COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars2.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars2.ts")
add_test(NAME test-compile-shared-export-import-enum COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_enum.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_enum.ts")
Expand All @@ -866,6 +871,7 @@ add_test(NAME test-jit-shared-export-import-class-interface COMMAND test-runner
add_test(NAME test-jit-shared-export-import-object-literal-with-class-types COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_with_class_types.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_with_class_types.ts")
add_test(NAME test-jit-shared-export-import-object-literal-with-interface COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_with_interface.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_with_interface.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-params COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_params.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_params.ts")
add_test(NAME test-jit-shared-export-import-vars COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars.ts")
add_test(NAME test-jit-shared-export-import-vars-2 COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars2.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars2.ts")
add_test(NAME test-jit-shared-export-import-enum COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_enum.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_enum.ts")
46 changes: 46 additions & 0 deletions tslang/test/tester/tests/00interface_object_array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Array-of-interface coverage: several DISTINCT object literals (each its own
// location-hashed storage type and its own lifted method, per
// docs/interface-vtable-simplification-design.md section 3) collected into a
// single Shape[]-typed array and dispatched through the SAME interface at a
// single call site in a loop. Exercises that each element's own vtable
// (constant, per-type) is independently correct - a bug here would typically
// show up as every element calling the FIRST element's method (a shared/
// aliased vtable) or a wrong `this` binding once mixed in a homogeneous
// array.

interface Shape {
area(): number;
}

function main() {
const square = {
side: 4.0,
area() { return this.side * this.side; },
};

const rectangle = {
width: 3.0,
height: 5.0,
area() { return this.width * this.height; },
};

const circleLike = {
radius: 2.0,
area() { return this.radius * this.radius * 3.0; },
};

let shapes: Shape[] = [<Shape>square, <Shape>rectangle, <Shape>circleLike];

let total = 0.0;
for (let i = 0; i < shapes.length; i++) {
total = total + shapes[i].area();
}

print(total);
assert(square.area() == 16.0);
assert(rectangle.area() == 15.0);
assert(circleLike.area() == 12.0);
assert(total == 43.0);

print("done.");
}
37 changes: 37 additions & 0 deletions tslang/test/tester/tests/00object_annotated_method_params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Extends 00object_annotated_method.ts's coverage: that test only exercised
// zero-argument methods (inc(): void, twice(): number). Here the type-literal
// method members take parameters and one method calls ANOTHER method on
// `this` (chained dispatch through the same implicit-this-param mechanism
// fixed for MethodSignature tuple members).

function main() {
let acc: { total: number; add(n: number): void; addTwice(n: number): void } = {
total: 0,
add(n: number) { this.total = this.total + n; },
addTwice(n: number) { this.add(n); this.add(n); },
};

acc.add(3);
assert(acc.total == 3);

acc.addTwice(4);
assert(acc.total == 11);
print(acc.total);

let calc: { base: number; scale(factor: number): number; setBase(value: number): void } = {
base: 5,
scale(factor: number) { return this.base * factor; },
setBase(value: number) { this.base = value; },
};

const scaled = calc.scale(3);
assert(scaled == 15);
print(scaled);

calc.setBase(10);
const rescaled = calc.scale(3);
assert(rescaled == 30);
print(rescaled);

print("done.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace A {

export interface Accumulator {
total: number;
add(n: number): void;
}

// structurally-typed (not interface-typed) export, like
// export_object_literal_structural_typed.ts, but extends that test's
// coverage: the method takes a PARAMETER (that test's inc() took none).
//
// NOTE: deliberately kept to ONE method. A multi-method version of this
// (total; add(n); addTwice(n); scaled(factor): number) was tried and
// found broken cross-module for any method beyond vtable slot 0 - see
// docs/interface-vtable-simplification-design.md's "multi-method
// cross-module vtable slot bug" section. That's a distinct, deeper,
// not-yet-fixed bug; this test intentionally stays within the
// currently-working single-method shape.
export var acc: { total: number; add(n: number): void } = {
total: 0.0,
add(n: number) { this.total = this.total + n; },
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import './export_object_literal_structural_typed_params'

// Casts the imported structurally-typed VALUE to a single-method interface in
// the importer, exercising a PARAMETERIZED (not zero-arg) method - extends
// export/import_object_literal_structural_typed.ts's coverage (that test's
// inc() took no arguments).
var acc: A.Accumulator = <A.Accumulator>A.acc;

acc.add(3);
print(acc.total);
assert(acc.total == 3);

acc.add(4);
print(acc.total);
assert(acc.total == 7);

print("done.");
Loading