Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checker: fix comptime evaluation on infix expr #23344

Merged
merged 17 commits into from
Jan 3, 2025
16 changes: 11 additions & 5 deletions vlib/math/stats/stats.v
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ pub fn covariance_mean[T](data1 []T, data2 []T, mean1 T, mean2 T) T {
for i in 0 .. n {
delta1 := data1[i] - mean1
delta2 := data2[i] - mean2
covariance += T((delta1 * delta2 - covariance) / (i + T(1)))
covariance += T((delta1 * delta2 - covariance) / (T(i) + T(1)))
}
return covariance
}
Expand All @@ -459,8 +459,11 @@ pub fn lag1_autocorrelation_mean[T](data []T, mean T) T {
for i := 1; i < data.len; i++ {
delta0 := data[i - 1] - mean
delta1 := data[i] - mean
q += T((delta0 * delta1 - q) / (i + T(1)))
v += T((delta1 * delta1 - v) / (T(i) + T(1)))
d01 := delta0 * delta1
d11 := delta1 * delta1
ti1 := T(i) + T(1)
q += T((d01 - q) / ti1)
v += T((d11 - v) / ti1)
}
return T(q / v)
}
Expand All @@ -486,7 +489,9 @@ pub fn kurtosis_mean_stddev[T](data []T, mean T, sd T) T {
*/
for i, v in data {
x := (v - mean) / sd
avg += T((x * x * x * x - avg) / (i + T(1)))
x4 := x * x * x * x
ti1 := (T(i) + T(1))
avg += T((x4 - avg) / ti1)
}
return avg - T(3)
}
Expand All @@ -511,7 +516,8 @@ pub fn skew_mean_stddev[T](data []T, mean T, sd T) T {
*/
for i, v in data {
x := (v - mean) / sd
skew += T((x * x * x - skew) / (i + T(1)))
x3 := x * x * x
skew += T((x3 - skew) / (T(i) + T(1)))
}
return skew
}
Expand Down
47 changes: 30 additions & 17 deletions vlib/math/stats/stats_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -400,15 +400,15 @@ fn test_covariance() {
mut data0 := [10.0, 4.45, 5.9, 2.7]
mut data1 := [5.0, 14.45, -15.9, 22.7]
mut o := stats.covariance(data0, data1)
assert math.alike(o, -17.37078207731247)
assert math.alike(o, -17.37078125)
data0 = [-3.0, 67.31, 4.4, 1.89]
data1 = [5.0, 77.31, 44.4, 11.89]
o = stats.covariance(data0, data1)
assert math.alike(o, 740.0695419311523)
assert math.alike(o, 740.06955)
data0 = [12.0, 7.88, 76.122, 54.83]
data1 = [2.0, 5.88, 7.122, 5.83]
o = stats.covariance(data0, data1)
assert math.alike(o, 36.65028190612793)
assert math.alike(o, 36.650282000000004)

// test for int, i64, f32 array
data0_int := [1, 2, 3, 1]
Expand All @@ -428,13 +428,15 @@ fn test_covariance() {
fn test_lag1_autocorrelation() {
mut data := [10.0, 4.45, 5.9, 2.7]
mut o := stats.lag1_autocorrelation(data)
assert math.alike(o, -0.554228566606572)
mut e := 0.0
assert math.alike(o, -0.5542285481446095)
data = [-3.0, 67.31, 4.4, 1.89]
o = stats.lag1_autocorrelation(data)
assert math.alike(o, -0.5102510823460722)
assert math.alike(o, -0.5102510654033415)
data = [12.0, 7.88, 76.122, 54.83]
o = stats.lag1_autocorrelation(data)
assert math.alike(o, 0.10484451825170164)
e = 0.10484450460892072
assert math.alike(o, e), diff(o, e)

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

fn diff(actual f64, expected f64) string {
return '\n actual:${actual:40.35f}\nexpected:${expected:40.35f}\n diff:${actual - expected:40.35f}'
}

fn test_kurtosis() {
mut data := [10.0, 4.45, 5.9, 2.7]
mut o := stats.kurtosis(data)
assert math.alike(o, -1.0443214689384779)
mut e := -1.0443212849233845
assert math.close(o, e), diff(o, e)
data = [-3.0, 67.31, 4.4, 1.89]
o = stats.kurtosis(data)
assert math.alike(o, -0.688495594786176)
e = -0.6884953374814851
assert math.close(o, e), diff(o, e)
data = [12.0, 7.88, 76.122, 54.83]
o = stats.kurtosis(data)
assert math.alike(o, -1.7323772574195067)
assert math.alike(o, -1.7323772836921467)

// test for int, i64, f32 array
assert stats.kurtosis[int]([1, 2, 3, 1]) == 1
assert stats.kurtosis[i64]([i64(1), 2, 3, 1]) == 1
o = stats.kurtosis[f32]([f32(1.0), 3, 5, 7, 3])
assert math.alike(o, -1.0443782806396484)
e = -1.044378399848938
assert math.alike(o, e), diff(o, e)
}

fn test_skew() {
mut data := [10.0, 4.45, 5.9, 2.7]
mut o := stats.skew(data)
assert math.alike(o, 0.5754020379048158)
mut e := 0.5754021106320453
assert math.veryclose(o, e), diff(o, e)
data = [-3.0, 67.31, 4.4, 1.89]
o = stats.skew(data)
assert math.alike(o, 1.1248732608899568)
e = 1.1248733711136492
assert math.veryclose(o, e), diff(o, e)
data = [12.0, 7.88, 76.122, 54.83]
o = stats.skew(data)
assert math.alike(o, 0.19007917421924964)
e = 0.19007911706827735
assert math.alike(o, e), diff(o, e)

// test for int, i64, f32 array
assert stats.skew[int]([1, 2, 3, 1]) == 2
assert stats.skew[i64]([i64(1), 2, 3, 1]) == 2
o = stats.skew[f32]([f32(1.0), 3, 5, 7, 3])
assert math.alike(o, 0.2715454697608948)
e = 0.27154541015625
assert math.alike(o, e), diff(o, e)
}

fn test_quantile() {
// Assumes sorted array

mut data := [2.7, 4.45, 5.9, 10.0]
mut o := stats.quantile(data, 0.1)!
assert math.alike(o, 3.225000020861626)
assert math.alike(o, 3.225)
data = [-3.0, 1.89, 4.4, 67.31]
o = stats.quantile(data, 0.2)!
assert math.alike(o, -0.06599988341331486)
assert math.alike(o, -0.06599999999999961)
data = [7.88, 12.0, 54.83, 76.122]
o = stats.quantile(data, 0.3)!
assert math.alike(o, 11.587999901771546)
assert math.alike(o, 11.588)

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

Expand Down
16 changes: 16 additions & 0 deletions vlib/strconv/format.v
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ const dec_round = [
0.000000000000000005,
0.0000000000000000005,
0.00000000000000000005,
0.000000000000000000005,
0.0000000000000000000005,
0.00000000000000000000005,
0.000000000000000000000005,
0.0000000000000000000000005,
0.00000000000000000000000005,
0.000000000000000000000000005,
0.0000000000000000000000000005,
0.00000000000000000000000000005,
0.000000000000000000000000000005,
0.0000000000000000000000000000005,
0.00000000000000000000000000000005,
0.000000000000000000000000000000005,
0.0000000000000000000000000000000005,
0.00000000000000000000000000000000005,
0.000000000000000000000000000000000005,
]!

// Single format functions
Expand Down
2 changes: 1 addition & 1 deletion vlib/strconv/format_mem.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ pub fn f64_to_str_lnd1(f f64, dec_digit int) string {

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

// println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")
Expand Down
42 changes: 35 additions & 7 deletions vlib/v/checker/assign.v
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
node.left_types = []
mut right_len := node.right.len
mut right_first_type := ast.void_type
old_recheck := c.inside_recheck
// check if we are rechecking an already checked expression on generic rechecking
c.inside_recheck = old_recheck || node.right_types.len > 0
defer {
c.inside_recheck = old_recheck
}
for i, mut right in node.right {
if right in [ast.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr, ast.DumpExpr,
ast.SelectorExpr, ast.ParExpr, ast.ComptimeCall] {
Expand All @@ -37,7 +43,9 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
}
right_type_sym := c.table.sym(right_type)
// fixed array returns an struct, but when assigning it must be the array type
right_type = c.cast_fixed_array_ret(right_type, right_type_sym)
if right_type_sym.info is ast.ArrayFixed {
right_type = c.cast_fixed_array_ret(right_type, right_type_sym)
}
if i == 0 {
right_first_type = right_type
node.right_types = [
Expand All @@ -62,11 +70,23 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
}
}
}
}
if mut right is ast.InfixExpr {
} else if mut right is ast.InfixExpr {
if right.op == .arrow {
c.error('cannot use `<-` on the right-hand side of an assignment, as it does not return any values',
right.pos)
} else if c.inside_recheck {
mut right_type := c.expr(mut right)
right_type_sym := c.table.sym(right_type)
// fixed array returns an struct, but when assigning it must be the array type
if right_type_sym.info is ast.ArrayFixed {
right_type = c.cast_fixed_array_ret(right_type, right_type_sym)
}
if i == 0 {
right_first_type = right_type
node.right_types = [
c.check_expr_option_or_result_call(right, right_first_type),
]
}
}
}
if mut right is ast.Ident {
Expand Down Expand Up @@ -182,16 +202,13 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
node.right_types << c.check_expr_option_or_result_call(node.right[i],
right_type)
}
} else {
} else if c.inside_recheck {
// on generic recheck phase it might be needed to resolve the rhs again
if i < node.right.len && c.comptime.has_comptime_expr(node.right[i]) {
mut expr := mut node.right[i]
old_inside_recheck := c.inside_recheck
c.inside_recheck = true
right_type := c.expr(mut expr)
node.right_types[i] = c.check_expr_option_or_result_call(node.right[i],
right_type)
c.inside_recheck = old_inside_recheck
}
}
mut right := if i < node.right.len { node.right[i] } else { node.right[0] }
Expand Down Expand Up @@ -393,6 +410,17 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
left.obj.ct_type_var = .field_var
left.obj.typ = c.comptime.comptime_for_field_type
}
} else if mut right is ast.InfixExpr {
right_ct_var := c.comptime.get_ct_type_var(right.left)
if right_ct_var != .no_comptime {
left.obj.ct_type_var = right_ct_var
}
} else if mut right is ast.IndexExpr
&& c.comptime.is_comptime(right) {
right_ct_var := c.comptime.get_ct_type_var(right.left)
if right_ct_var != .no_comptime {
left.obj.ct_type_var = right_ct_var
}
} else if mut right is ast.Ident && right.obj is ast.Var
&& right.or_expr.kind == .absent {
right_obj_var := right.obj as ast.Var
Expand Down
13 changes: 13 additions & 0 deletions vlib/v/gen/c/assign.v
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,19 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
left.obj.typ = var_type
g.assign_ct_type = var_type
}
} else if val is ast.InfixExpr && val.op in [.plus, .minus, .mul, .div, .mod]
&& g.comptime.is_comptime(val.left) {
ctyp := g.unwrap_generic(g.type_resolver.get_type(val.left))
if ctyp != ast.void_type {
ct_type_var := g.comptime.get_ct_type_var(val.left)
if ct_type_var in [.key_var, .value_var] {
g.type_resolver.update_ct_type(left.name, g.unwrap_generic(ctyp))
}
var_type = ctyp
val_type = var_type
left.obj.typ = var_type
g.assign_ct_type = var_type
}
}
is_auto_heap = left.obj.is_auto_heap
}
Expand Down
4 changes: 3 additions & 1 deletion vlib/v/gen/c/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -1188,8 +1188,10 @@ fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) {
&& node.op in [.plus, .minus, .mul, .div, .mod] && !(g.pref.translated
|| g.file.is_translated)
if needs_cast {
typ_str := if g.comptime.is_comptime(node.left) {
typ_str := if !node.left.is_literal() && g.comptime.is_comptime(node.left) {
g.styp(g.type_resolver.get_type_or_default(node.left, node.promoted_type))
} else if !node.right.is_literal() && g.comptime.is_comptime(node.right) {
g.styp(g.type_resolver.get_type_or_default(node.right, node.promoted_type))
} else {
g.styp(node.promoted_type)
}
Expand Down
62 changes: 62 additions & 0 deletions vlib/v/tests/comptime/comptime_infix_assign_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
enum Flag {
usa_old_glory
all_other_bad_excuses_for_a_flag
}

struct Test {
is_foo bool
name [5]u8
}

fn enc[T](item T) string {
$if T is $int {
len := match typeof(item).name {
'i8', 'u8' { u8(2) }
'i16', 'u16' { 4 }
'int', 'u32', 'i32' { 8 }
'i64', 'u64' { 16 }
else { return '' }
}
return u64_to_hex(item, len)
} $else $if T is $array {
mut hex := ''
for val in item {
hex += enc(val)
}
return hex
} $else $if T is $struct {
mut hex := ''
$for field in T.fields {
hex += enc(item.$(field.name)) + '_'
}
return hex
} $else {
if typeof(item).name == 'bool' {
return enc(int(item))
}
$if debug {
println('cannot encode ${T}(s)')
}
return ''
}
}

@[direct_array_access; inline]
fn u64_to_hex(nn u64, len u8) string {
mut n := nn
mut buf := [17]u8{}
buf[len] = 0
mut i := 0
for i = len - 1; i >= 0; i-- {
d := u8(n & 0xF)
buf[i] = if d < 10 { d + `0` } else { d + 87 }
n = n >> 4
}
return unsafe { tos(memdup(&buf[0], len + 1), len) }
}

fn test_main() {
assert enc(Test{}) == '00000000_0000000000_'
assert enc(Test{ is_foo: true }) == '00000001_0000000000_'
assert enc(Test{ name: [u8(1), 2, 3, 4, 5]! }) == '00000000_0102030405_'
}
10 changes: 9 additions & 1 deletion vlib/v/type_resolver/comptime_resolver.v
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ pub fn (t &ResolverInfo) is_comptime(node ast.Expr) bool {
return node.expr is ast.Ident && node.expr.ct_expr
}
ast.InfixExpr {
return t.is_comptime(node.left) || t.is_comptime(node.right)
if node.op in [.plus, .minus, .mul, .div, .mod] {
t.is_comptime(node.left) || t.is_comptime(node.right)
} else {
false
}
}
ast.ParExpr {
return t.is_comptime(node.expr)
Expand All @@ -76,6 +80,10 @@ pub fn (t &ResolverInfo) get_ct_type_var(node ast.Expr) ast.ComptimeVarKind {
}
} else if node is ast.IndexExpr {
return t.get_ct_type_var(node.left)
} else if node is ast.InfixExpr {
return t.get_ct_type_var(node.left)
} else if node is ast.ParExpr {
return t.get_ct_type_var(node.expr)
}
return .no_comptime
}
Expand Down
Loading
Loading