all: implement comptime if for generic type `$if T is string {` (#7901)
parent
f751271e4e
commit
9025c3528f
|
@ -4122,6 +4122,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
mut nbranches_without_return := 0
|
mut nbranches_without_return := 0
|
||||||
mut should_skip := false // Whether the current branch should be skipped
|
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 found_branch := false // Whether a matching branch was found- skip the rest
|
||||||
|
mut is_comptime_t_is_expr := false // if `$if T is string`
|
||||||
for i in 0 .. node.branches.len {
|
for i in 0 .. node.branches.len {
|
||||||
mut branch := node.branches[i]
|
mut branch := node.branches[i]
|
||||||
if branch.cond is ast.ParExpr {
|
if branch.cond is ast.ParExpr {
|
||||||
|
@ -4237,10 +4238,16 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
// smartcast field type on comptime if
|
// smartcast field type on comptime if
|
||||||
if branch.cond is ast.InfixExpr {
|
if branch.cond is ast.InfixExpr {
|
||||||
if branch.cond.op == .key_is {
|
if branch.cond.op == .key_is {
|
||||||
se := branch.cond.left
|
left := branch.cond.left
|
||||||
if se is ast.SelectorExpr {
|
|
||||||
got_type := (branch.cond.right as ast.Type).typ
|
got_type := (branch.cond.right as ast.Type).typ
|
||||||
c.comptime_fields_type[se.expr.str()] = got_type
|
if left is ast.SelectorExpr {
|
||||||
|
c.comptime_fields_type[left.expr.str()] = got_type
|
||||||
|
} else if left is ast.Type {
|
||||||
|
is_comptime_t_is_expr = true
|
||||||
|
left_type := c.unwrap_generic(left.typ)
|
||||||
|
if left_type != got_type {
|
||||||
|
should_skip = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4255,7 +4262,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
}
|
}
|
||||||
if !c.skip_flags || c.pref.output_cross_c {
|
if !c.skip_flags || c.pref.output_cross_c {
|
||||||
c.stmts(branch.stmts)
|
c.stmts(branch.stmts)
|
||||||
} else {
|
} else if !is_comptime_t_is_expr {
|
||||||
node.branches[i].stmts = []
|
node.branches[i].stmts = []
|
||||||
}
|
}
|
||||||
c.skip_flags = cur_skip_flags
|
c.skip_flags = cur_skip_flags
|
||||||
|
@ -4386,7 +4393,9 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
|
||||||
return l && r // skip (return true) only if both should be skipped
|
return l && r // skip (return true) only if both should be skipped
|
||||||
}
|
}
|
||||||
.key_is, .not_is {
|
.key_is, .not_is {
|
||||||
if cond.left is ast.SelectorExpr && cond.right is ast.Type {
|
if (cond.left is ast.SelectorExpr ||
|
||||||
|
cond.left is ast.Type) &&
|
||||||
|
cond.right is ast.Type {
|
||||||
// $if method.@type is string
|
// $if method.@type is string
|
||||||
} else {
|
} else {
|
||||||
c.error('invalid `\$if` condition: $cond.left', cond.pos)
|
c.error('invalid `\$if` condition: $cond.left', cond.pos)
|
||||||
|
|
|
@ -231,10 +231,18 @@ fn (mut g Gen) comp_if_expr(cond ast.Expr) {
|
||||||
g.comp_if_expr(cond.right)
|
g.comp_if_expr(cond.right)
|
||||||
}
|
}
|
||||||
.key_is, .not_is {
|
.key_is, .not_is {
|
||||||
se := cond.left as ast.SelectorExpr
|
left := cond.left
|
||||||
name := '${se.expr}.$se.field_name'
|
mut name := ''
|
||||||
exp_type := g.comptime_var_type_map[name]
|
mut exp_type := table.Type(0)
|
||||||
got_type := (cond.right as ast.Type).typ
|
got_type := (cond.right as ast.Type).typ
|
||||||
|
if left is ast.SelectorExpr {
|
||||||
|
name = '${left.expr}.$left.field_name'
|
||||||
|
exp_type = g.comptime_var_type_map[name]
|
||||||
|
} else if left is ast.Type {
|
||||||
|
name = left.str()
|
||||||
|
// this is only allowed for generics currently, otherwise blocked by checker
|
||||||
|
exp_type = g.unwrap_generic(left.typ)
|
||||||
|
}
|
||||||
g.write('$exp_type == $got_type')
|
g.write('$exp_type == $got_type')
|
||||||
}
|
}
|
||||||
.eq, .ne {
|
.eq, .ne {
|
||||||
|
|
|
@ -31,6 +31,10 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
node = p.sql_expr()
|
node = p.sql_expr()
|
||||||
p.inside_match = false
|
p.inside_match = false
|
||||||
} else {
|
} else {
|
||||||
|
if p.inside_if && p.tok.lit == 'T' {
|
||||||
|
// $if T is string {}
|
||||||
|
p.expecting_type = true
|
||||||
|
}
|
||||||
node = p.name_expr()
|
node = p.name_expr()
|
||||||
p.is_stmt_ident = is_stmt_ident
|
p.is_stmt_ident = is_stmt_ident
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,3 +28,40 @@ fn test_ct_expressions() {
|
||||||
assert true
|
assert true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generic_t_is<T>() T {
|
||||||
|
$if T is string {
|
||||||
|
return 'It\'s a string!'
|
||||||
|
} $else {
|
||||||
|
return T{}
|
||||||
|
}
|
||||||
|
return T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GenericTIsTest {}
|
||||||
|
|
||||||
|
fn test_generic_t_is() {
|
||||||
|
assert generic_t_is<string>() == 'It\'s a string!'
|
||||||
|
assert generic_t_is<GenericTIsTest>() == GenericTIsTest{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_t_is2<T>() ?T {
|
||||||
|
$if T is string {
|
||||||
|
return 'It\'s a string!'
|
||||||
|
} $else {
|
||||||
|
return T{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_t_is2() {
|
||||||
|
res := generic_t_is2<string>() or {
|
||||||
|
assert false
|
||||||
|
''
|
||||||
|
}
|
||||||
|
res2 := generic_t_is2<GenericTIsTest>() or {
|
||||||
|
assert false
|
||||||
|
GenericTIsTest{}
|
||||||
|
}
|
||||||
|
assert res == 'It\'s a string!'
|
||||||
|
assert res2 == GenericTIsTest{}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue