Skip to content

Commit 603cd90

Browse files
authored
checker,cgen: add comptime match support (#25165)
1 parent 70f694f commit 603cd90

22 files changed

+922
-272
lines changed

vlib/v/ast/ast.v

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,9 +1262,10 @@ pub mut:
12621262
@[minify]
12631263
pub struct MatchExpr {
12641264
pub:
1265-
tok_kind token.Kind
1266-
pos token.Pos
1267-
comments []Comment // comments before the first branch
1265+
is_comptime bool
1266+
tok_kind token.Kind
1267+
pos token.Pos
1268+
comments []Comment // comments before the first branch
12681269
pub mut:
12691270
cond Expr
12701271
branches []MatchBranch

vlib/v/checker/comptime.v

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,15 @@ fn (mut c Checker) comptime_if_to_ifdef(name string) !string {
969969
return error('bad os ifdef name "${name}"')
970970
}
971971

972+
// check if `ident` is a function generic, such as `T`
973+
fn (mut c Checker) is_generic_ident(ident string) bool {
974+
if !isnil(c.table.cur_fn) && ident in c.table.cur_fn.generic_names
975+
&& c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len {
976+
return true
977+
}
978+
return false
979+
}
980+
972981
fn (mut c Checker) get_expr_type(cond ast.Expr) ast.Type {
973982
match cond {
974983
ast.Ident {
@@ -978,6 +987,10 @@ fn (mut c Checker) get_expr_type(cond ast.Expr) ast.Type {
978987
|| cond.name == c.comptime.comptime_for_field_var) {
979988
// struct field
980989
return c.type_resolver.get_type_from_comptime_var(cond)
990+
} else if c.is_generic_ident(cond.name) {
991+
// generic type `T`
992+
idx := c.table.cur_fn.generic_names.index(cond.name)
993+
return c.table.cur_concrete_types[idx]
981994
} else if var := cond.scope.find_var(cond.name) {
982995
// var
983996
checked_type = c.unwrap_generic(var.typ)
@@ -1042,6 +1055,35 @@ fn (mut c Checker) get_expr_type(cond ast.Expr) ast.Type {
10421055
}
10431056
}
10441057

1058+
fn (mut c Checker) check_compatible_types(left_type ast.Type, left_name string, expr ast.Expr) bool {
1059+
if expr is ast.ComptimeType {
1060+
return c.type_resolver.is_comptime_type(left_type, expr as ast.ComptimeType)
1061+
} else if expr is ast.TypeNode {
1062+
typ := c.get_expr_type(expr)
1063+
right_type := c.unwrap_generic(typ)
1064+
right_sym := c.table.sym(right_type)
1065+
if right_sym.kind == .placeholder || right_type.has_flag(.generic) {
1066+
c.error('unknown type `${right_sym.name}`', expr.pos)
1067+
}
1068+
if right_sym.kind == .interface && right_sym.info is ast.Interface {
1069+
return left_type.has_flag(.option) == right_type.has_flag(.option)
1070+
&& c.table.does_type_implement_interface(left_type, right_type)
1071+
}
1072+
if right_sym.info is ast.FnType && c.comptime.comptime_for_method_var == left_name {
1073+
return c.table.fn_signature(right_sym.info.func,
1074+
skip_receiver: true
1075+
type_only: true
1076+
) == c.table.fn_signature(c.comptime.comptime_for_method,
1077+
skip_receiver: true
1078+
type_only: true
1079+
)
1080+
} else {
1081+
return left_type == right_type
1082+
}
1083+
}
1084+
return false
1085+
}
1086+
10451087
// comptime_if_cond evaluate the `cond` and return (`is_true`, `keep_stmts`)
10461088
// `is_true` is the evaluate result of `cond`;
10471089
// `keep_stmts` meaning the branch is a `multi pass branch`, we should keep the branch stmts even `is_true` is false, such as `$if T is int {`
@@ -1141,45 +1183,9 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, mut sb strings.Builder) (
11411183
}
11421184
// iter the `type_array`, for `is` and `!is`, it has only one element
11431185
for expr in type_array {
1144-
if expr is ast.ComptimeType {
1145-
is_true = c.type_resolver.is_comptime_type(left_type,
1146-
expr as ast.ComptimeType)
1147-
if is_true {
1148-
break
1149-
}
1150-
} else if expr is ast.TypeNode {
1151-
typ := c.get_expr_type(expr)
1152-
right_type := c.unwrap_generic(typ)
1153-
right_sym := c.table.sym(right_type)
1154-
if right_sym.kind == .placeholder || right_type.has_flag(.generic) {
1155-
c.error('unknown type `${right_sym.name}`', expr.pos)
1156-
}
1157-
if right_sym.kind == .interface && right_sym.info is ast.Interface {
1158-
is_true =
1159-
left_type.has_flag(.option) == right_type.has_flag(.option)
1160-
&& c.table.does_type_implement_interface(left_type, right_type)
1161-
if is_true {
1162-
break
1163-
}
1164-
}
1165-
if right_sym.info is ast.FnType
1166-
&& c.comptime.comptime_for_method_var == left_name {
1167-
is_true = c.table.fn_signature(right_sym.info.func,
1168-
skip_receiver: true
1169-
type_only: true
1170-
) == c.table.fn_signature(c.comptime.comptime_for_method,
1171-
skip_receiver: true
1172-
type_only: true
1173-
)
1174-
if is_true {
1175-
break
1176-
}
1177-
} else {
1178-
is_true = left_type == right_type
1179-
if is_true {
1180-
break
1181-
}
1182-
}
1186+
is_true = c.check_compatible_types(left_type, left_name, expr)
1187+
if is_true {
1188+
break
11831189
}
11841190
}
11851191
is_true = if cond.op in [.key_in, .key_is] { is_true } else { !is_true }

0 commit comments

Comments
 (0)