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
134 changes: 134 additions & 0 deletions BUGS-TO-REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -7911,3 +7911,137 @@ Severity: high for the crash: a program loses its unsaved data, and an error han
A 64-bit build gives the same results.

<!-- Reproducer: bugs/raiseeventbyname-no-events-crash/ (mode test: `tbrun --tests`; expect lists the five PASS and five FAIL lines); run on BETA 1010 with `bug_repro.mjs run` and `--arch win64`. In a built exe the crash ends the program: the run-time errors probe (`errors.mjs run`) stopped there with exception 0xc0000005, 32-bit and 64-bit, which is why the data has no case for it. Stated in a WARNING by hand (the crash) and a NOTE (no handler) on docs/Reference/Default/VBA/Interaction/RaiseEventByName.md and RaiseEventByName2.md, each beside an HTML comment naming this slug; when fixed, both go. Found 2026-10-22 by batch 6a of WIP.Errors.md. -->

---

## A function call in an `#If`, `#ElseIf` or `#Const` expression is always 0, so `#If CBool(1)` is false

**Describe the bug**
Every function call in a conditional compilation expression evaluates as 0, and the IDE reports nothing. `#If CBool(1) Then`, `#If CLng(1) = 1 Then`, `#If Abs(-1) = 1 Then`, `#If Len("abc") = 3 Then` and `#If Len(TWINBASIC_BUILD_TYPE) = 12 Then` (in a Standard EXE) all take the `#Else` branch, and `#Const X = CBool(1)` makes `X` 0. As a result `#If Not CBool(Win64) Then`, the usual way to select 32-bit-only code from a constant that is 1 rather than `True`, is true in a 64-bit build too. Observed in the compiler's test mode, in a `[RunAfterBuild]` run and in the DIAGNOSTICS list (a branch that declares a variable of a type that does not exist reports an error only when it is active), 32-bit and 64-bit.

**To Reproduce**
Steps to reproduce the behavior:
1. Open `ifdef-function-call-is-zero.twinproj` (attached as `ifdef-function-call-is-zero.zip`).
2. Run its test cases, the module below. On BETA 1010 the two controls pass and the seven cases with a function call fail with `Actual: False`; in a 64-bit build `If_NotCBoolOfWin64_AgreesWithWin64EqualsZero` fails too.

The module is meant to be adapted as a regression test. Each case records which branch an `#If` takes and asserts the branch VB6 takes, which the attached VB6 project (`ifdef-function-call-is-zero-vb6.zip`) prints.
```vb
[TestFixture]
Module RegressionTests

' Controls: literals, constants and operators alone are evaluated.
[TestCase]
Public Sub If_LiteralComparison_IsTrue()
Dim taken As Boolean
#If 1 = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_Win32EqualsOne_IsTrue()
Dim taken As Boolean
#If Win32 = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

' The defect: each call is 0.
[TestCase]
Public Sub If_CBoolOfOne_IsTrue()
Dim taken As Boolean
#If CBool(1) Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_CBoolOfWin32_IsTrue()
Dim taken As Boolean
#If CBool(Win32) Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_NotCBoolOfWin64_AgreesWithWin64EqualsZero()
Dim notCBool As Boolean, equalsZero As Boolean
#If Not CBool(Win64) Then
notCBool = True
#End If
#If Win64 = 0 Then
equalsZero = True
#End If
Assert.Strict.AreEqual equalsZero, notCBool
End Sub

[TestCase]
Public Sub If_CLngOfOneEqualsOne_IsTrue()
Dim taken As Boolean
#If CLng(1) = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_AbsOfMinusOneEqualsOne_IsTrue()
Dim taken As Boolean
#If Abs(-1) = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_LenOfLiteralEqualsThree_IsTrue()
Dim taken As Boolean
#If Len("abc") = 3 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_LenOfBuildTypeEqualsTwelve_IsTrue()
' The project is a Standard EXE: "Standard EXE" has 12 characters.
Dim taken As Boolean
#If Len(TWINBASIC_BUILD_TYPE) = 12 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

#Const FROM_CALL = CBool(1)

[TestCase]
Public Sub Const_FromCBoolOfOne_IsTrue()
Dim taken As Boolean
#If FROM_CALL Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

End Module
```

