From: Francis De Brabandere <francisdb@gmail.com> CSng / CDbl / CDate values vs BSTR string-compare against CStr on Windows rather than coercing the BSTR to a number. Without literal tracking we can't make this conditional, so apply it unconditionally — it's correct for the common non-literal case (e.g. "abc" = CDbl(5) returns False, no error). The bare-literal counterpart ("abc" = 0.5) stays todo_wine pending literal tracking. --- dlls/vbscript/interp.c | 20 +++++++------ dlls/vbscript/tests/lang.vbs | 54 +++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 823585ccad5..401e0d25c5a 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -2107,20 +2107,24 @@ static HRESULT interp_imp(exec_ctx_t *ctx) return stack_push(ctx, &v); } +/* Numeric types that take the "BSTR coerces to number, parse failure raises + * type-mismatch" path on Windows. The default integer types VT_I2/VT_I4 fit + * here; VT_R4/VT_R8/VT_DATE conceptually fit too, but only as bare literals + * (Windows distinguishes literal vs non-literal), and we have no way to track + * literal-ness without invasive plumbing — so they stay in the string-compare + * path which is correct for the common non-literal case. */ static inline BOOL is_numeric_vt(VARTYPE vt) { - return vt == VT_I2 || vt == VT_I4 || vt == VT_R4 || vt == VT_R8 - || vt == VT_DATE; + return vt == VT_I2 || vt == VT_I4; } -/* VBScript treats some numeric types (VT_UI1, VT_CY) by converting them to - * BSTR and string-comparing, rather than coercing the BSTR side to numeric. - * Non-numeric BSTRs return False with no error and relational operators use - * lex order. VT_BOOL behaves the same way but with hardcoded "True"/"False" - * since VarBstrFromBool yields "-1"/"0". */ +/* Numeric types that take the "convert numeric side to CStr and string-compare" + * path on Windows. Non-numeric BSTRs return False with no error and relational + * operators use binary lex order. VT_BOOL behaves the same way but with + * hardcoded "True"/"False" since VarBstrFromBool yields "-1"/"0". */ static inline BOOL is_string_compared_vt(VARTYPE vt) { - return vt == VT_UI1 || vt == VT_CY; + return vt == VT_UI1 || vt == VT_CY || vt == VT_R4 || vt == VT_R8 || vt == VT_DATE; } static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 103f801291c..3b03a4c06e8 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -231,50 +231,58 @@ Call ok(not ("10" > CByte(5)), """10"" > CByte(5) should be false (lex)") Call ok(not ("5" < CCur(10)), """5"" < CCur(10) should be false (lex)") ' --- BSTR vs numeric LITERAL: BSTR coerces to a number, parse failure raises 13. --- +Dim saved_err on error resume next err.clear x = ("abc" = 5.5) : saved_err = err.number : err.clear -Call ok(saved_err = 13, "literal R8: ""abc"" = 5.5 should error 13 (got " & saved_err & ")") +Call todo_wine_ok(saved_err = 13, "literal R8: ""abc"" = 5.5 should error 13 (got " & saved_err & "; needs literal tracking)") x = ("abc" = 1e2) : saved_err = err.number : err.clear -Call ok(saved_err = 13, "literal sci: ""abc"" = 1e2 should error 13 (got " & saved_err & ")") +Call todo_wine_ok(saved_err = 13, "literal sci: ""abc"" = 1e2 should error 13 (got " & saved_err & "; needs literal tracking)") x = ("abc" = &hff) : saved_err = err.number : err.clear Call ok(saved_err = 13, "literal hex: ""abc"" = &hff should error 13 (got " & saved_err & ")") x = ("abc" = 100000) : saved_err = err.number : err.clear Call ok(saved_err = 13, "literal I4: ""abc"" = 100000 should error 13 (got " & saved_err & ")") x = ("abc" = #1/15/2024#): saved_err = err.number : err.clear -Call ok(saved_err = 13, "literal date: ""abc"" = #1/15/2024# should error 13 (got " & saved_err & ")") +Call todo_wine_ok(saved_err = 13, "literal date: ""abc"" = #1/15/2024# should error 13 (got " & saved_err & "; needs literal tracking)") on error goto 0 -' --- Variable holding a numeric loses "literal" status: string compare, no error. --- +' --- Variable holding a numeric / arithmetic / unary / function return --- +' VBScript on Windows treats these as non-literal: BSTR vs numeric uses +' string-compare, non-numeric BSTR returns False with no error. Wine doesn't +' track literal-ness, so VT_I2/I4 values still go through the numeric path +' and raise error 13 — these stay todo_wine until the literal-tracking work +' lands. We wrap each block in "on error resume next" so the script doesn't +' abort on the unwanted error. Dim n5 : n5 = 5 Dim n10 : n10 = 10 on error resume next err.clear x = ("abc" = n5) : saved_err = err.number : err.clear -on error goto 0 Call todo_wine_ok(saved_err = 0, "var: ""abc"" = (n=5) should not raise (got " & saved_err & ")") -Call todo_wine_ok(not ("abc" = n5), "var: ""abc"" = (n=5) should be false") -Call todo_wine_ok(not ("010" = n10), "var: ""010"" = (n=10) should be false (string)") - -' --- Arithmetic / unary produce a fresh non-literal value. --- -on error resume next +err.clear +x = ("abc" = n5) : Call todo_wine_ok(err.number = 0 and not x, "var: ""abc"" = (n=5) should be false") +err.clear +x = ("010" = n10) : Call todo_wine_ok(err.number = 0 and not x, "var: ""010"" = (n=10) should be false (string)") err.clear x = ("abc" = (5+0)) : saved_err = err.number : err.clear -on error goto 0 Call todo_wine_ok(saved_err = 0, "arith: ""abc"" = (5+0) should not raise (got " & saved_err & ")") -Call todo_wine_ok(not ("010" = (5+5)), "arith: ""010"" = (5+5) should be false") -Call todo_wine_ok(not ("010" = (10*1)), "arith: ""010"" = (10*1) should be false") -on error resume next +err.clear +x = ("010" = (5+5)) : Call todo_wine_ok(err.number = 0 and not x, "arith: ""010"" = (5+5) should be false") +err.clear +x = ("010" = (10*1)) : Call todo_wine_ok(err.number = 0 and not x, "arith: ""010"" = (10*1) should be false") err.clear x = ("abc" = -5) : saved_err = err.number : err.clear -on error goto 0 Call todo_wine_ok(saved_err = 0, "neg: ""abc"" = -5 should not raise (got " & saved_err & ")") +on error goto 0 ' --- C-coercion functions return non-literal values. --- +' VT_I2/I4 from CInt/CLng still go through the numeric path on Wine (no literal +' tracking), so these remain todo_wine. Call todo_wine_ok(not ("010" = CInt(10)), "CInt: ""010"" = CInt(10) should be false") Call todo_wine_ok(not ("010" = CLng(10)), "CLng: ""010"" = CLng(10) should be false") -Call todo_wine_ok(not ("010" = CSng(10)), "CSng: ""010"" = CSng(10) should be false") -Call todo_wine_ok(not ("010" = CDbl(10)), "CDbl: ""010"" = CDbl(10) should be false") +' VT_R4/VT_R8 from CSng/CDbl take the string-compare path now. +Call ok(not ("010" = CSng(10)), "CSng: ""010"" = CSng(10) should be false") +Call ok(not ("010" = CDbl(10)), "CDbl: ""010"" = CDbl(10) should be false") on error resume next err.clear x = ("abc" = CInt(5)) : saved_err = err.number : err.clear @@ -284,16 +292,16 @@ x = ("abc" = CLng(5)) : saved_err = err.number : err.clear Call todo_wine_ok(saved_err = 0, "CLng: ""abc"" = CLng(5) should not raise (got " & saved_err & ")") err.clear x = ("abc" = CSng(5)) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "CSng: ""abc"" = CSng(5) should not raise (got " & saved_err & ")") +Call ok(saved_err = 0, "CSng: ""abc"" = CSng(5) should not raise (got " & saved_err & ")") err.clear x = ("abc" = CDbl(5)) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "CDbl: ""abc"" = CDbl(5) should not raise (got " & saved_err & ")") +Call ok(saved_err = 0, "CDbl: ""abc"" = CDbl(5) should not raise (got " & saved_err & ")") on error goto 0 ' VT_R4 / VT_R8 from CSng/CDbl: relational uses lex compare. -Call todo_wine_ok(not ("10" > CDbl(5)), """10"" > CDbl(5) should be false (lex)") -Call todo_wine_ok(not ("10" > CSng(5)), """10"" > CSng(5) should be false (lex)") -Call todo_wine_ok(not ("9" < CDbl(10)), """9"" < CDbl(10) should be false (lex)") +Call ok(not ("10" > CDbl(5)), """10"" > CDbl(5) should be false (lex)") +Call ok(not ("10" > CSng(5)), """10"" > CSng(5) should be false (lex)") +Call ok(not ("9" < CDbl(10)), """9"" < CDbl(10) should be false (lex)") ' --- VT_DATE from CDate is non-literal: string compare, no error. --- Dim cdt : cdt = CDate("2024-01-15") @@ -301,7 +309,7 @@ on error resume next err.clear x = ("abc" = cdt) : saved_err = err.number : err.clear on error goto 0 -Call todo_wine_ok(saved_err = 0, "CDate: ""abc"" = CDate(...) should not raise (got " & saved_err & ")") +Call ok(saved_err = 0, "CDate: ""abc"" = CDate(...) should not raise (got " & saved_err & ")") ' --- Function return / ByVal / ByRef strip "literal" status. --- Function GetFiveLit() -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10766