Skip to content

Commit 244edbc

Browse files
committed
Merge branch 'master' into davlgd-veb-static-gzip
* master: cgen: fix dump(ptr_to_fixed_array_value) - it now returns the argument directly (thanks @tankf33der) (vlang#25764) type_resolver: clear option flag if present in comptime $for (fix vlang#25761) (vlang#25763) db.sqlite: update windows version to https://sqlite.org/2025/sqlite-amalgamation-3510000.zip (vlang#25766) builder,pref: add `-file-list` support (implement feature vlang#25707) (vlang#25749) transformer: transform ArrayInit into a function call for C and native backends (part 1) doc: cleanup grammar issues in doc/docs.md parser: allow using `a`, `b` and `it` as var names, when using the builtin array methods (fix vlang#25729) (vlang#25755) v.builder: improve diagnostic information for a failed build_thirdparty_obj_file veb: fix gzip middleware decode/encode (fix vlang#20865) (fix vlang#25753) (vlang#25754) strconv: fix handling of subnormal numbers like `'1.23e-308'.f64()` (fix vlang#25751) (vlang#25752) crypto.blake2b,crypto.blake2s: add `@[direct_array_access]` to hot functions (vlang#25750)
2 parents a552a5f + dd6bb5b commit 244edbc

File tree

24 files changed

+434
-68
lines changed

24 files changed

+434
-68
lines changed
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
@echo off
22

3-
curl -L https://www.sqlite.org/2024/sqlite-amalgamation-3450300.zip -o sqlite-amalgamation-3450300.zip
3+
curl -L https://sqlite.org/2025/sqlite-amalgamation-3510000.zip -o sqlite-amalgamation-3510000.zip
44

5-
unzip sqlite-amalgamation-3450300.zip -d thirdparty\
5+
unzip sqlite-amalgamation-3510000.zip -d thirdparty\
66

7-
del thirdparty\sqlite-amalgamation-3450300\shell.c
7+
del thirdparty\sqlite-amalgamation-3510000\shell.c
88

9-
move /y thirdparty\sqlite-amalgamation-3450300 thirdparty\sqlite
9+
move /y thirdparty\sqlite-amalgamation-3510000 thirdparty\sqlite
1010

1111
dir thirdparty\sqlite

doc/docs.md

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -921,16 +921,16 @@ f2 := 456e+2 // 45600
921921

922922
An array is a collection of data elements of the same type. An array literal is a
923923
list of expressions surrounded by square brackets. An individual element can be
924-
accessed using an *index* expression. Indexes start from `0`:
924+
accessed using an *index* expression. Indexing starts from `0`.
925925

926926
```v
927-
mut nums := [1, 2, 3]
928-
println(nums) // `[1, 2, 3]`
929-
println(nums[0]) // `1`
930-
println(nums[1]) // `2`
927+
mut nums := [10, 20, 30]
928+
println(nums) // `[10, 20, 30]`
929+
println(nums[0]) // `10`
930+
println(nums[1]) // `20`
931931
932932
nums[1] = 5
933-
println(nums) // `[1, 5, 3]`
933+
println(nums) // `[10, 5, 30]`
934934
```
935935

936936
<a id='array-operations'></a>
@@ -4214,8 +4214,8 @@ fn pass_time(w World) {
42144214

42154215
### Option/Result types and error handling
42164216

4217-
Option types are for types which may represent `none`. Result types may
4218-
represent an error returned from a function.
4217+
Option types can represent a value or `none`. Result types may
4218+
represent a value, or an error returned from a function.
42194219

42204220
`Option` types are declared by prepending `?` to the type name: `?Type`.
42214221
`Result` types use `!`: `!Type`.
@@ -4467,9 +4467,9 @@ post := posts_repo.find_by_id(1)? // find_by_id[Post]
44674467
```
44684468

44694469
Currently generic function definitions must declare their type parameters, but in
4470-
future V will infer generic type parameters from single-letter type names in
4471-
runtime parameter types. This is why `find_by_id` can omit `[T]`, because the
4472-
receiver argument `r` uses a generic type `T`.
4470+
future versions, V will infer generic type parameters from single-letter type names in
4471+
runtime parameter types. This is why the `find_by_id(1)` calls above can omit `[T]`,
4472+
because the receiver argument `r` in the method declaration, uses a generic type `T`.
44734473

44744474
Another example:
44754475

@@ -5135,10 +5135,11 @@ Remaining small percentage of objects is freed via GC. The developer doesn't nee
51355135
anything in their code. "It just works", like in Python, Go, or Java, except there's no
51365136
heavy GC tracing everything or expensive RC for each object.
51375137

5138-
For developers willing to have more low level control, memory can be managed manually with
5138+
For developers willing to have more low-level control, memory can be managed manually with
51395139
`-gc none`.
51405140

5141-
Arena allocation is available via v `-prealloc`.
5141+
Arena allocation is available via a `-prealloc` flag. Note: currently this mode is only
5142+
suitable to speed up short lived, single-threaded, batch-like programs (like compilers).
51425143

51435144
### Control
51445145

@@ -5159,7 +5160,7 @@ Just as the compiler frees C data types with C's `free()`, it will statically in
51595160

51605161
Autofree can be enabled with an `-autofree` flag.
51615162

5162-
For developers willing to have more low level control, autofree can be disabled with
5163+
For developers willing to have more low-level control, autofree can be disabled with
51635164
`-manualfree`, or by adding a `[manualfree]` on each function that wants to manage its
51645165
memory manually. (See [attributes](#attributes)).
51655166

@@ -5232,7 +5233,7 @@ be stored:
52325233

52335234
#### V's default approach
52345235

5235-
Due to performance considerations V tries to put objects on the stack if possible
5236+
Due to performance considerations V tries to put objects on the stack if possible,
52365237
but allocates them on the heap when obviously necessary. Example:
52375238

52385239
```v
@@ -7410,7 +7411,7 @@ rm pgo_gen
74107411
74117412
## Atomics
74127413
7413-
V has no special support for atomics, yet, nevertheless it's possible to treat variables as atomics
7414+
V has no special support for atomics yet, nevertheless it's possible to treat variables as atomics
74147415
by [calling C](#v-and-c) functions from V. The standard C11 atomic functions like `atomic_store()`
74157416
are usually defined with the help of macros and C compiler magic to provide a kind of
74167417
*overloaded C functions*.
@@ -7499,7 +7500,7 @@ will hang &ndash; dependent on the compiler optimization used.)
74997500
75007501
## Global Variables
75017502
7502-
By default V does not allow global variables. However, in low level applications they have their
7503+
By default V does not allow global variables. However, in low-level applications they have their
75037504
place so their usage can be enabled with the compiler flag `-enable-globals`.
75047505
Declarations of global variables must be surrounded with a `__global ( ... )`
75057506
specification &ndash; as in the example [above](#atomics).
@@ -7550,7 +7551,7 @@ namespaced globals).
75507551
75517552
Note: their use is discouraged too, for reasons similar to why globals
75527553
are discouraged. The feature is supported to enable translating existing
7553-
low level C code into V code, using `v translate`.
7554+
low-level C code into V code, using `v translate`.
75547555
75557556
Note: the function in which you use a static variable, has to be marked
75567557
with @[unsafe]. Also unlike using globals, using static variables, do not
@@ -7603,7 +7604,7 @@ v -os freebsd .
76037604
> Cross-compiling a Windows binary on a Linux machine requires the GNU C compiler for
76047605
> MinGW-w64 (targeting Win64) to first be installed.
76057606
7606-
For Ubuntu/Debian based distributions:
7607+
For Ubuntu/Debian-based distributions:
76077608
76087609
```shell
76097610
sudo apt install gcc-mingw-w64-x86-64
@@ -7632,7 +7633,7 @@ To debug issues in the generated binary (flag: `-b c`), you can pass these flags
76327633
- `-g` - produces a less optimized executable with more debug information in it.
76337634
V will enforce line numbers from the .v files in the stacktraces, that the
76347635
executable will produce on panic. It is usually better to pass -g, unless
7635-
you are writing low level code, in which case use the next option `-cg`.
7636+
you are writing low-level code, in which case use the next option `-cg`.
76367637
- `-cg` - produces a less optimized executable with more debug information in it.
76377638
The executable will use C source line numbers in this case. It is frequently
76387639
used in combination with `-keepc`, so that you can inspect the generated
@@ -7645,7 +7646,7 @@ To debug issues in the generated binary (flag: `-b c`), you can pass these flags
76457646
compilation. Also keep using the same file path, so it is more stable,
76467647
and easier to keep opened in an editor/IDE.
76477648
7648-
For best debugging experience if you are writing a low level wrapper for an existing
7649+
For best debugging experience if you are writing a low-level wrapper for an existing
76497650
C library, you can pass several of these flags at the same time:
76507651
`v -keepc -cg -showcc yourprogram.v`, then just run your debugger (gdb/lldb) or IDE
76517652
on the produced executable `yourprogram`.

vlib/crypto/blake2b/blake2b_block_generic.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module blake2b
1111
import math.bits
1212

1313
// mixing function g
14-
@[inline]
14+
@[direct_array_access; inline]
1515
fn g(mut v []u64, a u8, b u8, c u8, d u8, x u64, y u64) {
1616
v[a] = v[a] + v[b] + x
1717
v[d] = bits.rotate_left_64((v[d] ^ v[a]), nr1)
@@ -24,7 +24,7 @@ fn g(mut v []u64, a u8, b u8, c u8, d u8, x u64, y u64) {
2424
}
2525

2626
// one complete mixing round with the function g
27-
@[inline]
27+
@[direct_array_access; inline]
2828
fn (d Digest) mixing_round(mut v []u64, s []u8) {
2929
g(mut v, 0, 4, 8, 12, d.m[s[0]], d.m[s[1]])
3030
g(mut v, 1, 5, 9, 13, d.m[s[2]], d.m[s[3]])
@@ -38,6 +38,7 @@ fn (d Digest) mixing_round(mut v []u64, s []u8) {
3838
}
3939

4040
// compression function f
41+
@[direct_array_access]
4142
fn (mut d Digest) f(f bool) {
4243
// initialize the working vector
4344
mut v := []u64{len: 0, cap: 16}

vlib/crypto/blake2s/blake2s_block_generic.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module blake2s
1111
import math.bits
1212

1313
// mixing function g
14-
@[inline]
14+
@[direct_array_access; inline]
1515
fn g(mut v []u32, a u8, b u8, c u8, d u8, x u32, y u32) {
1616
v[a] = v[a] + v[b] + x
1717
v[d] = bits.rotate_left_32((v[d] ^ v[a]), nr1)
@@ -24,7 +24,7 @@ fn g(mut v []u32, a u8, b u8, c u8, d u8, x u32, y u32) {
2424
}
2525

2626
// one complete mixing round with the function g
27-
@[inline]
27+
@[direct_array_access; inline]
2828
fn (d Digest) mixing_round(mut v []u32, s []u8) {
2929
g(mut v, 0, 4, 8, 12, d.m[s[0]], d.m[s[1]])
3030
g(mut v, 1, 5, 9, 13, d.m[s[2]], d.m[s[3]])
@@ -38,6 +38,7 @@ fn (d Digest) mixing_round(mut v []u32, s []u8) {
3838
}
3939

4040
// compression function f
41+
@[direct_array_access]
4142
fn (mut d Digest) f(f bool) {
4243
// initialize the working vector
4344
mut v := []u32{len: 0, cap: 16}

vlib/strconv/atof.c.v

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,30 @@ fn converter(mut pn PrepNumber) u64 {
298298
s0 = q0
299299
}
300300
}
301+
302+
// Handle subnormal (denormalized) numbers - very small numbers near zero
303+
//
304+
// Normal floats have an implicit leading 1 bit in their mantissa (like 1.xxxxx).
305+
// When numbers get too small (binexp < -1022), we can't represent them normally.
306+
// Instead, we use subnormals: set exponent to 0 and shift the mantissa right,
307+
// losing precision gradually. This prevents abrupt underflow to zero.
308+
//
309+
// Example: 1.23e-308 is smaller than the minimum normal float, so we:
310+
// 1. Keep the normalized mantissa from s2 and s1
311+
// 2. Shift it right to "denormalize" it (the leading 1 moves into the mantissa)
312+
// 3. Round correctly using the bits that were shifted out
313+
// 4. Return with exponent = 0 (subnormal marker)
314+
if binexp < -1022 && (s2 | s1) != 0 {
315+
shift := -1022 - binexp
316+
if shift > 60 {
317+
return if pn.negative { double_minus_zero } else { double_plus_zero }
318+
}
319+
shifted := ((u64(s2) << 32) | u64(s1)) >> u32(shift)
320+
q := (shifted >> 8) +
321+
u64((shifted >> 7) & 1 != 0 && ((shifted & 0x7F) != 0 || (shifted >> 8) & 1 != 0))
322+
return (q & 0x000FFFFFFFFFFFFF) | (u64(pn.negative) << 63)
323+
}
324+
301325
// rounding if needed
302326
/*
303327
* "round half to even" algorithm
@@ -374,6 +398,8 @@ fn converter(mut pn PrepNumber) u64 {
374398
result = double_plus_infinity
375399
}
376400
} else if binexp < 1 {
401+
// Should not reach here for subnormals anymore (handled earlier)
402+
// This is now only for true zeros
377403
if pn.negative {
378404
result = double_minus_zero
379405
} else {

vlib/strconv/atof_test.c.v

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,29 @@ fn test_atof() {
8282
println('DONE!')
8383
}
8484

85+
fn test_atof_subnormal() {
86+
// Test subnormal (denormalized) float numbers and edge cases
87+
// These are very small numbers close to the f64 minimum
88+
// IMPORTANT: Compare with hardcoded f64 literals, not .f64() which uses the same parser
89+
90+
// Normal numbers
91+
assert strconv.atof64('1.0e-250') or { panic('parse error') } == 1.0e-250
92+
assert strconv.atof64('2.5e-260') or { panic('parse error') } == 2.5e-260
93+
94+
// Transition zone
95+
assert strconv.atof64('1.0e-300') or { panic('parse error') } == 1.0e-300
96+
assert strconv.atof64('2.2250738585072014e-308') or { panic('parse error') } == 2.2250738585072014e-308
97+
98+
// Subnormal numbers (these fail without the fix)
99+
assert strconv.atof64('1.23e-308') or { panic('parse error') } == 1.23e-308
100+
assert strconv.atof64('1.0e-310') or { panic('parse error') } == 1.0e-310
101+
assert strconv.atof64('5.0e-320') or { panic('parse error') } == 5.0e-320
102+
assert strconv.atof64('5e-324') or { panic('parse error') } == 5e-324
103+
104+
// Negative subnormal
105+
assert strconv.atof64('-1.0e-320') or { panic('parse error') } == -1.0e-320
106+
}
107+
85108
fn test_atof_errors() {
86109
if x := strconv.atof64('') {
87110
eprintln('> x: ${x}')

vlib/v/ast/table.v

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2837,3 +2837,8 @@ pub fn (mut t Table) get_veb_result_type_idx() int {
28372837
pub fn (mut t Table) register_vls_info(key string, val VlsInfo) {
28382838
t.vls_info[key] = val
28392839
}
2840+
2841+
pub fn (t &Table) unwrap(typ Type) Type {
2842+
ts := t.sym(typ)
2843+
return if ts.info is Alias { t.unwrap(ts.info.parent_type) } else { typ }
2844+
}

vlib/v/builder/builder_test.v

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,42 @@ fn test_conditional_executable_removal() {
4343
dump(after_second_run___)
4444
assert executable in after_second_run___
4545
}
46+
47+
fn test_file_list() {
48+
os.chdir(test_path)!
49+
os.mkdir_all('filelist')!
50+
os.write_file('filelist/main.v', 'module main
51+
fn main() {
52+
part_a := AS{}
53+
part_b := BS{}
54+
println("\${part_a}=>\${part_b}")
55+
}')!
56+
os.write_file('filelist/part_a.v', 'module main
57+
pub struct AS{}
58+
')!
59+
60+
os.write_file('filelist/part_b.v', 'module main
61+
pub struct BS{}
62+
')!
63+
64+
// `part_c.v` is not included in the compilation
65+
// or there will be a conflit definition of `struct BS`
66+
os.write_file('filelist/part_c.v', 'module main
67+
pub struct BS{}
68+
')!
69+
mut executable := 'filelist_check'
70+
$if windows {
71+
executable += '.exe'
72+
}
73+
74+
original_file_list_ := os.ls(test_path)!
75+
dump(original_file_list_)
76+
assert executable !in original_file_list_
77+
78+
cmd := '${os.quoted_path(vexe)} -o ${executable} filelist/main.v -file-list "filelist/part_a.v,filelist/part_b.v,"'
79+
os.execute(cmd)
80+
after_compile_file_list := os.ls(test_path)!.filter(os.exists(it))
81+
dump(after_compile_file_list)
82+
assert executable in after_compile_file_list
83+
assert os.execute('./${executable}').output.trim_space() == 'AS{}=>BS{}'
84+
}

vlib/v/builder/cc.v

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,13 @@ fn (mut v Builder) build_thirdparty_obj_file(mod string, path string, moduleflag
12481248
res := os.execute(cmd)
12491249
os.chdir(current_folder) or {}
12501250
if res.exit_code != 0 {
1251-
eprintln('failed thirdparty object build cmd:\n${cmd}')
1251+
eprintln('> Failed build_thirdparty_obj_file cmd')
1252+
eprintln('> mod: ${mod}')
1253+
eprintln('> path: ${path}')
1254+
eprintln('> cfile: ${cfile}')
1255+
eprintln('> wd before cmd: ${current_folder}')
1256+
eprintln('> getwd for cmd: ${v.pref.vroot}')
1257+
eprintln('> cmd: ${cmd}')
12521258
verror(res.output)
12531259
return
12541260
}

vlib/v/builder/compile.v

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,24 @@ pub fn (v &Builder) get_user_files() []string {
369369
user_files << single_test_v_file
370370
dir = os.dir(single_test_v_file)
371371
}
372+
v.add_file_or_dir(mut user_files, dir)
373+
for f in v.pref.file_list {
374+
file := f.trim_space()
375+
if file.len > 0 {
376+
v.add_file_or_dir(mut user_files, file)
377+
}
378+
}
379+
if user_files.len == 0 {
380+
println('No input .v files')
381+
exit(1)
382+
}
383+
if v.pref.is_verbose {
384+
v.log('user_files: ${user_files}')
385+
}
386+
return user_files
387+
}
388+
389+
fn (v &Builder) add_file_or_dir(mut user_files []string, dir string) {
372390
does_exist := os.exists(dir)
373391
if !does_exist {
374392
verror("${dir} doesn't exist")
@@ -381,7 +399,7 @@ pub fn (v &Builder) get_user_files() []string {
381399
// Just compile one file and get parent dir
382400
user_files << single_v_file
383401
if v.pref.is_verbose {
384-
v.log('> just compile one file: "${single_v_file}"')
402+
v.log('> add one file: "${single_v_file}"')
385403
}
386404
} else if os.is_dir(dir) {
387405
if v.pref.is_verbose {
@@ -395,12 +413,4 @@ pub fn (v &Builder) get_user_files() []string {
395413
println('unknown file extension `${ext}`')
396414
exit(1)
397415
}
398-
if user_files.len == 0 {
399-
println('No input .v files')
400-
exit(1)
401-
}
402-
if v.pref.is_verbose {
403-
v.log('user_files: ${user_files}')
404-
}
405-
return user_files
406416
}

0 commit comments

Comments
 (0)