mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: support typeof[ T ]().idx and typeof[ T ]().name, where T can be any type, including ![]&string
(#16513)
This commit is contained in:
parent
bb705c01d2
commit
48a7de643e
@ -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
|
||||
}
|
||||
|
@ -1626,10 +1626,11 @@ pub mut:
|
||||
[minify]
|
||||
pub struct TypeOf {
|
||||
pub:
|
||||
is_type bool
|
||||
pos token.Pos
|
||||
pub mut:
|
||||
expr Expr
|
||||
expr_type Type
|
||||
expr Expr // checker uses this to set typ
|
||||
typ Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
|
@ -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 := ''
|
||||
|
@ -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 {
|
||||
|
@ -2729,10 +2729,17 @@ pub fn (mut f Fmt) type_expr(node ast.TypeNode) {
|
||||
}
|
||||
|
||||
pub fn (mut f Fmt) type_of(node ast.TypeOf) {
|
||||
f.write('typeof(')
|
||||
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) {
|
||||
single_line := node.pos.line_nr >= node.pos.last_line
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
112
vlib/v/tests/typeof_type_test.v
Normal file
112
vlib/v/tests/typeof_type_test.v
Normal file
@ -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
|
||||
}
|
@ -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)!
|
||||
}
|
||||
|
@ -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
|
||||
)
|
Loading…
Reference in New Issue
Block a user