Skip to content

Commit 3ed799e

Browse files
authored
checker: fix comptime evaluation on infix expr (fix #23341) (#23344)
1 parent 5eecd04 commit 3ed799e

11 files changed

Lines changed: 208 additions & 34 deletions

File tree

‎vlib/math/stats/stats.v‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ pub fn covariance_mean[T](data1 []T, data2 []T, mean1 T, mean2 T) T {
434434
for i in 0 .. n {
435435
delta1 := data1[i] - mean1
436436
delta2 := data2[i] - mean2
437-
covariance += T((delta1 * delta2 - covariance) / (i + T(1)))
437+
covariance += T((delta1 * delta2 - covariance) / (T(i) + T(1)))
438438
}
439439
return covariance
440440
}
@@ -459,8 +459,11 @@ pub fn lag1_autocorrelation_mean[T](data []T, mean T) T {
459459
for i := 1; i < data.len; i++ {
460460
delta0 := data[i - 1] - mean
461461
delta1 := data[i] - mean
462-
q += T((delta0 * delta1 - q) / (i + T(1)))
463-
v += T((delta1 * delta1 - v) / (T(i) + T(1)))
462+
d01 := delta0 * delta1
463+
d11 := delta1 * delta1
464+
ti1 := T(i) + T(1)
465+
q += T((d01 - q) / ti1)
466+
v += T((d11 - v) / ti1)
464467
}
465468
return T(q / v)
466469
}
@@ -486,7 +489,9 @@ pub fn kurtosis_mean_stddev[T](data []T, mean T, sd T) T {
486489
*/
487490
for i, v in data {
488491
x := (v - mean) / sd
489-
avg += T((x * x * x * x - avg) / (i + T(1)))
492+
x4 := x * x * x * x
493+
ti1 := (T(i) + T(1))
494+
avg += T((x4 - avg) / ti1)
490495
}
491496
return avg - T(3)
492497
}
@@ -511,7 +516,8 @@ pub fn skew_mean_stddev[T](data []T, mean T, sd T) T {
511516
*/
512517
for i, v in data {
513518
x := (v - mean) / sd
514-
skew += T((x * x * x - skew) / (i + T(1)))
519+
x3 := x * x * x
520+
skew += T((x3 - skew) / (T(i) + T(1)))
515521
}
516522
return skew
517523
}

‎vlib/math/stats/stats_test.v‎

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -400,15 +400,15 @@ fn test_covariance() {
400400
mut data0 := [10.0, 4.45, 5.9, 2.7]
401401
mut data1 := [5.0, 14.45, -15.9, 22.7]
402402
mut o := stats.covariance(data0, data1)
403-
assert math.alike(o, -17.37078207731247)
403+
assert math.alike(o, -17.37078125)
404404
data0 = [-3.0, 67.31, 4.4, 1.89]
405405
data1 = [5.0, 77.31, 44.4, 11.89]
406406
o = stats.covariance(data0, data1)
407-
assert math.alike(o, 740.0695419311523)
407+
assert math.alike(o, 740.06955)
408408
data0 = [12.0, 7.88, 76.122, 54.83]
409409
data1 = [2.0, 5.88, 7.122, 5.83]
410410
o = stats.covariance(data0, data1)
411-
assert math.alike(o, 36.65028190612793)
411+
assert math.alike(o, 36.650282000000004)
412412

413413
// test for int, i64, f32 array
414414
data0_int := [1, 2, 3, 1]
@@ -428,13 +428,15 @@ fn test_covariance() {
428428
fn test_lag1_autocorrelation() {
429429
mut data := [10.0, 4.45, 5.9, 2.7]
430430
mut o := stats.lag1_autocorrelation(data)
431-
assert math.alike(o, -0.554228566606572)
431+
mut e := 0.0
432+
assert math.alike(o, -0.5542285481446095)
432433
data = [-3.0, 67.31, 4.4, 1.89]
433434
o = stats.lag1_autocorrelation(data)
434-
assert math.alike(o, -0.5102510823460722)
435+
assert math.alike(o, -0.5102510654033415)
435436
data = [12.0, 7.88, 76.122, 54.83]
436437
o = stats.lag1_autocorrelation(data)
437-
assert math.alike(o, 0.10484451825170164)
438+
e = 0.10484450460892072
439+
assert math.alike(o, e), diff(o, e)
438440

439441
// test for int, i64, f32 array
440442
assert stats.lag1_autocorrelation[int]([1, 2, 3, 1]) == 0
@@ -443,54 +445,65 @@ fn test_lag1_autocorrelation() {
443445
assert math.alike(o, 0.1975308507680893)
444446
}
445447

448+
fn diff(actual f64, expected f64) string {
449+
return '\n actual:${actual:40.35f}\nexpected:${expected:40.35f}\n diff:${actual - expected:40.35f}'
450+
}
451+
446452
fn test_kurtosis() {
447453
mut data := [10.0, 4.45, 5.9, 2.7]
448454
mut o := stats.kurtosis(data)
449-
assert math.alike(o, -1.0443214689384779)
455+
mut e := -1.0443212849233845
456+
assert math.close(o, e), diff(o, e)
450457
data = [-3.0, 67.31, 4.4, 1.89]
451458
o = stats.kurtosis(data)
452-
assert math.alike(o, -0.688495594786176)
459+
e = -0.6884953374814851
460+
assert math.close(o, e), diff(o, e)
453461
data = [12.0, 7.88, 76.122, 54.83]
454462
o = stats.kurtosis(data)
455-
assert math.alike(o, -1.7323772574195067)
463+
assert math.alike(o, -1.7323772836921467)
456464

457465
// test for int, i64, f32 array
458466
assert stats.kurtosis[int]([1, 2, 3, 1]) == 1
459467
assert stats.kurtosis[i64]([i64(1), 2, 3, 1]) == 1
460468
o = stats.kurtosis[f32]([f32(1.0), 3, 5, 7, 3])
461-
assert math.alike(o, -1.0443782806396484)
469+
e = -1.044378399848938
470+
assert math.alike(o, e), diff(o, e)
462471
}
463472

464473
fn test_skew() {
465474
mut data := [10.0, 4.45, 5.9, 2.7]
466475
mut o := stats.skew(data)
467-
assert math.alike(o, 0.5754020379048158)
476+
mut e := 0.5754021106320453
477+
assert math.veryclose(o, e), diff(o, e)
468478
data = [-3.0, 67.31, 4.4, 1.89]
469479
o = stats.skew(data)
470-
assert math.alike(o, 1.1248732608899568)
480+
e = 1.1248733711136492
481+
assert math.veryclose(o, e), diff(o, e)
471482
data = [12.0, 7.88, 76.122, 54.83]
472483
o = stats.skew(data)
473-
assert math.alike(o, 0.19007917421924964)
484+
e = 0.19007911706827735
485+
assert math.alike(o, e), diff(o, e)
474486

475487
// test for int, i64, f32 array
476488
assert stats.skew[int]([1, 2, 3, 1]) == 2
477489
assert stats.skew[i64]([i64(1), 2, 3, 1]) == 2
478490
o = stats.skew[f32]([f32(1.0), 3, 5, 7, 3])
479-
assert math.alike(o, 0.2715454697608948)
491+
e = 0.27154541015625
492+
assert math.alike(o, e), diff(o, e)
480493
}
481494

482495
fn test_quantile() {
483496
// Assumes sorted array
484497

485498
mut data := [2.7, 4.45, 5.9, 10.0]
486499
mut o := stats.quantile(data, 0.1)!
487-
assert math.alike(o, 3.225000020861626)
500+
assert math.alike(o, 3.225)
488501
data = [-3.0, 1.89, 4.4, 67.31]
489502
o = stats.quantile(data, 0.2)!
490-
assert math.alike(o, -0.06599988341331486)
503+
assert math.alike(o, -0.06599999999999961)
491504
data = [7.88, 12.0, 54.83, 76.122]
492505
o = stats.quantile(data, 0.3)!
493-
assert math.alike(o, 11.587999901771546)
506+
assert math.alike(o, 11.588)
494507

495508
stats.quantile(data, -0.3) or { assert err.msg() == 'index out of range' }
496509

‎vlib/strconv/format.v‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@ const dec_round = [
4343
0.000000000000000005,
4444
0.0000000000000000005,
4545
0.00000000000000000005,
46+
0.000000000000000000005,
47+
0.0000000000000000000005,
48+
0.00000000000000000000005,
49+
0.000000000000000000000005,
50+
0.0000000000000000000000005,
51+
0.00000000000000000000000005,
52+
0.000000000000000000000000005,
53+
0.0000000000000000000000000005,
54+
0.00000000000000000000000000005,
55+
0.000000000000000000000000000005,
56+
0.0000000000000000000000000000005,
57+
0.00000000000000000000000000000005,
58+
0.000000000000000000000000000000005,
59+
0.0000000000000000000000000000000005,
60+
0.00000000000000000000000000000000005,
61+
0.000000000000000000000000000000000005,
4662
]!
4763

4864
// Single format functions

‎vlib/strconv/format_mem.c.v‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
209209

210210
// allocate exp+32 chars for the return string
211211
// mut res := []u8{len:exp+32,init:`0`}
212-
mut res := []u8{len: exp + 32, init: 0}
212+
mut res := []u8{len: exp + 40, init: 0}
213213
mut r_i := 0 // result string buffer index
214214

215215
// println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")

‎vlib/v/checker/assign.v‎

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
2323
node.left_types = []
2424
mut right_len := node.right.len
2525
mut right_first_type := ast.void_type
26+
old_recheck := c.inside_recheck
27+
// check if we are rechecking an already checked expression on generic rechecking
28+
c.inside_recheck = old_recheck || node.right_types.len > 0
29+
defer {
30+
c.inside_recheck = old_recheck
31+
}
2632
for i, mut right in node.right {
2733
if right in [ast.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr, ast.DumpExpr,
2834
ast.SelectorExpr, ast.ParExpr, ast.ComptimeCall] {
@@ -37,7 +43,9 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
3743
}
3844
right_type_sym := c.table.sym(right_type)
3945
// fixed array returns an struct, but when assigning it must be the array type
40-
right_type = c.cast_fixed_array_ret(right_type, right_type_sym)
46+
if right_type_sym.info is ast.ArrayFixed {
47+
right_type = c.cast_fixed_array_ret(right_type, right_type_sym)
48+
}
4149
if i == 0 {
4250
right_first_type = right_type
4351
node.right_types = [
@@ -62,11 +70,23 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
6270
}
6371
}
6472
}
65-
}
66-
if mut right is ast.InfixExpr {
73+
} else if mut right is ast.InfixExpr {
6774
if right.op == .arrow {
6875
c.error('cannot use `<-` on the right-hand side of an assignment, as it does not return any values',
6976
right.pos)
77+
} else if c.inside_recheck {
78+
mut right_type := c.expr(mut right)
79+
right_type_sym := c.table.sym(right_type)
80+
// fixed array returns an struct, but when assigning it must be the array type
81+
if right_type_sym.info is ast.ArrayFixed {
82+
right_type = c.cast_fixed_array_ret(right_type, right_type_sym)
83+
}
84+
if i == 0 {
85+
right_first_type = right_type
86+
node.right_types = [
87+
c.check_expr_option_or_result_call(right, right_first_type),
88+
]
89+
}
7090
}
7191
}
7292
if mut right is ast.Ident {
@@ -182,16 +202,13 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
182202
node.right_types << c.check_expr_option_or_result_call(node.right[i],
183203
right_type)
184204
}
185-
} else {
205+
} else if c.inside_recheck {
186206
// on generic recheck phase it might be needed to resolve the rhs again
187207
if i < node.right.len && c.comptime.has_comptime_expr(node.right[i]) {
188208
mut expr := mut node.right[i]
189-
old_inside_recheck := c.inside_recheck
190-
c.inside_recheck = true
191209
right_type := c.expr(mut expr)
192210
node.right_types[i] = c.check_expr_option_or_result_call(node.right[i],
193211
right_type)
194-
c.inside_recheck = old_inside_recheck
195212
}
196213
}
197214
mut right := if i < node.right.len { node.right[i] } else { node.right[0] }
@@ -393,6 +410,17 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
393410
left.obj.ct_type_var = .field_var
394411
left.obj.typ = c.comptime.comptime_for_field_type
395412
}
413+
} else if mut right is ast.InfixExpr {
414+
right_ct_var := c.comptime.get_ct_type_var(right.left)
415+
if right_ct_var != .no_comptime {
416+
left.obj.ct_type_var = right_ct_var
417+
}
418+
} else if mut right is ast.IndexExpr
419+
&& c.comptime.is_comptime(right) {
420+
right_ct_var := c.comptime.get_ct_type_var(right.left)
421+
if right_ct_var != .no_comptime {
422+
left.obj.ct_type_var = right_ct_var
423+
}
396424
} else if mut right is ast.Ident && right.obj is ast.Var
397425
&& right.or_expr.kind == .absent {
398426
right_obj_var := right.obj as ast.Var

‎vlib/v/gen/c/assign.v‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,19 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
371371
left.obj.typ = var_type
372372
g.assign_ct_type = var_type
373373
}
374+
} else if val is ast.InfixExpr && val.op in [.plus, .minus, .mul, .div, .mod]
375+
&& g.comptime.is_comptime(val.left) {
376+
ctyp := g.unwrap_generic(g.type_resolver.get_type(val.left))
377+
if ctyp != ast.void_type {
378+
ct_type_var := g.comptime.get_ct_type_var(val.left)
379+
if ct_type_var in [.key_var, .value_var] {
380+
g.type_resolver.update_ct_type(left.name, g.unwrap_generic(ctyp))
381+
}
382+
var_type = ctyp
383+
val_type = var_type
384+
left.obj.typ = var_type
385+
g.assign_ct_type = var_type
386+
}
374387
}
375388
is_auto_heap = left.obj.is_auto_heap
376389
}

