mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
comp_for: allow checking full mehod and arg types (#5997)
This commit is contained in:
parent
7476428def
commit
eff319f869
@ -1,45 +1,32 @@
|
|||||||
module main
|
struct App {}
|
||||||
|
|
||||||
struct App {
|
fn (mut app App) method_one() {}
|
||||||
test string [test]
|
fn (mut app App) method_two() int { return 0 }
|
||||||
mut:
|
fn (mut app App) method_three(s string) string { return s }
|
||||||
a string
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println('All functions')
|
|
||||||
$for method in App.methods {
|
$for method in App.methods {
|
||||||
$if method.@type is int {
|
$if method.Type is fn(string) string {
|
||||||
println('hi')
|
println('$method.name IS `fn(string) string`')
|
||||||
|
} $else {
|
||||||
|
println('$method.name is NOT `fn(string) string`')
|
||||||
}
|
}
|
||||||
println('$method.name.len')
|
$if method.ReturnType !is int {
|
||||||
println('$method.name.str')
|
println('$method.name does NOT return `int`')
|
||||||
println('Method: $method.name')
|
} $else {
|
||||||
println('Attributes: $method.attrs')
|
println('$method.name DOES return `int`')
|
||||||
println('Return type: $method.ret_type')
|
|
||||||
}
|
}
|
||||||
println('All integer functions')
|
$if method.args[0].Type !is string {
|
||||||
$for method in App.methods {
|
println("${method.name}'s first arg is NOT `string`")
|
||||||
println('Method: $method.name')
|
} $else {
|
||||||
println('Attributes: $method.attrs')
|
println("${method.name}'s first arg IS `string`")
|
||||||
}
|
}
|
||||||
$for field in App.fields {
|
// TODO: Double inversion, should this even be allowed?
|
||||||
$if field.@type is string {
|
$if method.Type is fn() {
|
||||||
println(field)
|
println('$method.name IS a void method')
|
||||||
|
} $else {
|
||||||
|
println('$method.name is NOT a void method')
|
||||||
}
|
}
|
||||||
|
println('')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut app App) method_one() {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut app App) method_two() {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut app App) method_three() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut app App) method_four() int {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
@ -291,26 +291,25 @@ fn __print_assert_failure(i &VAssertMetaInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MethodAttr {
|
pub struct MethodArgs {
|
||||||
pub:
|
pub:
|
||||||
value string
|
Type int
|
||||||
method string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FunctionData {
|
pub struct FunctionData {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
attrs []string
|
attrs []string
|
||||||
ret_type string
|
args []MethodArgs
|
||||||
@type int
|
ReturnType int
|
||||||
|
Type int
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FieldData {
|
pub struct FieldData {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
attrs []string
|
attrs []string
|
||||||
typ string
|
|
||||||
is_pub bool
|
is_pub bool
|
||||||
is_mut bool
|
is_mut bool
|
||||||
@type int
|
Type int
|
||||||
}
|
}
|
||||||
|
@ -107,31 +107,33 @@ fn (mut g Gen) comp_if(mut it ast.CompIf) {
|
|||||||
}
|
}
|
||||||
if it.kind == .typecheck {
|
if it.kind == .typecheck {
|
||||||
mut comptime_var_type := table.Type(0)
|
mut comptime_var_type := table.Type(0)
|
||||||
|
mut name := ''
|
||||||
if it.tchk_expr is ast.SelectorExpr {
|
if it.tchk_expr is ast.SelectorExpr {
|
||||||
se := it.tchk_expr as ast.SelectorExpr
|
se := it.tchk_expr as ast.SelectorExpr
|
||||||
x := se.expr.str()
|
name = '${se.expr}.$se.field_name'
|
||||||
comptime_var_type = g.comptime_var_type_map[x]
|
comptime_var_type = g.comptime_var_type_map[name]
|
||||||
}
|
}
|
||||||
if comptime_var_type == 0 {
|
// if comptime_var_type == 0 {
|
||||||
$if trace_gen ? {
|
// $if trace_gen ? {
|
||||||
eprintln('Known compile time types: ')
|
// eprintln('Known compile time types: ')
|
||||||
eprintln(g.comptime_var_type_map.str())
|
// eprintln(g.comptime_var_type_map.str())
|
||||||
|
// }
|
||||||
|
// // verror('the compile time type of `$it.tchk_expr.str()` is unknown')
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
it_type_name := g.table.get_type_name(it.tchk_type)
|
||||||
|
should_write := (comptime_var_type == it.tchk_type && !it.is_not) ||
|
||||||
|
(comptime_var_type != it.tchk_type && it.is_not)
|
||||||
|
if should_write {
|
||||||
|
inversion := if it.is_not { '!' } else { '' }
|
||||||
|
g.writeln('/* \$if $name ${inversion}is $it_type_name */ {')
|
||||||
|
g.stmts(it.stmts)
|
||||||
|
g.writeln('}')
|
||||||
|
} else if it.has_else {
|
||||||
|
g.writeln('/* \$else */ {')
|
||||||
|
g.stmts(it.else_stmts)
|
||||||
|
g.writeln('}')
|
||||||
}
|
}
|
||||||
verror('the compile time type of `$it.tchk_expr.str()` is unknown')
|
|
||||||
}
|
|
||||||
ret_type_name := g.table.get_type_symbol(comptime_var_type).name
|
|
||||||
it_type_name := g.table.get_type_symbol(it.tchk_type).name
|
|
||||||
types_match := comptime_var_type == it.tchk_type
|
|
||||||
g.writeln('{ // \$if $it.val is $it_type_name, typecheck start, $comptime_var_type == $it.tchk_type => $ret_type_name == $it_type_name => $types_match ')
|
|
||||||
mut stmts := it.stmts
|
|
||||||
if !types_match {
|
|
||||||
stmts = []ast.Stmt{}
|
|
||||||
if it.has_else {
|
|
||||||
stmts = it.else_stmts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.stmts(stmts)
|
|
||||||
g.writeln('} // typecheck end')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ifdef := g.comp_if_to_ifdef(it.val, it.is_opt)
|
ifdef := g.comp_if_to_ifdef(it.val, it.is_opt)
|
||||||
@ -192,17 +194,53 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
|
|||||||
g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
|
g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
|
||||||
attrs.join(', ') + '}));')
|
attrs.join(', ') + '}));')
|
||||||
}
|
}
|
||||||
method_sym := g.table.get_type_symbol(method.return_type)
|
if method.args.len < 2 {
|
||||||
g.writeln('\t${node.val_var}.ret_type = tos_lit("$method_sym.name");')
|
// 0 or 1 (the receiver) args
|
||||||
styp := int(method.return_type).str()
|
g.writeln('\t${node.val_var}.args = __new_array_with_default(0, 0, sizeof(MethodArgs), 0);')
|
||||||
g.writeln('\t${node.val_var}.type = $styp;')
|
} else {
|
||||||
|
len := method.args.len - 1
|
||||||
|
g.write('\t${node.val_var}.args = new_array_from_c_array($len, $len, sizeof(MethodArgs), _MOV((MethodArgs[$len]){')
|
||||||
|
// Skip receiver arg
|
||||||
|
for j, arg in method.args[1..] {
|
||||||
|
typ := arg.typ.idx()
|
||||||
|
g.write(typ.str())
|
||||||
|
if j < len - 1 {
|
||||||
|
g.write(', ')
|
||||||
|
}
|
||||||
|
g.comptime_var_type_map['${node.val_var}.args[$j].Type'] = typ
|
||||||
|
}
|
||||||
|
g.writeln('}));')
|
||||||
|
}
|
||||||
|
mut sig := 'anon_fn_'
|
||||||
|
// skip the first (receiver) arg
|
||||||
|
for j, arg in method.args[1..] {
|
||||||
|
// TODO: ignore mut/pts in sig for now
|
||||||
|
typ := arg.typ.set_nr_muls(0)
|
||||||
|
sig += '$typ'
|
||||||
|
if j < method.args.len - 2 {
|
||||||
|
sig += '_'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sig += '_$method.return_type'
|
||||||
|
styp := g.table.find_type_idx(sig)
|
||||||
|
// println(styp)
|
||||||
|
// if styp == 0 { }
|
||||||
|
// TODO: type aliases
|
||||||
|
ret_typ := method.return_type.idx()
|
||||||
|
g.writeln('\t${node.val_var}.Type = $styp;')
|
||||||
|
g.writeln('\t${node.val_var}.ReturnType = $ret_typ;')
|
||||||
//
|
//
|
||||||
g.comptime_var_type_map[node.val_var] = method.return_type
|
g.comptime_var_type_map['${node.val_var}.ReturnType'] = ret_typ
|
||||||
|
g.comptime_var_type_map['${node.val_var}.Type'] = styp
|
||||||
g.stmts(node.stmts)
|
g.stmts(node.stmts)
|
||||||
i++
|
i++
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
|
for key, _ in g.comptime_var_type_map {
|
||||||
|
if key.starts_with(node.val_var) {
|
||||||
|
g.comptime_var_type_map.delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.comptime_var_type_map.delete(node.val_var)
|
|
||||||
} else if node.kind == .fields {
|
} else if node.kind == .fields {
|
||||||
// TODO add fields
|
// TODO add fields
|
||||||
if sym.info is table.Struct {
|
if sym.info is table.Struct {
|
||||||
@ -224,13 +262,13 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
|
|||||||
g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
|
g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
|
||||||
attrs.join(', ') + '}));')
|
attrs.join(', ') + '}));')
|
||||||
}
|
}
|
||||||
field_sym := g.table.get_type_symbol(field.typ)
|
// field_sym := g.table.get_type_symbol(field.typ)
|
||||||
g.writeln('\t${node.val_var}.typ = tos_lit("$field_sym.name");')
|
// g.writeln('\t${node.val_var}.typ = tos_lit("$field_sym.name");')
|
||||||
styp := int(field.typ).str()
|
styp := field.typ
|
||||||
g.writeln('\t${node.val_var}.type = $styp;')
|
g.writeln('\t${node.val_var}.Type = $styp;')
|
||||||
g.writeln('\t${node.val_var}.is_pub = $field.is_pub;')
|
g.writeln('\t${node.val_var}.is_pub = $field.is_pub;')
|
||||||
g.writeln('\t${node.val_var}.is_mut = $field.is_mut;')
|
g.writeln('\t${node.val_var}.is_mut = $field.is_mut;')
|
||||||
g.comptime_var_type_map[node.val_var] = field.typ
|
g.comptime_var_type_map[node.val_var + '.Type'] = styp
|
||||||
g.stmts(node.stmts)
|
g.stmts(node.stmts)
|
||||||
i++
|
i++
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
|
@ -193,7 +193,8 @@ fn (mut p Parser) comp_if() ast.Stmt {
|
|||||||
// return p.vweb()
|
// return p.vweb()
|
||||||
// }
|
// }
|
||||||
p.check(.key_if)
|
p.check(.key_if)
|
||||||
is_not := p.tok.kind == .not
|
mut is_not := p.tok.kind == .not
|
||||||
|
inversion_pos := p.tok.position()
|
||||||
if is_not {
|
if is_not {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
@ -202,7 +203,7 @@ fn (mut p Parser) comp_if() ast.Stmt {
|
|||||||
mut val := ''
|
mut val := ''
|
||||||
mut tchk_expr := ast.Expr{}
|
mut tchk_expr := ast.Expr{}
|
||||||
if p.peek_tok.kind == .dot {
|
if p.peek_tok.kind == .dot {
|
||||||
vname := p.parse_ident(table.Language.v)
|
vname := p.parse_ident(.v)
|
||||||
cobj := p.scope.find(vname.name) or {
|
cobj := p.scope.find(vname.name) or {
|
||||||
p.error_with_pos('unknown variable `$vname.name`', name_pos_start)
|
p.error_with_pos('unknown variable `$vname.name`', name_pos_start)
|
||||||
return ast.Stmt{}
|
return ast.Stmt{}
|
||||||
@ -210,9 +211,17 @@ fn (mut p Parser) comp_if() ast.Stmt {
|
|||||||
if cobj is ast.Var {
|
if cobj is ast.Var {
|
||||||
tchk_expr = p.dot_expr(vname)
|
tchk_expr = p.dot_expr(vname)
|
||||||
val = vname.name
|
val = vname.name
|
||||||
if tchk_expr is ast.SelectorExpr {
|
if tchk_expr is ast.SelectorExpr as tchk_expr2 {
|
||||||
if tchk_expr.field_name !in ['type', '@type'] {
|
if p.tok.kind == .lsbr && tchk_expr2.field_name == 'args' {
|
||||||
p.error_with_pos('only the `.@type` field name is supported for now',
|
tchk_expr = p.index_expr(tchk_expr)
|
||||||
|
if p.tok.kind == .dot && p.peek_tok.lit == 'Type' {
|
||||||
|
tchk_expr = p.dot_expr(tchk_expr)
|
||||||
|
} else {
|
||||||
|
p.error_with_pos('only the `Type` field is supported for arguments',
|
||||||
|
p.peek_tok.position())
|
||||||
|
}
|
||||||
|
} else if tchk_expr2.field_name !in ['Type', 'ReturnType'] {
|
||||||
|
p.error_with_pos('only the `Type` and `ReturnType` fields are supported for now',
|
||||||
name_pos_start)
|
name_pos_start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,10 +289,17 @@ fn (mut p Parser) comp_if() ast.Stmt {
|
|||||||
if p.tok.kind == .question {
|
if p.tok.kind == .question {
|
||||||
p.next()
|
p.next()
|
||||||
is_opt = true
|
is_opt = true
|
||||||
} else if p.tok.kind == .key_is {
|
} else if p.tok.kind in [.key_is, .not_is] {
|
||||||
|
typecheck_inversion := p.tok.kind == .not_is
|
||||||
p.next()
|
p.next()
|
||||||
tchk_type = p.parse_type()
|
tchk_type = p.parse_type()
|
||||||
is_typecheck = true
|
is_typecheck = true
|
||||||
|
if is_not {
|
||||||
|
name := p.table.get_type_name(tchk_type)
|
||||||
|
p.error_with_pos('use `\$if $tchk_expr !is $name {`, not `\$if !$tchk_expr is $name {`',
|
||||||
|
inversion_pos)
|
||||||
|
}
|
||||||
|
is_not = typecheck_inversion
|
||||||
}
|
}
|
||||||
if !skip {
|
if !skip {
|
||||||
stmts = p.parse_block()
|
stmts = p.parse_block()
|
||||||
|
@ -28,11 +28,14 @@ fn (mut app App) int_method2() int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut app App) string_arg(x string) {
|
||||||
|
}
|
||||||
|
|
||||||
fn no_lines(s string) string { return s.replace('\n', ' ') }
|
fn no_lines(s string) string { return s.replace('\n', ' ') }
|
||||||
|
|
||||||
fn test_comptime_for() {
|
fn test_comptime_for() {
|
||||||
println(@FN)
|
println(@FN)
|
||||||
methods := ['run', 'method2', 'int_method1', 'int_method2']
|
methods := ['run', 'method2', 'int_method1', 'int_method2', 'string_arg']
|
||||||
$for method in App.methods {
|
$for method in App.methods {
|
||||||
println(' method: $method.name | ' + no_lines('$method'))
|
println(' method: $method.name | ' + no_lines('$method'))
|
||||||
assert method.name in methods
|
assert method.name in methods
|
||||||
@ -41,12 +44,16 @@ fn test_comptime_for() {
|
|||||||
|
|
||||||
fn test_comptime_for_with_if() {
|
fn test_comptime_for_with_if() {
|
||||||
println(@FN)
|
println(@FN)
|
||||||
methods := ['int_method1', 'int_method2']
|
|
||||||
$for method in App.methods {
|
$for method in App.methods {
|
||||||
println(' method: ' + no_lines('$method'))
|
println(' method: ' + no_lines('$method'))
|
||||||
$if method.@type is int {
|
$if method.Type is fn() {
|
||||||
println(method.attrs)
|
assert method.name in ['run', 'method2']
|
||||||
assert method.name in methods
|
}
|
||||||
|
$if method.ReturnType is int {
|
||||||
|
assert method.name in ['int_method1', 'int_method2']
|
||||||
|
}
|
||||||
|
$if method.args[0].Type is string {
|
||||||
|
assert method.name == 'string_arg'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,10 +62,10 @@ fn test_comptime_for_fields() {
|
|||||||
println(@FN)
|
println(@FN)
|
||||||
$for field in App.fields {
|
$for field in App.fields {
|
||||||
println(' field: $field.name | ' + no_lines('$field'))
|
println(' field: $field.name | ' + no_lines('$field'))
|
||||||
$if field.@type is string {
|
$if field.Type is string {
|
||||||
assert field.name in ['a', 'b', 'g']
|
assert field.name in ['a', 'b', 'g']
|
||||||
}
|
}
|
||||||
$if field.@type is f32 {
|
$if field.Type is f32 {
|
||||||
assert field.name in ['d', 'e']
|
assert field.name in ['d', 'e']
|
||||||
}
|
}
|
||||||
if field.is_mut {
|
if field.is_mut {
|
||||||
|
@ -370,7 +370,7 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
|
|||||||
mut vars := []string{cap: route_words_a.len}
|
mut vars := []string{cap: route_words_a.len}
|
||||||
mut action := ''
|
mut action := ''
|
||||||
$for method in T.methods {
|
$for method in T.methods {
|
||||||
$if method.@type is Result {
|
$if method.ReturnType is Result {
|
||||||
attrs := method.attrs
|
attrs := method.attrs
|
||||||
route_words_a = [][]string{}
|
route_words_a = [][]string{}
|
||||||
if attrs.len == 0 {
|
if attrs.len == 0 {
|
||||||
@ -469,7 +469,7 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
$for method in T.methods {
|
$for method in T.methods {
|
||||||
$if method.@type is Result {
|
$if method.ReturnType is Result {
|
||||||
// search again for method
|
// search again for method
|
||||||
if action == method.name && method.attrs.len > 0 {
|
if action == method.name && method.attrs.len > 0 {
|
||||||
// call action method
|
// call action method
|
||||||
|
Loading…
Reference in New Issue
Block a user