1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: fix generic type $else $if (#8339)

This commit is contained in:
Daniel Däschle 2021-01-25 17:08:02 +01:00 committed by GitHub
parent 9d1d35ebdc
commit 006a11454f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 38 deletions

View File

@ -312,6 +312,9 @@ pub fn (x Expr) str() string {
StringLiteral {
return '"$x.val"'
}
Type {
return 'Type($x.typ)'
}
TypeOf {
return 'typeof($x.expr.str())'
}

View File

@ -4361,7 +4361,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
mut nbranches_without_return := 0
mut should_skip := false // Whether the current branch should be skipped
mut found_branch := false // Whether a matching branch was found- skip the rest
mut is_comptime_t_is_expr := false // if `$if T is string`
mut is_comptime_type_is_expr := false // if `$if T is string`
for i in 0 .. node.branches.len {
mut branch := node.branches[i]
if branch.cond is ast.ParExpr {
@ -4443,8 +4443,9 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
got_type := (branch.cond.right as ast.Type).typ
if left is ast.SelectorExpr {
c.comptime_fields_type[left.expr.str()] = got_type
is_comptime_type_is_expr = true
} else if left is ast.Type {
is_comptime_t_is_expr = true
is_comptime_type_is_expr = true
left_type := c.unwrap_generic(left.typ)
if left_type != got_type {
should_skip = true
@ -4458,12 +4459,12 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
} else if should_skip {
c.skip_flags = true
should_skip = false // Reset the value of `should_skip` for the next branch
} else {
} else if !is_comptime_type_is_expr {
found_branch = true // If a branch wasn't skipped, the rest must be
}
if !c.skip_flags || c.pref.output_cross_c {
c.stmts(branch.stmts)
} else if !is_comptime_t_is_expr {
} else if !is_comptime_type_is_expr {
node.branches[i].stmts = []
}
c.skip_flags = cur_skip_flags

View File

@ -155,13 +155,14 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
start_pos := g.out.len
if i == node.branches.len - 1 && node.has_else {
g.writeln('#else')
comp_if_stmts_skip = false
} else {
if i == 0 {
g.write('#if ')
} else {
g.write('#elif ')
}
g.comp_if_expr(branch.cond)
comp_if_stmts_skip = !g.comp_if_cond(branch.cond)
g.writeln('')
}
expr_str := g.out.last_n(g.out.len - start_pos).trim_space()
@ -193,30 +194,12 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
if should_create_scope {
g.writeln('{')
}
if branch.cond is ast.InfixExpr {
if branch.cond.op == .key_is {
left := branch.cond.left
got_type := (branch.cond.right as ast.Type).typ
if left is ast.Type {
left_type := g.unwrap_generic(left.typ)
if left_type != got_type {
comp_if_stmts_skip = true
}
}
}
}
is_else := node.has_else && i == node.branches.len - 1
if !comp_if_stmts_skip || (comp_if_stmts_skip && is_else) {
if !comp_if_stmts_skip {
g.stmts(branch.stmts)
}
if should_create_scope {
g.writeln('}')
}
if !comp_if_stmts_skip && branch.cond is ast.InfixExpr {
if (branch.cond as ast.InfixExpr).op == .key_is {
break
}
}
}
g.defer_ifdef = ''
}
@ -227,33 +210,37 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
}
}
fn (mut g Gen) comp_if_expr(cond ast.Expr) {
fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
match cond {
ast.BoolLiteral {
g.expr(cond)
return true
}
ast.ParExpr {
g.write('(')
g.comp_if_expr(cond.expr)
is_cond_true := g.comp_if_cond(cond.expr)
g.write(')')
return is_cond_true
}
ast.PrefixExpr {
g.write(cond.op.str())
g.comp_if_expr(cond.right)
return g.comp_if_cond(cond.right)
}
ast.PostfixExpr {
ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or {
verror(err)
return
return false
}
g.write('defined($ifdef)')
return true
}
ast.InfixExpr {
match cond.op {
.and, .logical_or {
g.comp_if_expr(cond.left)
l := g.comp_if_cond(cond.left)
g.write(' $cond.op ')
g.comp_if_expr(cond.right)
r := g.comp_if_cond(cond.right)
return l && r
}
.key_is, .not_is {
left := cond.left
@ -268,23 +255,34 @@ fn (mut g Gen) comp_if_expr(cond ast.Expr) {
// this is only allowed for generics currently, otherwise blocked by checker
exp_type = g.unwrap_generic(left.typ)
}
op := if cond.op == .key_is { '==' } else { '!=' }
g.write('$exp_type $op $got_type')
if cond.op == .key_is {
g.write('$exp_type == $got_type')
return exp_type == got_type
} else {
g.write('$exp_type !=$got_type')
return exp_type != got_type
}
}
.eq, .ne {
// TODO Implement `$if method.args.len == 1`
g.write('1')
return true
}
else {
return true
}
else {}
}
}
ast.Ident {
ifdef := g.comp_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker
g.write('defined($ifdef)')
return true
}
else {
// should be unreachable, but just in case
g.write('1')
return true
}
}
}

View File

@ -81,6 +81,14 @@ fn test_generic_t_is3() {
assert res == GenericTIsTest{}
}
fn generic_t_is_with_else<T>(raw_data string) ?T {
$if T is string {
return raw_data
} $else {
return T{}
}
}
fn test_generic_t_is_with_else() {
res := generic_t_is_with_else<GenericTIsTest>('') or {
assert false
@ -94,10 +102,25 @@ fn test_generic_t_is_with_else() {
assert str == 'test'
}
fn generic_t_is_with_else<T>(raw_data string) ?T {
$if T is string {
return raw_data
} $else {
return T{}
fn generic_t_is_with_else_if<T>() []string {
mut fields := []string{}
$for field in T.fields {
$if field.typ is string {
fields << field.name
} $else $if field.typ is int {
fields << field.name
}
}
return fields
}
struct User {
name string
age int
}
fn test_generic_t_is_with_else_if() {
x := generic_t_is_with_else_if<User>()
assert x == ['name', 'age']
}