medvednikov

/

vxx2 Public
0 commits 0 issues 4 pull requests 0 contributors Discussions Projects CI

committed 56 years ago · View patch
5 files changed +274 -19
vlib/v3/tests/review_transform_regressions_test.v +7 -0

@@ -137,6 +137,13 @@ fn test_interface_array_repeat_evaluates_receiver_once() {

137137 assert out == '1\n3'

138138}

139139

140+fn test_negative_is_return_smartcasts_following_statements() {

141+ v3_bin := build_v3_review_transform()

142+ out := run_good(v3_bin, 'negative_is_return_smartcast',

143+ 'struct MapKind {\n\tkey_type int\n\tvalue_type int\n}\nstruct OtherKind {}\ntype Kind = MapKind | OtherKind\n\nfn passthrough(k Kind) Kind {\n\treturn k\n}\n\nfn score(k Kind) int {\n\tclean := passthrough(k)\n\tif clean !is MapKind {\n\t\treturn 0\n\t}\n\treturn clean.key_type + clean.value_type\n}\n\nfn main() {\n\tprintln(int_str(score(Kind(MapKind{\n\t\tkey_type: 2\n\t\tvalue_type: 5\n\t}))))\n\tprintln(int_str(score(Kind(OtherKind{}))))\n}\n')

144+ assert out == '7\n0'

145+}

146+

