mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cgen, checker: fix comptimeselector resolution + if comptime branching improvement + comptimeselector cleanup (#17302)
This commit is contained in:
parent
5a8c433548
commit
039c9b2550
@ -385,7 +385,7 @@ pub fn (x Expr) str() string {
|
||||
}
|
||||
}
|
||||
ComptimeSelector {
|
||||
return '${x.left}.$${x.field_expr}'
|
||||
return '${x.left}.$(${x.field_expr})'
|
||||
}
|
||||
ConcatExpr {
|
||||
return x.vals.map(it.str()).join(',')
|
||||
|
@ -296,6 +296,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
}
|
||||
if right is ast.ComptimeSelector {
|
||||
left.obj.is_comptime_field = true
|
||||
left.obj.typ = c.comptime_fields_default_type
|
||||
}
|
||||
}
|
||||
ast.GlobalField {
|
||||
|
@ -873,19 +873,12 @@ fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr) {
|
||||
typ = ast.new_type(idx).derive(arg.typ)
|
||||
} else if c.inside_comptime_for_field && sym.kind in [.struct_, .any]
|
||||
&& arg.expr is ast.ComptimeSelector {
|
||||
compselector := arg.expr as ast.ComptimeSelector
|
||||
if compselector.field_expr is ast.SelectorExpr {
|
||||
selectorexpr := compselector.field_expr as ast.SelectorExpr
|
||||
if selectorexpr.expr is ast.Ident {
|
||||
ident := selectorexpr.expr as ast.Ident
|
||||
if ident.name == c.comptime_for_field_var {
|
||||
typ = c.comptime_fields_default_type
|
||||
|
||||
if func.return_type.has_flag(.generic)
|
||||
&& gt_name == c.table.type_to_str(func.return_type) {
|
||||
node.comptime_ret_val = true
|
||||
}
|
||||
}
|
||||
comptime_typ := c.get_comptime_selector_type(arg.expr, ast.void_type)
|
||||
if comptime_typ != ast.void_type {
|
||||
typ = comptime_typ
|
||||
if func.return_type.has_flag(.generic)
|
||||
&& gt_name == c.table.type_to_str(func.return_type) {
|
||||
node.comptime_ret_val = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2688,15 +2688,6 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
// return ast.void_type
|
||||
// }
|
||||
|
||||
fn (mut c Checker) get_comptime_selector_type(node ast.ComptimeSelector, default_type ast.Type) ast.Type {
|
||||
if node.field_expr is ast.SelectorExpr
|
||||
&& c.check_comptime_is_field_selector(node.field_expr as ast.SelectorExpr)
|
||||
&& (node.field_expr as ast.SelectorExpr).field_name == 'name' {
|
||||
return c.comptime_fields_default_type
|
||||
}
|
||||
return default_type
|
||||
}
|
||||
|
||||
fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
||||
// Given: `Outside( Inside(xyz) )`,
|
||||
// node.expr_type: `Inside`
|
||||
|
@ -146,7 +146,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
||||
|
||||
fn (mut c Checker) comptime_selector(mut node ast.ComptimeSelector) ast.Type {
|
||||
node.left_type = c.expr(node.left)
|
||||
expr_type := c.unwrap_generic(c.expr(node.field_expr))
|
||||
mut expr_type := c.unwrap_generic(c.expr(node.field_expr))
|
||||
expr_sym := c.table.sym(expr_type)
|
||||
if expr_type != ast.string_type {
|
||||
c.error('expected `string` instead of `${expr_sym.name}` (e.g. `field.name`)',
|
||||
@ -158,6 +158,10 @@ fn (mut c Checker) comptime_selector(mut node ast.ComptimeSelector) ast.Type {
|
||||
c.error('compile time field access can only be used when iterating over `T.fields`',
|
||||
left_pos)
|
||||
}
|
||||
expr_type = c.get_comptime_selector_type(node, ast.void_type)
|
||||
if expr_type != ast.void_type {
|
||||
return expr_type
|
||||
}
|
||||
expr_name := node.field_expr.expr.str()
|
||||
if expr_name in c.comptime_fields_type {
|
||||
return c.comptime_fields_type[expr_name]
|
||||
@ -755,6 +759,18 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBran
|
||||
return .unknown
|
||||
}
|
||||
|
||||
// get_comptime_selector_type retrieves the var.$(field.name) type when field_name is 'name' otherwise default_type is returned
|
||||
[inline]
|
||||
fn (mut c Checker) get_comptime_selector_type(node ast.ComptimeSelector, default_type ast.Type) ast.Type {
|
||||
if node.field_expr is ast.SelectorExpr
|
||||
&& c.check_comptime_is_field_selector(node.field_expr as ast.SelectorExpr)
|
||||
&& (node.field_expr as ast.SelectorExpr).field_name == 'name' {
|
||||
return c.unwrap_generic(c.comptime_fields_default_type)
|
||||
}
|
||||
return default_type
|
||||
}
|
||||
|
||||
// check_comptime_is_field_selector checks if the SelectorExpr is related to $for variable
|
||||
[inline]
|
||||
fn (mut c Checker) check_comptime_is_field_selector(node ast.SelectorExpr) bool {
|
||||
if c.inside_comptime_for_field && node.expr is ast.Ident {
|
||||
@ -763,6 +779,7 @@ fn (mut c Checker) check_comptime_is_field_selector(node ast.SelectorExpr) bool
|
||||
return false
|
||||
}
|
||||
|
||||
// check_comptime_is_field_selector_bool checks if the SelectorExpr is related to field.is_* boolean fields
|
||||
[inline]
|
||||
fn (mut c Checker) check_comptime_is_field_selector_bool(node ast.SelectorExpr) bool {
|
||||
if c.check_comptime_is_field_selector(node) {
|
||||
@ -772,6 +789,7 @@ fn (mut c Checker) check_comptime_is_field_selector_bool(node ast.SelectorExpr)
|
||||
return false
|
||||
}
|
||||
|
||||
// get_comptime_selector_bool_field evaluates the bool value for field.is_* fields
|
||||
fn (mut c Checker) get_comptime_selector_bool_field(field_name string) bool {
|
||||
field := c.comptime_for_field_value
|
||||
field_typ := c.comptime_fields_default_type
|
||||
|
@ -37,15 +37,6 @@ fn (mut c Checker) for_c_stmt(node ast.ForCStmt) {
|
||||
c.in_for_count--
|
||||
}
|
||||
|
||||
fn (mut c Checker) get_compselector_type_from_selector_name(node ast.ComptimeSelector) (bool, ast.Type) {
|
||||
if c.inside_comptime_for_field && node.field_expr is ast.SelectorExpr
|
||||
&& (node.field_expr as ast.SelectorExpr).expr.str() in c.comptime_fields_type
|
||||
&& (node.field_expr as ast.SelectorExpr).field_name == 'name' {
|
||||
return true, c.unwrap_generic(c.comptime_fields_default_type)
|
||||
}
|
||||
return false, ast.void_type
|
||||
}
|
||||
|
||||
fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
|
||||
c.in_for_count++
|
||||
prev_loop_label := c.loop_label
|
||||
@ -97,10 +88,10 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
|
||||
node.val_is_ref = node.cond.op == .amp
|
||||
}
|
||||
ast.ComptimeSelector {
|
||||
found, selector_type := c.get_compselector_type_from_selector_name(node.cond)
|
||||
if found {
|
||||
sym = c.table.final_sym(selector_type)
|
||||
typ = selector_type
|
||||
comptime_typ := c.get_comptime_selector_type(node.cond, ast.void_type)
|
||||
if comptime_typ != ast.void_type {
|
||||
sym = c.table.final_sym(comptime_typ)
|
||||
typ = comptime_typ
|
||||
}
|
||||
}
|
||||
ast.Ident {
|
||||
|
@ -6,6 +6,22 @@ import v.ast
|
||||
import v.pref
|
||||
import v.token
|
||||
|
||||
fn (mut c Checker) check_compatible_types(left_type ast.Type, right ast.TypeNode) ComptimeBranchSkipState {
|
||||
right_type := c.unwrap_generic(right.typ)
|
||||
sym := c.table.sym(right_type)
|
||||
|
||||
if sym.kind == .interface_ {
|
||||
checked_type := c.unwrap_generic(left_type)
|
||||
return if c.table.does_type_implement_interface(checked_type, right_type) {
|
||||
.eval
|
||||
} else {
|
||||
.skip
|
||||
}
|
||||
} else {
|
||||
return if left_type == right_type { .eval } else { .skip }
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
||||
if_kind := if node.is_comptime { '\$if' } else { 'if' }
|
||||
mut node_is_expr := false
|
||||
@ -121,22 +137,25 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
||||
comptime_field_name = left.expr.str()
|
||||
c.comptime_fields_type[comptime_field_name] = got_type
|
||||
is_comptime_type_is_expr = true
|
||||
} else if right is ast.TypeNode && left is ast.TypeNode
|
||||
&& sym.kind == .interface_ {
|
||||
is_comptime_type_is_expr = true
|
||||
// is interface
|
||||
checked_type := c.unwrap_generic(left.typ)
|
||||
skip_state = if c.table.does_type_implement_interface(checked_type,
|
||||
got_type)
|
||||
{
|
||||
.eval
|
||||
} else {
|
||||
.skip
|
||||
if comptime_field_name == c.comptime_for_field_var {
|
||||
left_type := c.unwrap_generic(c.comptime_fields_default_type)
|
||||
if left.field_name == 'typ' {
|
||||
skip_state = c.check_compatible_types(left_type, right as ast.TypeNode)
|
||||
} else if left.field_name == 'unaliased_typ' {
|
||||
skip_state = c.check_compatible_types(c.table.unaliased_type(left_type),
|
||||
right as ast.TypeNode)
|
||||
}
|
||||
} else if c.check_comptime_is_field_selector_bool(left) {
|
||||
skip_state = if c.get_comptime_selector_bool_field(left.field_name) {
|
||||
.eval
|
||||
} else {
|
||||
.skip
|
||||
}
|
||||
}
|
||||
} else if left is ast.TypeNode {
|
||||
is_comptime_type_is_expr = true
|
||||
left_type := c.unwrap_generic(left.typ)
|
||||
skip_state = if left_type == got_type { .eval } else { .skip }
|
||||
skip_state = c.check_compatible_types(left_type, right as ast.TypeNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,7 @@ fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type {
|
||||
}
|
||||
if !(typ_sym.is_number() || ((c.inside_unsafe || c.pref.translated) && is_non_void_pointer)) {
|
||||
if c.inside_comptime_for_field {
|
||||
if c.is_comptime_var(node.expr) {
|
||||
return c.comptime_fields_default_type
|
||||
} else if node.expr is ast.ComptimeSelector {
|
||||
if c.is_comptime_var(node.expr) || node.expr is ast.ComptimeSelector {
|
||||
return c.comptime_fields_default_type
|
||||
}
|
||||
}
|
||||
|
@ -647,8 +647,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
|
||||
node.update_expr_type = update_type
|
||||
if node.update_expr is ast.ComptimeSelector {
|
||||
c.error('cannot use struct update syntax in compile time expressions', node.update_expr_pos)
|
||||
}
|
||||
if c.table.final_sym(update_type).kind != .struct_ {
|
||||
} else if c.table.final_sym(update_type).kind != .struct_ {
|
||||
s := c.table.type_to_str(update_type)
|
||||
c.error('expected struct, found `${s}`', node.update_expr.pos())
|
||||
} else if update_type != node.typ {
|
||||
|
@ -1,8 +1,8 @@
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: c
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: d
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: 1
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: 0
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: struct {
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): c
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): d
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): 1
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): 0
|
||||
[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): struct {
|
||||
i: 100
|
||||
}
|
||||
ok
|
||||
|
@ -131,6 +131,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
key_str := g.get_comptime_selector_key_type(val)
|
||||
if key_str != '' {
|
||||
var_type = g.comptime_var_type_map[key_str] or { var_type }
|
||||
val_type = var_type
|
||||
left.obj.typ = var_type
|
||||
is_comptime_var = true
|
||||
}
|
||||
@ -142,6 +143,17 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
}
|
||||
is_auto_heap = left.obj.is_auto_heap
|
||||
}
|
||||
} else if mut left is ast.ComptimeSelector {
|
||||
key_str := g.get_comptime_selector_key_type(left)
|
||||
if key_str != '' {
|
||||
var_type = g.comptime_var_type_map[key_str] or { var_type }
|
||||
}
|
||||
if val is ast.ComptimeSelector {
|
||||
key_str_right := g.get_comptime_selector_key_type(val)
|
||||
if key_str_right != '' {
|
||||
val_type = g.comptime_var_type_map[key_str_right] or { var_type }
|
||||
}
|
||||
}
|
||||
}
|
||||
mut styp := g.typ(var_type)
|
||||
mut is_fixed_array_init := false
|
||||
@ -490,8 +502,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
} else if val_type.has_flag(.shared_f) {
|
||||
g.expr_with_cast(val, val_type, var_type)
|
||||
} else if is_comptime_var && g.right_is_opt {
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(val, val_type, var_type, tmp_var)
|
||||
if var_type.has_flag(.option) && val is ast.ComptimeSelector {
|
||||
g.expr(val)
|
||||
} else {
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(val, val_type, var_type, tmp_var)
|
||||
}
|
||||
} else {
|
||||
g.expr(val)
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! {
|
||||
$if field.unaliased_typ is string {
|
||||
e.encode_string(val.$(field.name).str(), mut wr)!
|
||||
} $else $if field.unaliased_typ is time.Time {
|
||||
parsed_time := val.$(field.name) as time.Time
|
||||
parsed_time := time.parse(val.$(field.name).str()) or { time.Time{} }
|
||||
e.encode_string(parsed_time.format_rfc3339(), mut wr)!
|
||||
} $else $if field.unaliased_typ in [bool, $Float, $Int] {
|
||||
wr.write(val.$(field.name).str().bytes())!
|
||||
|
Loading…
Reference in New Issue
Block a user