**Expected behavior**
Each call is evaluated, as VB6 evaluates it: `CBool(1)`, `CBool(Win32)`, `CLng(1) = 1`, `Abs(-1) = 1`, `Len("abc") = 3` and `#Const FROM_CALL = CBool(1)` are all true there. If twinBASIC is not meant to support them, the call should be a compile error rather than a silent 0.

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

**Additional context**
Severity: medium. The wrong branch is compiled with no error or warning, so a program can call a 32-bit declaration in a 64-bit build, or leave out code it meant to keep. `#If Not CBool(Win64)` was the documentation's own advice until now.

A 64-bit build gives the same results. Reported independently on BETA 1024 (docs issue twinbasic/documentation#250), with `VarType(...)` as well.

What does not reproduce it: an expression of constants, literals and operators alone, such as `#If Win64 = 0`, `#If Win64 <> 1` or `#If Not (Win64 = 1)`, each true in a 32-bit build only, as it should be.

<!-- Reproducer: bugs/ifdef-function-call-is-zero/ (mode test: `tbrun --tests`; expect lists the two PASS and seven FAIL lines of a 32-bit run); run on BETA 1010 with `bug_repro.mjs run`, and with `--arch win64`, where the Not CBool(Win64) case fails as well. VB6 side in vb6/ (VB6 6.0): `bug_repro.mjs vb6` prints True for every line. Stated in a WARNING labelled BETA 1010 on docs/Reference/Compiler Constants.md (after the IMPORTANT note) and docs/Reference/Core/Topic-Preprocessor.md (under the #If directive), each beside an HTML comment naming this slug; when fixed, both go. Found 2026-10-27 from docs issue #250; that issue's second point, a #Const reaching later modules of the same file, is intended (owner: it works like C's #define), and the pages now say so. -->
Binary file not shown.
17 changes: 17 additions & 0 deletions bugs/ifdef-function-call-is-zero/repro.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"mode": "test",
"expect": {
"exit": 7,
"output": [
"^PASS RegressionTests.If_LiteralComparison_IsTrue$",
"^PASS RegressionTests.If_Win32EqualsOne_IsTrue$",
"^FAIL RegressionTests.If_CBoolOfOne_IsTrue: ",
"^FAIL RegressionTests.If_CBoolOfWin32_IsTrue: ",
"^FAIL RegressionTests.If_CLngOfOneEqualsOne_IsTrue: ",
"^FAIL RegressionTests.If_AbsOfMinusOneEqualsOne_IsTrue: ",
"^FAIL RegressionTests.If_LenOfLiteralEqualsThree_IsTrue: ",
"^FAIL RegressionTests.If_LenOfBuildTypeEqualsTwelve_IsTrue: ",
"^FAIL RegressionTests.Const_FromCBoolOfOne_IsTrue: "
]
}
}
75 changes: 75 additions & 0 deletions bugs/ifdef-function-call-is-zero/src/Settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"configuration.inherits": "Defaults",
"project.appTitle": "IfdefFunctionCallIsZero",
"project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}",
"project.buildType": "Standard EXE",
"project.description": "Reproduces: #If treats every function call as 0",
"project.exportPathIsV2": true,
"project.id": "{95BFA9FC-6D6D-4000-B200-D5DB2BC0E770}",
"project.name": "IfdefFunctionCallIsZero",
"project.optionExplicit": true,
"project.references": [
{
"id": "{00020430-0000-0000-C000-000000000046}",
"lcid": 0,
"name": "OLE Automation",
"path32": "C:\\Windows\\SysWOW64\\stdole2.tlb",
"path64": "C:\\Windows\\System32\\stdole2.tlb",
"symbolId": "stdole",
"versionMajor": 2,
"versionMinor": 0
},
{
"id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}",
"isCompilerPackage": true,
"licence": "MIT",
"name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)",
"path32": "",
"path64": "",
"publisher": "TWINBASIC-COMPILER",
"symbolId": "VB",
"versionBuild": 0,
"versionMajor": 0,
"versionMinor": 0,
"versionRevision": 31
},
{
"id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}",
"isCompilerPackage": true,
"licence": "MIT",
"name": "[COMPILER PACKAGE] twinBASIC - App global class object",
"path32": "",
"path64": "",
"publisher": "TWINBASIC-COMPILER",
"symbolId": "AppGlobalClassProject",
"versionBuild": 0,
"versionMajor": 1,
"versionMinor": 0,
"versionRevision": 0
},
{
"id": "{9847A10D-8987-44B2-8B87-EA77DEF7F1D9}",
"isCompilerPackage": true,
"licence": "MIT",
"name": "[COMPILER PACKAGE] Assert",
"path32": "",
"path64": "",
"publisher": "TWINBASIC-COMPILER",
"symbolId": "Assert",
"versionBuild": 0,
"versionMajor": 0,
"versionMinor": 0,
"versionRevision": 24
}
],
"project.settingsVersion": 1,
"project.startupObject": "Sub Main",
"project.warnings": {
"errors": [],
"hints": [],
"ignored": [],
"info": [],
"warnings": []
},
"runtime.useUnicodeStandardLibrary": true
}
106 changes: 106 additions & 0 deletions bugs/ifdef-function-call-is-zero/src/Sources/RegressionTests.twin
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
' Regression tests: a function call in an #If expression must be evaluated, as VB6 and
' VBA evaluate it. On BETA 1010 every call is 0, with no diagnostic, so each #If below
' that calls a function takes its #Else branch. Each case records the branch taken and
' asserts the branch VB6 takes.
[TestFixture]
Module RegressionTests

