diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 69c8dbd3a9..b3c4483e05 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1679,8 +1679,9 @@ fn (t Tree) concat_expr(node ast.ConcatExpr) &Node { fn (t Tree) type_of(node ast.TypeOf) &Node { mut obj := new_object() obj.add_terse('ast_type', t.string_node('TypeOf')) + obj.add_terse('is_type', t.bool_node(node.is_type)) + obj.add_terse('typ', t.type_node(node.typ)) obj.add_terse('expr', t.expr(node.expr)) - obj.add_terse('expr_type', t.type_node(node.expr_type)) obj.add('pos', t.pos(node.pos)) return obj } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 36480c383a..b90f0aba22 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1626,10 +1626,11 @@ pub mut: [minify] pub struct TypeOf { pub: - pos token.Pos + is_type bool + pos token.Pos pub mut: - expr Expr - expr_type Type + expr Expr // checker uses this to set typ + typ Type } [minify] diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index a8c710f4f3..af02b64744 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -453,7 +453,7 @@ pub fn (x Expr) str() string { if x.is_type { return 'sizeof(${global_table.type_to_str(x.typ)})' } - return 'sizeof(${x.expr})' + return 'sizeof(${x.expr.str()})' } OffsetOf { return '__offsetof(${global_table.type_to_str(x.struct_type)}, ${x.field})' @@ -487,6 +487,9 @@ pub fn (x Expr) str() string { return 'TypeNode(${x.typ})' } TypeOf { + if x.is_type { + return 'typeof[${global_table.type_to_str(x.typ)}]()' + } return 'typeof(${x.expr.str()})' } Likely { @@ -499,11 +502,10 @@ pub fn (x Expr) str() string { return 'none' } IsRefType { - return 'isreftype(' + if x.is_type { - global_table.type_to_str(x.typ) - } else { - x.expr.str() - } + ')' + if x.is_type { + return 'isreftype(${global_table.type_to_str(x.typ)})' + } + return 'isreftype(${x.expr.str()})' } IfGuardExpr { mut s := '' diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index eb2c7927e2..cebd4a5d1f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1151,10 +1151,13 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { name_type = ast.Type(c.table.find_type_idx(name)).set_flag(.generic) } } - // Note: in future typeof() should be a type known at compile-time - // sum types should not be handled dynamically ast.TypeOf { - name_type = c.expr(node.expr.expr) + // TODO: fix this weird case, since just `typeof(x)` is `string`, but `|typeof(x).| propertyname` should be the actual type, + // so that we can get other metadata properties of the type, depending on `propertyname` (one of `name` or `idx` for now). + // A better alternative would be a new `meta(x).propertyname`, that does not have a `meta(x)` case (an error), + // or if it does, it should be a normal constant struct value, just filled at comptime. + c.expr(node.expr) + name_type = node.expr.typ } else {} } @@ -2556,7 +2559,9 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type { return node.typ } ast.TypeOf { - node.expr_type = c.expr(node.expr) + if !node.is_type { + node.typ = c.expr(node.expr) + } return ast.string_type } ast.UnsafeExpr { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 1554943838..c628550214 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -2729,9 +2729,16 @@ pub fn (mut f Fmt) type_expr(node ast.TypeNode) { } pub fn (mut f Fmt) type_of(node ast.TypeOf) { - f.write('typeof(') - f.expr(node.expr) - f.write(')') + f.write('typeof') + if node.is_type { + f.write('[') + f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias)) + f.write(']()') + } else { + f.write('(') + f.expr(node.expr) + f.write(')') + } } pub fn (mut f Fmt) unsafe_expr(node ast.UnsafeExpr) { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 189555cae5..4601417024 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3501,10 +3501,10 @@ fn (mut g Gen) type_name(raw_type ast.Type) { } fn (mut g Gen) typeof_expr(node ast.TypeOf) { - typ := if node.expr_type == g.field_data_type { + typ := if node.typ == g.field_data_type { g.comptime_for_field_value.typ } else { - node.expr_type + node.typ } sym := g.table.sym(typ) if sym.kind == .sum_type { @@ -3544,6 +3544,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { return } .unknown { + // ast.TypeOf of `typeof(string).idx` etc if node.field_name == 'name' { // typeof(expr).name mut name_type := node.name_type @@ -3560,7 +3561,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { g.type_name(name_type) return } else if node.field_name == 'idx' { - // typeof(expr).idx + // `typeof(expr).idx` g.write(int(g.unwrap_generic(node.name_type)).str()) return } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 16f0dfa18b..fc3c43349f 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -3460,7 +3460,7 @@ fn (mut g JsGen) gen_struct_init(it ast.StructInit) { } fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) { - sym := g.table.sym(it.expr_type) + sym := g.table.sym(it.typ) if sym.kind == .sum_type { // TODO: JS sumtypes not implemented yet } else if sym.kind == .array_fixed { diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index d8bedaff10..75c0628e4e 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -625,7 +625,7 @@ fn (mut g Gen) get_sizeof_ident(ident ast.Ident) int { fn (mut g Gen) gen_typeof_expr(it ast.TypeOf, newline bool) { nl := if newline { '\n' } else { '' } - r := g.typ(it.expr_type).name + r := g.typ(it.typ).name g.learel(.rax, g.allocate_string('${r}${nl}', 3, .rel32)) } diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 10becc96c1..ac19d7a4a5 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -203,6 +203,36 @@ pub fn (mut p Parser) check_expr(precedence int) !ast.Expr { pos: pos } } + .key_typeof { + spos := p.tok.pos() + p.next() + if p.tok.kind == .lsbr { + p.check(.lsbr) + type_pos := p.tok.pos() + typ := p.parse_type() + p.check(.rsbr) + p.check(.lpar) + p.check(.rpar) + node = ast.TypeOf{ + is_type: true + typ: typ + pos: type_pos.extend(p.tok.pos()) + } + } else { + p.check(.lpar) + expr := p.expr(0) + p.check(.rpar) + if p.tok.kind != .dot && p.tok.line_nr == p.prev_tok.line_nr { + p.warn_with_pos('use e.g. `typeof(expr).name` or `sum_type_instance.type_name()` instead', + spos) + } + node = ast.TypeOf{ + is_type: false + expr: expr + pos: spos.extend(p.tok.pos()) + } + } + } .key_sizeof, .key_isreftype { is_reftype := p.tok.kind == .key_isreftype p.next() // sizeof @@ -258,21 +288,6 @@ pub fn (mut p Parser) check_expr(precedence int) !ast.Expr { } p.check(.rpar) } - .key_typeof { - spos := p.tok.pos() - p.next() - p.check(.lpar) - expr := p.expr(0) - p.check(.rpar) - if p.tok.kind != .dot && p.tok.line_nr == p.prev_tok.line_nr { - p.warn_with_pos('use e.g. `typeof(expr).name` or `sum_type_instance.type_name()` instead', - spos) - } - node = ast.TypeOf{ - expr: expr - pos: spos.extend(p.tok.pos()) - } - } .key_dump { spos := p.tok.pos() p.next() diff --git a/vlib/v/tests/typeof_type_test.v b/vlib/v/tests/typeof_type_test.v new file mode 100644 index 0000000000..f9250c9d39 --- /dev/null +++ b/vlib/v/tests/typeof_type_test.v @@ -0,0 +1,112 @@ +module main + +fn test_typeof_fn() { + assert typeof[fn (s string, x u32) (int, f32)]().name == 'fn (string, u32) (int, f32)' +} + +fn test_typeof_int() { + assert typeof[int]().idx == 7 + assert typeof[int]().name == 'int' +} + +fn test_typeof_u32() { + assert typeof[u32]().idx == 12 + assert typeof[u32]().name == 'u32' +} + +fn test_typeof_string() { + assert typeof[string]().idx == 20 + assert typeof[string]().name == 'string' +} + +fn test_typeof_optional_type() { + assert typeof[?string]().name == '?string' +} + +fn test_typeof_result_type() { + assert typeof[!string]().name == '!string' +} + +fn test_typeof_array_type() { + assert typeof[[]string]().name == '[]string' +} + +fn test_typeof_map_type() { + assert typeof[map[string]int]().name == 'map[string]int' +} + +// + +struct MyStruct {} + +struct MyGenericStruct[T] {} + +struct MyGenericStruct2[T, U] {} + +fn test_typeof_struct_type() { + assert typeof[MyStruct]().name == 'MyStruct' + assert typeof[MyGenericStruct]().name == 'MyGenericStruct' + assert typeof[MyGenericStruct[int]]().name == 'MyGenericStruct[int]' + assert typeof[MyGenericStruct[string]]().name == 'MyGenericStruct[string]' + assert typeof[MyGenericStruct2]().name == 'MyGenericStruct2' + assert typeof[MyGenericStruct2[string, int]]().name == 'MyGenericStruct2[string, int]' +} + +// + +union MyUnion { + x int + s string +} + +fn test_typeof_union_type() { + assert typeof[MyUnion]().name == 'MyUnion' +} + +// + +type Abc = int | string + +fn test_typeof_sumtype() { + assert typeof[Abc]().name == 'Abc' +} + +// + +enum EFoo { + a + b + c +} + +fn test_typeof_enum() { + assert typeof[EFoo]().name == 'EFoo' +} + +// + +type AnAlias = int + +fn test_typeof_alias() { + assert typeof[AnAlias]().name == 'AnAlias' +} + +// + +fn abc[T](x T) string { + return typeof[T]().name +} + +fn test_typeof_generic_type() { + assert abc[int](123) == 'int' + assert abc[string]('xyz') == 'string' +} + +// + +fn test_typeof_idx_comparison() { + i := 123 + u := u32(5) + assert typeof[int]().idx == typeof(i).idx + assert typeof[u32]().idx == typeof(u).idx +} diff --git a/vlib/x/json2/encoder.v b/vlib/x/json2/encoder.v index 57afb5cd29..e7720b3646 100644 --- a/vlib/x/json2/encoder.v +++ b/vlib/x/json2/encoder.v @@ -196,13 +196,13 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! { } } else { match field.unaliased_typ { - string_type_idx { + typeof[string]().idx { e.encode_string(val.$(field.name).str(), mut wr)! } - int_type_idx { + typeof[int]().idx { wr.write(val.$(field.name).str().bytes())! } - byte_array_type_idx { + typeof[[]byte]().idx { //! array e.encode_array(val.$(field.name), level, mut wr)! } diff --git a/vlib/x/json2/temporary_workaround_types_id.v b/vlib/x/json2/temporary_workaround_types_id.v deleted file mode 100644 index b7779574b4..0000000000 --- a/vlib/x/json2/temporary_workaround_types_id.v +++ /dev/null @@ -1,31 +0,0 @@ -module json2 - -fn gen_workaround[T](result T) T { - return result -} - -fn gen_workaround_result[T](result T) ?T { - return result -} - -fn gen_workaround_optional[T](result T) !T { - return result -} - -const ( - string_type_idx = typeof(gen_workaround[string](unsafe { nil })).idx - result_string_type_idx = typeof(gen_workaround_result[string](unsafe { nil })).idx - optional_string_type_idx = typeof(gen_workaround_optional[string](unsafe { nil })).idx - - int_type_idx = typeof(gen_workaround[int](unsafe { nil })).idx - result_int_type_idx = typeof(gen_workaround_result[int](unsafe { nil })).idx - optional_int_type_idx = typeof(gen_workaround_optional[int](unsafe { nil })).idx - - int_array_type_idx = typeof(gen_workaround[[]int](unsafe { nil })).idx - result_int_array_type_idx = typeof(gen_workaround_result[[]int](unsafe { nil })).idx - optional_int_array_type_idx = typeof(gen_workaround_optional[[]int](unsafe { nil })).idx - - byte_array_type_idx = typeof(gen_workaround[[]byte](unsafe { nil })).idx - result_byte_array_type_idx = typeof(gen_workaround_result[[]byte](unsafe { nil })).idx - optional_byte_array_type_idx = typeof(gen_workaround_optional[[]byte](unsafe { nil })).idx -)