From 900c37aa6564bdd76909ad830be5e5c2dda10709 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 14 Aug 2021 08:48:25 +0300 Subject: [PATCH] v.gen.c: support `T.typ` - an int for the type index of the generic type `T` (#11175) --- cmd/tools/vast/vast.v | 1 + vlib/math/math.v | 40 ++++++++++++++++++++++ vlib/math/math_test.v | 40 ---------------------- vlib/v/ast/ast.v | 7 ++++ vlib/v/checker/checker.v | 19 ++++++++--- vlib/v/gen/c/cgen.v | 20 +++++++++-- vlib/v/parser/parser.v | 6 ++++ vlib/v/tests/generics_T_typ_test.v | 55 ++++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+), 46 deletions(-) create mode 100644 vlib/v/tests/generics_T_typ_test.v diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index fe2d5da891..2fdccdcfaa 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1325,6 +1325,7 @@ fn (t Tree) selector_expr(node ast.SelectorExpr) &Node { obj.add('field_name', t.string_node(node.field_name)) obj.add('typ', t.type_node(node.typ)) obj.add('name_type', t.type_node(node.name_type)) + obj.add('gkind_field', t.enum_node(node.gkind_field)) obj.add('from_embed_type', t.type_node(node.from_embed_type)) obj.add('next_token', t.token_node(node.next_token)) obj.add('pos', t.position(node.pos)) diff --git a/vlib/math/math.v b/vlib/math/math.v index f102f39257..5e36425b6a 100644 --- a/vlib/math/math.v +++ b/vlib/math/math.v @@ -146,3 +146,43 @@ pub fn radians(degrees f64) f64 { pub fn signbit(x f64) bool { return f64_bits(x) & sign_mask != 0 } + +pub fn tolerance(a f64, b f64, tol f64) bool { + mut ee := tol + // Multiplying by ee here can underflow denormal values to zero. + // Check a==b so that at least if a and b are small and identical + // we say they match. + if a == b { + return true + } + mut d := a - b + if d < 0 { + d = -d + } + // note: b is correct (expected) value, a is actual value. + // make error tolerance a fraction of b, not a. + if b != 0 { + ee = ee * b + if ee < 0 { + ee = -ee + } + } + return d < ee +} + +pub fn close(a f64, b f64) bool { + return tolerance(a, b, 1e-14) +} + +pub fn veryclose(a f64, b f64) bool { + return tolerance(a, b, 4e-16) +} + +pub fn alike(a f64, b f64) bool { + if is_nan(a) && is_nan(b) { + return true + } else if a == b { + return signbit(a) == signbit(b) + } + return false +} diff --git a/vlib/math/math_test.v b/vlib/math/math_test.v index c0cd7d8fa6..2f99353fa6 100644 --- a/vlib/math/math_test.v +++ b/vlib/math/math_test.v @@ -186,50 +186,10 @@ const ( ] ) -fn tolerance(a f64, b f64, tol f64) bool { - mut ee := tol - // Multiplying by ee here can underflow denormal values to zero. - // Check a==b so that at least if a and b are small and identical - // we say they match. - if a == b { - return true - } - mut d := a - b - if d < 0 { - d = -d - } - // note: b is correct (expected) value, a is actual value. - // make error tolerance a fraction of b, not a. - if b != 0 { - ee = ee * b - if ee < 0 { - ee = -ee - } - } - return d < ee -} - -fn close(a f64, b f64) bool { - return tolerance(a, b, 1e-14) -} - -fn veryclose(a f64, b f64) bool { - return tolerance(a, b, 4e-16) -} - fn soclose(a f64, b f64, e f64) bool { return tolerance(a, b, e) } -fn alike(a f64, b f64) bool { - if is_nan(a) && is_nan(b) { - return true - } else if a == b { - return signbit(a) == signbit(b) - } - return false -} - fn test_nan() { nan_f64 := nan() assert nan_f64 != nan_f64 diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index fac9fd2925..d2c65f6a9b 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -129,6 +129,12 @@ pub: pos token.Position } +pub enum GenericKindField { + unknown + name + typ +} + // `foo.bar` pub struct SelectorExpr { pub: @@ -142,6 +148,7 @@ pub mut: expr_type Type // type of `Foo` in `Foo.bar` typ Type // type of the entire thing (`Foo.bar`) name_type Type // T in `T.name` or typeof in `typeof(expr).name` + gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown scope &Scope from_embed_type Type // holds the type of the embed that the method is called from } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7bcb33675f..8c65ad2688 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3365,11 +3365,22 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { else {} } if name_type > 0 { - if node.field_name != 'name' { - c.error('invalid field `.$node.field_name` for type `$node.expr`', node.pos) - } node.name_type = name_type - return ast.string_type + match node.gkind_field { + .name { + return ast.string_type + } + .typ { + return ast.int_type + } + else { + if node.field_name != 'name' { + c.error('invalid field `.$node.field_name` for type `$node.expr`', + node.pos) + } + return ast.string_type + } + } } old_selector_expr := c.inside_selector_expr diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 7d417025cd..d483e75d0a 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3649,8 +3649,24 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once g.prevent_sum_type_unwrapping_once = false if node.name_type > 0 { - g.type_name(node.name_type) - return + match node.gkind_field { + .name { + g.type_name(node.name_type) + return + } + .typ { + g.write(int(g.unwrap_generic(node.name_type)).str()) + return + } + .unknown { + if node.field_name == 'name' { + // typeof(expr).name + g.type_name(node.name_type) + return + } + g.error('unknown generic field', node.pos) + } + } } if node.expr_type == 0 { g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos) diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 29cd2696cd..c0bba4f84d 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2158,6 +2158,11 @@ pub fn (mut p Parser) name_expr() ast.Expr { name := p.check_name() p.check(.dot) field := p.check_name() + fkind := match field { + 'name' { ast.GenericKindField.name } + 'typ' { ast.GenericKindField.typ } + else { ast.GenericKindField.unknown } + } pos.extend(p.tok.position()) return ast.SelectorExpr{ expr: ast.Ident{ @@ -2165,6 +2170,7 @@ pub fn (mut p Parser) name_expr() ast.Expr { scope: p.scope } field_name: field + gkind_field: fkind pos: pos scope: p.scope } diff --git a/vlib/v/tests/generics_T_typ_test.v b/vlib/v/tests/generics_T_typ_test.v new file mode 100644 index 0000000000..084f8a46ee --- /dev/null +++ b/vlib/v/tests/generics_T_typ_test.v @@ -0,0 +1,55 @@ +import math + +struct Any { +mut: + data voidptr + typ int +} + +fn make_any(obj T) Any { + tsize := int(sizeof(T)) + mut a := Any{ + typ: T.typ + data: unsafe { malloc(tsize) } + } + unsafe { + vmemcpy(a.data, &obj, tsize) + } + return a +} + +fn cast(obj Any) ?T { + if T.typ == obj.typ { + return *&T(obj.data) + } + return none +} + +fn test_any_values() { + arr := [make_any(true), make_any(false), make_any(7), make_any('cat'), + make_any([3.1415926535]), + ] + for elm in arr { + if b := cast(elm) { + println(!b) + } else if i := cast(elm) { + println(i + 1) + } else if s := cast(elm) { + println(s + '!') + } else if f := cast<[]f64>(elm) { + println(f[0]) + } + } + if b := cast(arr[0]) { + assert b == true + } + if b := cast(arr[1]) { + assert b == false + } + if s := cast(arr[3]) { + assert s == 'cat' + } + if f := cast<[]f64>(arr[4]) { + assert math.veryclose(f[0], 3.1415926535) + } +}