' Controls: literals, constants and operators alone are evaluated.
[TestCase]
Public Sub If_LiteralComparison_IsTrue()
Dim taken As Boolean
#If 1 = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_Win32EqualsOne_IsTrue()
Dim taken As Boolean
#If Win32 = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

' The defect: each call is 0.
[TestCase]
Public Sub If_CBoolOfOne_IsTrue()
Dim taken As Boolean
#If CBool(1) Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_CBoolOfWin32_IsTrue()
Dim taken As Boolean
#If CBool(Win32) Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_NotCBoolOfWin64_AgreesWithWin64EqualsZero()
Dim notCBool As Boolean, equalsZero As Boolean
#If Not CBool(Win64) Then
notCBool = True
#End If
#If Win64 = 0 Then
equalsZero = True
#End If
Assert.Strict.AreEqual equalsZero, notCBool
End Sub

[TestCase]
Public Sub If_CLngOfOneEqualsOne_IsTrue()
Dim taken As Boolean
#If CLng(1) = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_AbsOfMinusOneEqualsOne_IsTrue()
Dim taken As Boolean
#If Abs(-1) = 1 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_LenOfLiteralEqualsThree_IsTrue()
Dim taken As Boolean
#If Len("abc") = 3 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

[TestCase]
Public Sub If_LenOfBuildTypeEqualsTwelve_IsTrue()
' The project is a Standard EXE: "Standard EXE" has 12 characters.
Dim taken As Boolean
#If Len(TWINBASIC_BUILD_TYPE) = 12 Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

#Const FROM_CALL = CBool(1)

[TestCase]
Public Sub Const_FromCBoolOfOne_IsTrue()
Dim taken As Boolean
#If FROM_CALL Then
taken = True
#End If
Assert.Strict.AreEqual True, taken
End Sub

End Module
6 changes: 6 additions & 0 deletions bugs/ifdef-function-call-is-zero/src/Sources/Startup.twin
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Module Startup

Public Sub Main()
End Sub

End Module
Loading
Loading