‎vlib/v/gen/c/infix.v‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,8 +1188,10 @@ fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) {
11881188
&& node.op in [.plus, .minus, .mul, .div, .mod] && !(g.pref.translated
11891189
|| g.file.is_translated)
11901190
if needs_cast {
1191-
typ_str := if g.comptime.is_comptime(node.left) {
1191+
typ_str := if !node.left.is_literal() && g.comptime.is_comptime(node.left) {
11921192
g.styp(g.type_resolver.get_type_or_default(node.left, node.promoted_type))
1193+
} else if !node.right.is_literal() && g.comptime.is_comptime(node.right) {
1194+
g.styp(g.type_resolver.get_type_or_default(node.right, node.promoted_type))
11931195
} else {
11941196
g.styp(node.promoted_type)
11951197
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
enum Flag {
2+
usa_old_glory
3+
all_other_bad_excuses_for_a_flag
4+
}
5+
6+
struct Test {
7+
is_foo bool
8+
name [5]u8
9+
}
10+
11+
fn enc[T](item T) string {
12+
$if T is $int {
13+
len := match typeof(item).name {
14+
'i8', 'u8' { u8(2) }
15+
'i16', 'u16' { 4 }
16+
'int', 'u32', 'i32' { 8 }
17+
'i64', 'u64' { 16 }
18+
else { return '' }
19+
}
20+
return u64_to_hex(item, len)
21+
} $else $if T is $array {
22+
mut hex := ''
23+
for val in item {
24+
hex += enc(val)
25+
}
26+
return hex
27+
} $else $if T is $struct {
28+
mut hex := ''
29+
$for field in T.fields {
30+
hex += enc(item.$(field.name)) + '_'
31+
}
32+
return hex
33+
} $else {
34+
if typeof(item).name == 'bool' {
35+
return enc(int(item))
36+
}
37+
$if debug {
38+
println('cannot encode ${T}(s)')
39+
}
40+
return ''
41+
}
42+
}
43+
44+
@[direct_array_access; inline]
45+
fn u64_to_hex(nn u64, len u8) string {
46+
mut n := nn
47+
mut buf := [17]u8{}
48+
buf[len] = 0
49+
mut i := 0
50+
for i = len - 1; i >= 0; i-- {
51+
d := u8(n & 0xF)
52+
buf[i] = if d < 10 { d + `0` } else { d + 87 }
53+
n = n >> 4
54+
}
55+
return unsafe { tos(memdup(&buf[0], len + 1), len) }
56+
}
57+
58+
fn test_main() {
59+
assert enc(Test{}) == '00000000_0000000000_'
60+
assert enc(Test{ is_foo: true }) == '00000001_0000000000_'
61+
assert enc(Test{ name: [u8(1), 2, 3, 4, 5]! }) == '00000000_0102030405_'
62+
}

‎vlib/v/type_resolver/comptime_resolver.v‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ pub fn (t &ResolverInfo) is_comptime(node ast.Expr) bool {
5050
return node.expr is ast.Ident && node.expr.ct_expr
5151
}
5252
ast.InfixExpr {
53-
return t.is_comptime(node.left) || t.is_comptime(node.right)
53+
if node.op in [.plus, .minus, .mul, .div, .mod] {
54+
t.is_comptime(node.left) || t.is_comptime(node.right)
55+
} else {
56+
false
57+
}
5458
}
5559
ast.ParExpr {
5660
return t.is_comptime(node.expr)
@@ -76,6 +80,10 @@ pub fn (t &ResolverInfo) get_ct_type_var(node ast.Expr) ast.ComptimeVarKind {
7680
}
7781
} else if node is ast.IndexExpr {
7882
return t.get_ct_type_var(node.left)
83+
} else if node is ast.InfixExpr {
84+
return t.get_ct_type_var(node.left)
85+
} else if node is ast.ParExpr {
86+
return t.get_ct_type_var(node.expr)
7987
}
8088
return .no_comptime
8189
}

0 commit comments

Comments
 (0)