140147fn test_comptime_type_conditions_handle_logical_ops() {

141148 v3_bin := build_v3_review_transform()

142149 out := run_good(v3_bin, 'comptime_type_condition_logical_ops',

vlib/v3/tests/type_checker_errors_test.v +10 -0

@@ -357,6 +357,16 @@ fn test_fixed_array_length_checks() {

357357 assert good_ret_lit == '5'

358358}

359359

360+fn test_statement_if_branch_tails_are_not_value_checked() {

361+ v3_bin := build_v3()

362+ statement_if := run_good(v3_bin, 'statement_if_mixed_tail_exprs',

363+ "fn main() {\n\tmut n := 0\n\tmut errors := []string{}\n\tif true {\n\t\tn++\n\t} else {\n\t\terrors << 'bad'\n\t}\n\tprintln(int_str(n + errors.len))\n}\n")

364+ assert statement_if == '1'

365+ run_bad(v3_bin, 'bad_if_branch_primitive_mismatch',

366+ "fn main() {\n\tc := true\n\t_ := if c { 1 } else { 'bad' }\n}\n",

367+ 'if-expression branch type mismatch')

368+}

369+

360370// Regression tests for the post-PR review fixes around generic struct receivers

361371// and generic heap struct literals.

362372fn test_generic_struct_receiver_and_heap_init() {

vlib/v3/transform/if.v +104 -0

@@ -1208,6 +1208,110 @@ fn (mut t Transformer) transform_if_branches_with_smartcast(id flat.NodeId, node

12081208 return new_if

12091209}

12101210

1211+fn (t &Transformer) post_if_exit_smartcasts(id flat.NodeId) []IsExprInfo {

1212+ if int(id) < 0 {

1213+ return []IsExprInfo{}

1214+ }

1215+ node := t.a.nodes[int(id)]

1216+ if node.kind != .if_expr || node.children_count < 2 {

1217+ return []IsExprInfo{}

1218+ }

1219+ cond_id := t.a.child(&node, 0)

1220+ then_id := t.a.child(&node, 1)

1221+ if info := t.negated_is_expr_info(cond_id) {

1222+ if t.stmt_definitely_exits(then_id) {

1223+ return [info]

1224+ }

1225+ }

1226+ if node.children_count >= 3 {

1227+ else_id := t.a.child(&node, 2)

1228+ if t.stmt_definitely_exits(else_id) {

1229+ return t.extract_all_is_exprs(cond_id)

1230+ }

1231+ }

1232+ return []IsExprInfo{}

1233+}

1234+

1235+fn (t &Transformer) negated_is_expr_info(cond_id flat.NodeId) ?IsExprInfo {

1236+ if int(cond_id) < 0 {

1237+ return none

1238+ }

1239+ cond := t.a.nodes[int(cond_id)]

1240+ if cond.kind != .prefix || cond.op != .not || cond.children_count == 0 {

1241+ return none

1242+ }

1243+ inner_id := t.a.child(&cond, 0)

1244+ if int(inner_id) < 0 {

1245+ return none

1246+ }

1247+ inner := t.a.nodes[int(inner_id)]

1248+ if inner.kind != .is_expr {

1249+ return none

1250+ }

1251+ info := t.extract_is_expr(inner_id)

1252+ if info.expr_name.len == 0 || info.sum_type_name.len == 0 || info.variant_name.len == 0 {

1253+ return none

1254+ }

1255+ return info

1256+}

1257+

1258+fn (t &Transformer) stmt_definitely_exits(id flat.NodeId) bool {

1259+ if int(id) < 0 {

1260+ return false

1261+ }

1262+ node := t.a.nodes[int(id)]

1263+ match node.kind {

1264+ .return_stmt {

1265+ return true

1266+ }

1267+ .block {

1268+ for i in 0 .. node.children_count {

1269+ if t.stmt_definitely_exits(t.a.child(&node, i)) {

1270+ return true

1271+ }

1272+ }

1273+ return false

1274+ }

1275+ .if_expr {

1276+ if node.children_count < 3 {

1277+ return false

1278+ }

1279+ return t.stmt_definitely_exits(t.a.child(&node, 1))

1280+ && t.stmt_definitely_exits(t.a.child(&node, 2))

1281+ }

1282+ .match_stmt {

1283+ if node.children_count < 2 {

1284+ return false

1285+ }

1286+ mut has_else := false

1287+ for i in 1 .. node.children_count {

1288+ branch := t.a.child_node(&node, i)

1289+ if branch.kind != .match_branch {

1290+ return false

1291+ }

1292+ if branch.value == 'else' {

1293+ has_else = true

1294+ }

1295+ body_start := if branch.value == 'else' { 0 } else { branch.value.int() }

1296+ mut branch_exits := false

1297+ for j in body_start .. branch.children_count {

1298+ if t.stmt_definitely_exits(t.a.child(branch, j)) {

1299+ branch_exits = true

1300+ break

1301+ }

1302+ }

1303+ if !branch_exits {

1304+ return false

1305+ }

1306+ }

1307+ return has_else

1308+ }

1309+ else {

1310+ return false

1311+ }

1312+ }

1313+}

1314+

12111315// --- helpers ---

12121316

12131317// IsExprInfo stores is expr info metadata used by transform.

vlib/v3/transform/transform.v +10 -0

@@ -1641,6 +1641,12 @@ fn (t &Transformer) fn_return_type_for_name(name string) ?string {

16411641// transform_stmts transforms transform stmts data for transform.

16421642pub fn (mut t Transformer) transform_stmts(ids []flat.NodeId) []flat.NodeId {

16431643 mut result := []flat.NodeId{cap: ids.len}

1644+ mut post_if_smartcasts := 0

1645+ defer {

1646+ for _ in 0 .. post_if_smartcasts {

1647+ t.pop_smartcast()

1648+ }

1649+ }

16441650 mut i := 0

16451651 for i < ids.len {

16461652 id := ids[i]

@@ -1688,6 +1694,10 @@ pub fn (mut t Transformer) transform_stmts(ids []flat.NodeId) []flat.NodeId {

16881694 for eid in expanded {

16891695 result << eid

16901696 }

1697+ for info in t.post_if_exit_smartcasts(id) {

1698+ t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)

1699+ post_if_smartcasts++

1700+ }

16911701 i++

16921702 }

16931703 t.drain_pending(mut result)

vlib/v3/types/checker.v +143 -19

@@ -220,6 +220,7 @@ pub mut:

220220 resolved_call_set []bool

221221 resolved_fn_value_names []string // node_id -> resolved function value name

222222 resolved_fn_value_set []bool

223+ statement_nodes []bool

223224 // Methods used as *values* (`recv.method` passed as a callback), recorded per enclosing

224225 // function during semantic checking — which has full scope/type info, runs before

225226 // markused, and (unlike a call) routes a value-context selector through check_selector.

@@ -297,6 +298,7 @@ pub fn TypeChecker.new(a &flat.FlatAst) TypeChecker {

297298 resolved_call_set: []bool{len: a.nodes.len}

298299 resolved_fn_value_names: []string{len: a.nodes.len}

299300 resolved_fn_value_set: []bool{len: a.nodes.len}

301+ statement_nodes: []bool{len: a.nodes.len}

300302 method_values_by_fn: map[int][]string{}

301303 method_value_locals: map[string]bool{}

302304 method_value_local_depth: map[string]int{}

@@ -341,6 +343,7 @@ fn (mut tc TypeChecker) reset_node_caches(n int) {

341343 tc.resolved_call_set = []bool{len: n}

342344 tc.resolved_fn_value_names = []string{len: n}

343345 tc.resolved_fn_value_set = []bool{len: n}

346+ tc.statement_nodes = []bool{len: n}

344347 tc.expr_type_values = []Type{len: n, init: Type(void_)}

345348 tc.expr_type_set = []bool{len: n}

346349 tc.checking_nodes = []bool{len: n}

@@ -348,13 +351,14 @@ fn (mut tc TypeChecker) reset_node_caches(n int) {

348351

349352fn (mut tc TypeChecker) extend_node_caches(n int) {

350353 if n <= tc.resolved_call_names.len && n <= tc.resolved_fn_value_names.len

351- && n <= tc.expr_type_values.len && n <= tc.checking_nodes.len {

354+ && n <= tc.statement_nodes.len && n <= tc.expr_type_values.len && n <= tc.checking_nodes.len {

352355 return

353356 }

354357 extend_string_cache(mut tc.resolved_call_names, n)

355358 extend_bool_cache(mut tc.resolved_call_set, n)

356359 extend_string_cache(mut tc.resolved_fn_value_names, n)

357360 extend_bool_cache(mut tc.resolved_fn_value_set, n)

361+ extend_bool_cache(mut tc.statement_nodes, n)

358362 extend_type_cache(mut tc.expr_type_values, n)

359363 extend_bool_cache(mut tc.expr_type_set, n)

360364 extend_bool_cache(mut tc.checking_nodes, n)

@@ -2107,13 +2111,18 @@ fn (tc &TypeChecker) fn_returns_veb_result(node flat.Node) bool {

21072111

21082112// check_fn_body validates check fn body state for types.

21092113fn (mut tc TypeChecker) check_fn_body(node flat.Node) {

2114+ saved_smartcasts := clone_smartcasts(tc.smartcasts)

2115+ defer {

2116+ tc.smartcasts = clone_smartcasts(saved_smartcasts)

2117+ }

21102118 for i in 0 .. node.children_count {

21112119 child_id := tc.a.child(&node, i)

21122120 child := tc.a.child_node(&node, i)

21132121 if child.kind == .param {

21142122 continue

21152123 }

2116- tc.check_node(child_id)

2124+ tc.check_stmt_node(child_id)

2125+ tc.apply_post_if_exit_smartcasts(child_id)

21172126 }

21182127}

21192128

@@ -3298,9 +3307,7 @@ fn (mut tc TypeChecker) check_select_stmt(node flat.Node) {

32983307 }

32993308 body_start = 2

33003309 }

3301- for j in body_start .. branch.children_count {

3302- tc.check_node(tc.a.child(&branch, j))

3303- }

3310+ tc.check_statement_sequence(branch, body_start, false)

33043311 tc.pop_scope()

33053312 }

33063313}

@@ -3339,7 +3346,7 @@ fn (mut tc TypeChecker) check_or_expr(node flat.Node) {

33393346 }

33403347 tc.push_scope()

33413348 tc.cur_scope.insert('err', tc.parse_type('IError'))

3342- tc.check_node(tc.a.child(&node, 1))

3349+ tc.check_branch_node(tc.a.child(&node, 1), true)

33433350 tc.pop_scope()

33443351}

33453352

@@ -3360,7 +3367,7 @@ fn (mut tc TypeChecker) check_fn_literal(node flat.Node) {

33603367 if child.kind == .param || child.kind == .ident {

33613368 continue

33623369 }

3363- tc.check_node(child_id)

3370+ tc.check_stmt_node(child_id)

33643371 }

33653372 tc.pop_scope()

33663373 tc.cur_fn_ret_type = saved_ret

@@ -3385,9 +3392,7 @@ fn (mut tc TypeChecker) check_lambda_expr(node flat.Node) {

33853392// check_block validates check block state for types.

33863393fn (mut tc TypeChecker) check_block(node flat.Node) {

33873394 tc.push_scope()

3388- for i in 0 .. node.children_count {

3389- tc.check_node(tc.a.child(&node, i))

3390- }

3395+ tc.check_statement_sequence(node, 0, false)

33913396 tc.pop_scope()

33923397}

33933398

@@ -3413,7 +3418,7 @@ fn (mut tc TypeChecker) check_for_stmt(node flat.Node) {

34133418 }

34143419 }

34153420 for i in 3 .. node.children_count {

3416- tc.check_node(tc.a.child(&node, i))

3421+ tc.check_stmt_node(tc.a.child(&node, i))

34173422 }

34183423 tc.pop_scope()

34193424}

@@ -3481,7 +3486,7 @@ fn (mut tc TypeChecker) check_for_in_stmt(node flat.Node) {

34813486 }

34823487 }

34833488 for i in header .. node.children_count {

3484- tc.check_node(tc.a.child(&node, i))

3489+ tc.check_stmt_node(tc.a.child(&node, i))

34853490 }

34863491 tc.pop_scope()

34873492}

@@ -6351,6 +6356,7 @@ fn (mut tc TypeChecker) check_if_expr(id flat.NodeId, node flat.Node) {

63516356 if node.children_count < 2 {

63526357 return

63536358 }

6359+ value_context := !tc.is_statement_node(id)

63546360 cond_id := tc.a.child(&node, 0)

63556361 guard_bindings := tc.check_condition(cond_id)

63566362 smartcasts := tc.extract_smartcasts(cond_id)

@@ -6365,14 +6371,20 @@ fn (mut tc TypeChecker) check_if_expr(id flat.NodeId, node flat.Node) {

63656371 for binding in guard_bindings {

63666372 tc.cur_scope.insert(binding.name, binding.typ)

63676373 }

6368- tc.check_node(then_id)

6369- then_type := tc.branch_tail_type(then_id)

6374+ tc.check_branch_node(then_id, value_context)

63706375 tc.pop_scope()

63716376 tc.smartcasts = clone_smartcasts(saved_smartcasts)

6377+ if node.children_count > 2 {

6378+ else_id := tc.a.child(&node, 2)

6379+ tc.check_branch_node(else_id, value_context)

6380+ }

6381+ if !value_context {

6382+ return

6383+ }

6384+ then_type := tc.branch_tail_type(then_id)

63726385 mut else_type := Type(void_)

63736386 if node.children_count > 2 {

63746387 else_id := tc.a.child(&node, 2)

6375- tc.check_node(else_id)

63766388 else_type = tc.branch_tail_type(else_id)

63776389 }

63786390 if then_type !is Void && else_type !is Void {

@@ -6388,6 +6400,119 @@ fn (mut tc TypeChecker) check_if_expr(id flat.NodeId, node flat.Node) {

63886400 }

63896401}

63906402

6403+fn (mut tc TypeChecker) check_stmt_node(id flat.NodeId) {

6404+ if !tc.valid_node_id(id) {

6405+ return

6406+ }

6407+ idx := int(id)

6408+ if idx >= tc.statement_nodes.len {

6409+ tc.extend_node_caches(tc.a.nodes.len)

6410+ }

6411+ if idx < tc.statement_nodes.len {

6412+ tc.statement_nodes[idx] = true

6413+ }

6414+ tc.check_node(id)

6415+}

6416+

6417+fn (tc &TypeChecker) is_statement_node(id flat.NodeId) bool {

6418+ idx := int(id)

6419+ return idx >= 0 && idx < tc.statement_nodes.len && tc.statement_nodes[idx]

6420+}

6421+

6422+fn (mut tc TypeChecker) check_statement_sequence(node flat.Node, body_start int, value_tail bool) {

6423+ saved_smartcasts := clone_smartcasts(tc.smartcasts)

6424+ defer {

6425+ tc.smartcasts = clone_smartcasts(saved_smartcasts)

6426+ }

6427+ last_idx := int(node.children_count) - 1

6428+ for i in body_start .. node.children_count {

6429+ child_id := tc.a.child(&node, i)

6430+ if value_tail && i == last_idx {

6431+ tc.check_node(child_id)

6432+ } else {

6433+ tc.check_stmt_node(child_id)

6434+ }

6435+ tc.apply_post_if_exit_smartcasts(child_id)

6436+ }

6437+}

6438+

6439+fn (mut tc TypeChecker) check_branch_node(id flat.NodeId, value_tail bool) {

6440+ if !tc.valid_node_id(id) {

6441+ return

6442+ }

6443+ node := tc.a.nodes[int(id)]

6444+ if node.kind == .block {

6445+ tc.push_scope()

6446+ tc.check_statement_sequence(node, 0, value_tail)

6447+ tc.pop_scope()

6448+ return

6449+ }

6450+ if value_tail {

6451+ tc.check_node(id)

6452+ } else {

6453+ tc.check_stmt_node(id)

6454+ }

6455+}

6456+

6457+fn (mut tc TypeChecker) apply_post_if_exit_smartcasts(id flat.NodeId) {

6458+ for binding in tc.post_if_exit_smartcasts(id) {

6459+ if valid_string_data(binding.name) {

6460+ tc.smartcasts[binding.name] = binding.typ

6461+ }

6462+ }

6463+}

6464+

6465+fn (tc &TypeChecker) post_if_exit_smartcasts(id flat.NodeId) []LocalBinding {

6466+ if !tc.valid_node_id(id) {

6467+ return []LocalBinding{}

6468+ }

6469+ node := tc.a.nodes[int(id)]

6470+ if node.kind != .if_expr || node.children_count < 2 {

6471+ return []LocalBinding{}

6472+ }

6473+ cond_id := tc.a.child(&node, 0)

6474+ then_id := tc.a.child(&node, 1)

6475+ if binding := tc.negated_is_smartcast(cond_id) {

6476+ if tc.stmt_definitely_returns(then_id) {

6477+ return [binding]

6478+ }

6479+ }

6480+ if node.children_count >= 3 {

6481+ else_id := tc.a.child(&node, 2)

6482+ if tc.stmt_definitely_returns(else_id) {

6483+ return tc.extract_smartcasts(cond_id)

6484+ }

6485+ }

6486+ return []LocalBinding{}

6487+}

6488+

6489+fn (tc &TypeChecker) negated_is_smartcast(cond_id flat.NodeId) ?LocalBinding {

6490+ if !tc.valid_node_id(cond_id) {

6491+ return none

6492+ }

6493+ cond := tc.a.nodes[int(cond_id)]

6494+ if cond.kind != .prefix || cond.op != .not || cond.children_count == 0 {

6495+ return none

6496+ }

6497+ inner_id := tc.a.child(&cond, 0)

6498+ if !tc.valid_node_id(inner_id) {

6499+ return none

6500+ }

6501+ inner := tc.a.nodes[int(inner_id)]

6502+ if inner.kind != .is_expr || inner.children_count == 0 {

6503+ return none

6504+ }

6505+ expr_id := tc.a.child(&inner, 0)

6506+ key := tc.expr_key(expr_id)

6507+ if key.len == 0 || !valid_string_data(key) || inner.value.len == 0 {

6508+ return none

6509+ }

6510+ return LocalBinding{

6511+ name: key

6512+ typ: tc.parse_type(inner.value)

6513+ }

6514+}

6515+

63916516// branch_has_value_tail converts branch has value tail data for types.

63926517fn (tc &TypeChecker) branch_has_value_tail(id flat.NodeId) bool {

63936518 if !tc.valid_node_id(id) {

@@ -6501,10 +6626,11 @@ fn (mut tc TypeChecker) check_if_guard(id flat.NodeId, node flat.Node) []LocalBi

65016626}

65026627

65036628// check_match_stmt validates check match stmt state for types.

6504-fn (mut tc TypeChecker) check_match_stmt(_id flat.NodeId, node flat.Node) {

6629+fn (mut tc TypeChecker) check_match_stmt(id flat.NodeId, node flat.Node) {

65056630 if node.children_count == 0 {

65066631 return

65076632 }

6633+ value_context := !tc.is_statement_node(id)

65086634 subject_id := tc.a.child(&node, 0)

65096635 tc.check_node(subject_id)

65106636 subject_key := tc.expr_key(subject_id)

@@ -6541,9 +6667,7 @@ fn (mut tc TypeChecker) check_match_stmt(_id flat.NodeId, node flat.Node) {

65416667 }

65426668 }

65436669 tc.push_scope()

6544- for j in n_conds .. branch.children_count {

6545- tc.check_node(tc.a.child(branch, j))

6546- }

6670+ tc.check_statement_sequence(branch, n_conds, value_context)

65476671 tc.pop_scope()

65486672 tc.smartcasts = clone_smartcasts(saved_smartcasts)

65496673 }