Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2c1ead5
String: an unassigned fixed-length string holds Chr(0), not spaces; t…
KubaO Oct 18, 2026
8b93f77
BigNumberLib: moved to 1.0.4.0, which fixes the four defects that mad…
KubaO Oct 18, 2026
676dc23
WIP.BigNumberLib.md: the unconfirmed AssignDivision defect near the l…
KubaO Oct 18, 2026
9a5b715
IDE Building chapter: its 14 pictures in both themes and as SVG, six …
KubaO Oct 18, 2026
9c1b76a
impexp (twinBASIC edition): Sub Main and Arguments moved to Program.t…
KubaO Oct 18, 2026
9ab18eb
tbbroker: modules named Broker* as tblaunch's copy has them, Sub Main…
KubaO Oct 18, 2026
2a9d3e6
tbbroker: the shared entry is BrokerMain.RunArgs, not Command, which …
KubaO Oct 18, 2026
c8ac28d
impexp.py: names sorted by UTF-16 code unit and Packages matched in A…
KubaO Oct 18, 2026
9718248
impexp (twinBASIC edition): Impexp.PackTree packs the tree an ImpexpS…
KubaO Oct 18, 2026
07d08cd
One project per IDE is the harness's choice, not a limit: an IDE open…
KubaO Oct 19, 2026
50e080d
WIP: root.loadProject over CDP is not a switch (the tree keeps the ol…
KubaO Oct 19, 2026
535ac0c
waitForCompile wakes for a compile whose files the page has yet to li…
KubaO Oct 19, 2026
7b1b220
tb-wire: compileId 1 right after compileId 0 of the same compiler is …
KubaO Oct 19, 2026
a780827
BUGS: a 64-bit build converts a negative zero to "-0" (CStr, Str$, Fo…
KubaO Oct 19, 2026
c574054
waitForCompile's early path agrees for a project whose embedded packa…
KubaO Oct 19, 2026
cc02410
WIP.Harness: the compile wait's tail for WinReg and ArrayListLib is t…
KubaO Oct 19, 2026
cba09b6
examples.bat opens project after project in one IDE per lane (IdeSess…
KubaO Oct 19, 2026
e514db8
A --keep run's folder survives the next run's sweep: check_examples a…
KubaO Oct 19, 2026
774ac2e
A template built with no samples that meets its limit is built once m…
KubaO Oct 19, 2026
a06dbb3
WinReg's 21 samples are no longer marked check_build: under four lane…
KubaO Oct 19, 2026
b6620fe
shoot_docs: capture back to back with no rest until two captures agre…
KubaO Oct 19, 2026
5ae0a32
shoot_docs: a picture two captures agree on that is not the file is c…
KubaO Oct 19, 2026
9c72d09
shoot_docs: the title bar shows the window maximized, set in the page…
KubaO Oct 19, 2026
f81508b
shoot_docs: steady pictures on BETA 1010, every run unchanged
KubaO Oct 20, 2026
9998361
BETA 1010 retake of the documentation's IDE screenshots, both themes,…
KubaO Oct 20, 2026
f65332e
Class_Initialize or Sub New: when to use each, and constructors in de…
KubaO Oct 20, 2026
9025c5e
Constructors across a class chain, and what an initializer can call
KubaO Oct 20, 2026
b054136
Queue two initializer bugs: a class field reading another field, and …
KubaO Oct 20, 2026
942f468
Class_Initialize or Sub New: where each comes from
KubaO Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
124 changes: 124 additions & 0 deletions BUGS-TO-REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6283,3 +6283,127 @@ This looks like the same cause as #2497 (fixed in BETA 1004), where a constant o
Severity: low. The error is raised and is easy to see; the statement that raises it holds a correct value. A program meets it only when a constant that overflows is followed in the same procedure by a constant `Double` expression, which is mostly code that tests overflow handling.

<!-- Reproducer: bugs/const-double-expression-after-overflow-32bit/ (mode test, arch win32: `tbrun --tests` runs the eleven cases; expect lists the five FAIL and six PASS lines); verify reproduces on BETA 1010; VB6 side in vb6/ (Module1.bas, the same two statements for a Double and a Long). docs/Reference/Data-Types/LongLong.md carries the callout for it (the NOTE under Conversions); when a fixed build is released, the callout goes. Found 2026-10-18 while writing the Data Types pages; owner-approved. -->

---

## A 64-bit build prints a negative zero as `-0`

**Describe the bug**
A `Single` or `Double` that holds a negative zero (the sign bit set, every other bit clear) converts to the text `-0` in a 64-bit build: `CStr`, `Str$` (with no leading space), `Format$`, concatenation with `&` and `Debug.Print` all give it. A 32-bit twinBASIC build gives `0` (`Str$` gives ` 0`), and so does VB6. Both builds hold the negative zero: the sign bit (`&H80000000` in a `Single`, `&H8000000000000000` in a `Double`) is read back through `RtlMoveMemory`, and the value compares equal to 0. It was found on a page of the documentation whose sample prints a GDI+ matrix offset that is a negative zero, `0 5 -0` in a 64-bit build where the page says `0 5 0`. It was observed in IDE runs.

**To Reproduce**
Steps to reproduce the behavior:
1. Open `negative-zero-text-win64.twinproj` (attached as `negative-zero-text-win64.zip`). Its module `NegativeZeroTextTests` has one `[TestCase]` for each conversion, asserting what VB6 gives, two cases that check the sign bit, and two controls.
2. Run the test cases as a 64-bit build (`tbrun --tests --arch win64`). The seven conversion cases fail, with `-0` as `Actual`; the sign-bit cases and the controls pass. As a 32-bit build all eleven pass.
3. The shortest form of it, in a 64-bit build:
```vb
Dim s As Single
s = 0!
s = -s
Debug.Print CStr(s), Str$(s), Format$(s), "x=" & s ' VB6 and 32-bit: 0 " 0" 0 x=0 ; 64-bit: -0 -0 -0 x=-0
```

**Expected behavior**
The same text in a 64-bit build as in a 32-bit one and in VB6: `0` from `CStr`, `Format$` and `&`, and ` 0` from `Str$`. The reproducer's `vb6/` project prints them (attached as `negative-zero-text-win64-vb6.zip`).

**Desktop:**
- OS: Windows 10 Pro 22H2 (build 19045)
- twinBASIC compiler version: BETA 1010

**Additional context**
The reproducer's `repro.json` names the 64-bit build (`"arch": "win64"`). A positive zero and an ordinary negative number (`-1.5`) print the same in both builds. Severity: low. A negative zero is the result of `-x` for a zero `x`, and a COM or API result can hold one, so a program meets it, and one that shows or compares the text behaves differently in the two builds.

<!-- Reproducer: bugs/negative-zero-text-win64/ (mode test, arch win64: `tbrun --tests --arch win64` runs the eleven cases; expect lists the seven FAIL and four PASS lines); verify reproduces on BETA 1010; VB6 side in vb6/ (Module1.bas). docs/Reference/Default/VBA/Conversion/CStr.md, docs/Reference/Default/VBA/Conversion/Str.md and docs/Reference/Default/VBA/Strings/Format.md carry the callout for it (a WARNING each); docs/Reference/Contributed/GdiPlus/LinearGradientBrush.md has a check_run sample whose output "0 5 0" differs on win64 (not yet changed); when a fixed build is released, the callouts go. Found 2026-10-19 on the GdiPlus page. -->

---

## A class field whose initializer reads another field of the class fails the build with a codegen error

**Describe the bug**
A field initializer in a class that reads another field of the same class (`Public Second As Long = First + 1`) compiles with no diagnostic, and the build fails once the class is used: `[LINKER] compilation (codegen) error detected in 'Counter.{fields_init}' at line #1`, then `[LINKER] FAILED due to compilation errors`. A run in the IDE goes ahead instead, and the field holds an arbitrary number: with `A` = 1, `X = A + 1` read `1073741889`. Observed with `tbbuild --build` and with `tbrun` (a `[RunAfterBuild]` procedure in the IDE).

**To Reproduce**
Steps to reproduce the behavior:
1. Open `class-field-init-reads-field.twinproj` (attached as `class-field-init-reads-field.zip`). Its file `Startup.twin` is:
```
Module Startup
Public Sub Main()
Dim c As New Counter
Debug.Print c.First & ", " & c.Second
End Sub
End Module

Class Counter
Public First As Long = 1
Public Second As Long = First + 1
End Class
```
2. Compile the project: no diagnostic.
3. Build it. The DEBUG CONSOLE prints `[LINKER] compilation (codegen) error detected in 'Counter.{fields_init}' at line #1` and `[BUILD] failed`.

**Expected behavior**
The build succeeds and `Main` prints `1, 2`: `Second`'s initializer reads `First`, whose initializer comes before it. If reading another field is not meant to be allowed, the compiler should say so at the declaration, as it does for a call to a procedure of the project (TB5079). Field initializers are a twinBASIC extension, so there is no VB6 behaviour to compare with.

**Desktop:**
- OS: Windows 10 Pro 22H2 (build 19045)
- twinBASIC compiler version: BETA 1010

**Additional context**
The same codegen error, one line per class, for `X = A` with no arithmetic, `X = Me.A + 1`, `String` fields (`X = S & "b"`), a `ReadOnly` field read, a field declared below the one that reads it, and a field with no initializer of its own, in 32-bit and 64-bit builds. These are right: a field that reads a module-level variable (`X = Mods.MV + 1`) or a constant, a module-level variable that reads a variable of its own module (`MW = MV + 1`), and a class that is never created (it is not compiled, so the build does not fail).

Severity: medium. The build fails with a message that names the class but not the field or the reason, and the IDE run gives a wrong value with no message at all. Setting the field in `Class_Initialize` or `Sub New` avoids it.

Not tried: an `Object` or `Variant` field, LLVM.

<!-- Reproducer: bugs/class-field-init-reads-field/ (mode build: tbbuild --build exits 5 and the output names the codegen error in 'Counter.{fields_init}'); verified on BETA 1010 by `bug_repro.mjs verify`. The variants and win64 are from a scratch probe (one class per variant), not kept. Page: docs/Features/Language/Inline-Initialization.md, "What an Initializer Can Use", states it as the behaviour ("A class field cannot read another field of the same class"); when a fix is released, that bullet changes to say it can. Found 2026-10-20 while documenting field initializers. -->

---

## In the IDE, a module-level initializer that reads another module's variable leaves every module-level variable at 0

**Describe the bug**
When one module-level variable's initializer reads a variable of another module (`Public FromConfig As Long = Config.Limit + 100`), code run in the IDE reads every module-level variable of the project as 0, including ones whose initializers are constants (`Public Limit As Long = 5`). The built exe gets them all right. Observed with a `[RunAfterBuild]` procedure, through `tbrun` and `bug_repro.mjs run`; the exe was run with `tbrun --exe`.

**To Reproduce**
Steps to reproduce the behavior:
1. Open `module-init-reads-other-module-ide.twinproj` (attached as `module-init-reads-other-module-ide.zip`). Its file `Startup.twin` is:
```
Module Config
Public Const Base As Long = 7
Public Limit As Long = 5
Public Doubled As Long = Base * 2
End Module

Module Other
Public FromConfig As Long = Config.Limit + 100
End Module

Class Box
Public Size As Long = 3
End Class

Module Startup
Public Sub Main()
Dim b As New Box
Debug.Print "Limit=" & Config.Limit & " Doubled=" & Config.Doubled & " FromConfig=" & Other.FromConfig & " Box.Size=" & b.Size
End Sub
End Module
```
2. Add a module with a `[RunAfterBuild]` procedure that calls `Startup.Main`, and build the project.
3. The DEBUG CONSOLE prints `Limit=0 Doubled=0 FromConfig=0 Box.Size=3`.

**Expected behavior**
`Limit=5 Doubled=14 FromConfig=105 Box.Size=3`, which is what the built exe prints. Module-level initializers are a twinBASIC extension, so there is no VB6 behaviour to compare with.

**Desktop:**
- OS: Windows 10 Pro 22H2 (build 19045)
- twinBASIC compiler version: BETA 1010

**Additional context**
Every module-level variable reads 0 as long as one initializer reads another module's variable. Each of these makes the IDE print `Limit=5 Doubled=14`: the same project without `Other`, `FromConfig = 100`, `FromConfig = Config.Base + 100` (another module's constant), and `FromConfig = Limit2 + 100`, where `Limit2` is a variable of `Other` itself. A class field initializer runs either way (`Box.Size=3`). In the exe, a module whose initializer reads a module that is set up after it reads 0 there; that is a matter of order, and the IDE gives 0 for every module, whatever the order.

Severity: medium. A `[RunAfterBuild]` procedure that signs or post-processes the build sees wrong settings with no message, and code that runs correctly as an exe behaves differently in the IDE.

Not tried: a run with F5 (only `[RunAfterBuild]` was observed), a variable of the module's own read with a qualified name (`Other.Limit2`), 64-bit, LLVM.

<!-- Reproducer: bugs/module-init-reads-other-module-ide/ (mode run: bug_repro run prints the zeros; expect matches "Limit=0 Doubled=0 FromConfig=0 Box.Size=3"); verified on BETA 1010 by `bug_repro.mjs verify`. The exe side and the variants are from scratch probes (tbrun --exe with TbRun.Out; one tbrun per variant), not kept. It also means a check_run sample that relies on a module-level initializer reading another module would print 0 under check_examples. Page: docs/Features/Language/Inline-Initialization.md, "What an Initializer Can Use", last bullet, advises against reading another module's variable; when a fix is released, nothing there needs to change. Found 2026-10-20 while documenting field initializers. -->
28 changes: 28 additions & 0 deletions WIP.BigNumberLib.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# BigNumberLib --- working notes

The contributed BigNumberLib package, documented at `docs/Reference/Contributed/BigNumberLib/`,
its test project at `test/package-tests/BigNumberLib/`. This file holds what is known about the
package and is not on its pages yet, with the reason.

## Unconfirmed: AssignDivision near the library's capacity (1.0.4.0, BETA 1010)

Seen by one agent only, while it confirmed the hang of `AssignModulus` and `AssignAdditionModulus`
(the 1.0.4.0 WARNING on the BigNumber page). The owner's rule is that a defect goes on a page only
once a second, independent agent has reproduced it, so this stays here until one has.

- **Corrupt quotient, no error.** In a 32-bit program `AssignDivision` of a dividend of 4300 bits
gave a quotient of 2173 bits (about 4280 expected); 4288 bits was right. In a 64-bit program a
dividend of 4544 bits gave 2305 bits (about 4524 expected); 4300 bits was right. The edge is a
dividend of FP_SIZE - 1 digits or more: over about 4288 bits (32-bit), 4480 bits (64-bit).
- **Cause, from the source.** TomsFastMath 0.13.1 `fp_div` sets `q.used = a.used + 2`; when that
exceeds FP_SIZE (136 digits of 32 bits, 72 of 64), `fp_clamp` stops at a stray digit and the
quotient's `used` is wrong. BigNumber.twin's `AssignDivision` calls `fp_div` with no guard.
- **Also seen:** `AssignDivision` hangs from the same edge as `AssignModulus`, since both go
through `fp_div`, which drops the carry of `fp_mul_2d` when the normalised dividend does not
fit.
- Both need a dividend beyond the 4096 bits the class supports, which a product that
`AssignMultiplication` accepts can be.

**To settle it:** a raise-or-right case in `OverflowTests.twin` (as
`Overflow_ModulusOfANumberThatFillsTheLibrary_RaisesErrorOrIsRight`), a second agent with the
verifier brief, then the BigNumber page's overflow WARNING and `expect.json`.
29 changes: 28 additions & 1 deletion WIP.ExamplesBuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,30 @@ sample that names it.
One project per fence is unaffordable. What makes it tractable is that **IDE cost is flat in
project size**: what is paid for is startup, not compilation.

**So a lane starts its IDE once.** Each lane holds an `IdeSession`
([scripts/lib/tb-session.mjs](scripts/lib/tb-session.mjs)) beside its slot, and every project
after the first opens in that IDE through the page's own File > Open Project. Before each switch
the session undoes the Safe Mode a crash loop leaves and the TRACE-MODE a native exception turns
on; a project that crashed the compiler is closed at once and never reopened in that IDE, and
its bisect halves, which are other projects, go to the same IDE. A failed switch, exit 2 and
exit 3 give the IDE up for a new one, and a project a reused IDE never reported open, or met a
harness fault in, is built again in a new IDE before its result counts. Why each of those, and
the measurements: [WIP.Harness.md](WIP.Harness.md#one-ide-per-project-or-one-per-lane).
`--fresh` starts a new IDE for every project, and is the control: a full run with it and one
without must report the same findings. The run's summary line says how many IDEs started and
how many projects were opened in one already running.

**A batch that meets its limit is a finding, not the end of the run.** Its samples are not
judged, and it is not split: a slow template's batches meet the `compile` limit with no samples
in them, because the time is the template's load (WinReg's did, which is why its samples are not
marked). The run goes on, so one slow template cannot cost the
other batches their result.
The template built with no samples (`ownRowsOf`, which tells the template's own rows from
a batch's) is built once more when it meets the limit; met twice, the batch that asked is not
judged and the template is forgotten, so the next batch asks again. Taking that empty read as
a clean template blamed every sample of a package that does not compile on its own: a shared
`--fresh` run reported 17 ArrayListLib samples for the package's own TB5001 rows.

**Fill the lanes, not the batches.** Filling each batch to `--batch` before opening another
would put 120, 55, 4 and 3 samples on four lanes, and a run takes as long as its biggest
batch. The batcher sizes to `ceil(total / jobs)` instead.
Expand Down Expand Up @@ -802,7 +826,10 @@ LLVM refusal is isolated, and its samples reported as failing the build, as any
wedge and neither ever returns. Each lane owns a workspace, not just a slot.
- **A run's folder is named for its process**, `tbexamples\<pid>`, so runs at once never
share one; a later run removes the folders of runs whose process has ended, and leaves one
it cannot remove yet. Before each IDE ran in its launcher's job, a run that died left its
it cannot remove yet. A `--keep` run marks its folder with a `.keep` file from the start
(`keepRunFolder` in `scripts/lib/tb-registry.mjs`), and the sweep leaves a marked folder
for good; before that, a kept folder went within seconds of the next run starting.
`sweep_attributes.mjs` sweeps `tbsweep\<pid>` the same way. Before each IDE ran in its launcher's job, a run that died left its
lanes' IDEs running on desktops nobody could see, holding that folder.
- **Batches are packed in process by impexp** (`packTree` in `scripts/lib/tb-project.mjs`),
never by the compiler executable's `import`, which exits 999 with nothing written on a
Expand Down
Loading
Loading