1
0
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:
Delyan Angelov 2022-12-07 11:26:27 +02:00 committed by GitHub
parent bb705c01d2
commit 48a7de643e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 184 additions and 71 deletions

View File

@ -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
}

View File

@ -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]

View File

@ -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 := ''

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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))
}

View File

@ -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()

View 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
}

View File

@ -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)!
}

View File

@ -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
)