v / vlib / v2 / ssa / build_paren_modifier_from_flat_test.v
177 lines · 168 sloc · 4.94 KB · d358576851079d4716834cc86627307d28acd1ae
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4// vtest build: macos
5//
6// Bit-equality pin for s189: `.expr_paren` and `.expr_modifier` arms inside
7// `build_expr_from_flat`. Both are pure unwrap-and-recurse — they call
8// `build_expr_from_flat(c.edge(0))` on the inner edge. No per-kind helper
9// is needed; the new dispatcher handles the inner cursor directly.
10module ssa
11
12import v2.ast
13import v2.token
14import v2.types
15
16// Fixture: `fn forty_two() int { return (42) }` — ParenExpr wraps a
17// BasicLiteral. Exercises the unwrap path through build_expr_from_flat's
18// `.expr_paren` arm.
19fn make_paren_fixture() []ast.File {
20 return [
21 ast.File{
22 name: 'main.v'
23 mod: 'main'
24 stmts: [
25 ast.Stmt(ast.ModuleStmt{
26 name: 'main'
27 }),
28 ast.Stmt(ast.FnDecl{
29 name: 'forty_two'
30 typ: ast.FnType{
31 return_type: ast.Expr(ast.Ident{
32 name: 'int'
33 })
34 }
35 stmts: [
36 ast.Stmt(ast.ReturnStmt{
37 exprs: [
38 ast.Expr(ast.ParenExpr{
39 expr: ast.Expr(ast.BasicLiteral{
40 kind: .number
41 value: '42'
42 })
43 }),
44 ]
45 }),
46 ]
47 }),
48 ]
49 },
50 ]
51}
52
53// Fixture: ModifierExpr wraps an Ident. Exercises the `.expr_modifier` arm.
54// `fn pass_mut(mut x int) int { return mut x }` — ModifierExpr with
55// kind=key_mut around an Ident.
56fn make_modifier_fixture() []ast.File {
57 return [
58 ast.File{
59 name: 'main.v'
60 mod: 'main'
61 stmts: [
62 ast.Stmt(ast.ModuleStmt{
63 name: 'main'
64 }),
65 ast.Stmt(ast.FnDecl{
66 name: 'unwrap'
67 typ: ast.FnType{
68 params: [
69 ast.Parameter{
70 name: 'x'
71 typ: ast.Expr(ast.Ident{
72 name: 'int'
73 })
74 },
75 ]
76 return_type: ast.Expr(ast.Ident{
77 name: 'int'
78 })
79 }
80 stmts: [
81 ast.Stmt(ast.ReturnStmt{
82 exprs: [
83 ast.Expr(ast.ModifierExpr{
84 kind: token.Token.key_mut
85 expr: ast.Expr(ast.Ident{
86 name: 'x'
87 })
88 }),
89 ]
90 }),
91 ]
92 }),
93 ]
94 },
95 ]
96}
97
98fn build_via_legacy_unwrap(files []ast.File, env &types.Environment, name string) &Module {
99 mut mod := Module.new(name)
100 mut b := Builder.new_with_env(mod, env)
101 b.register_fn_signatures(files[0])
102 b.build_fn_bodies(files[0])
103 return mod
104}
105
106// Flat-side helper routes ReturnStmt's 0th edge through build_expr_from_flat
107// which dispatches `.expr_paren` / `.expr_modifier` to the unwrap arm
108// (calls build_expr_from_flat on the inner edge 0).
109fn build_via_flat_unwrap(files []ast.File, env &types.Environment, name string) &Module {
110 flat := ast.flatten_files(files)
111 mut mod := Module.new(name)
112 mut b := Builder.new_with_env(mod, env)
113 b.register_fn_signatures_from_flat(flat.file_cursor(0))
114 stmts := flat.file_cursor(0).stmts()
115 for si in 0 .. stmts.len() {
116 c := stmts.at(si)
117 if c.kind() != .stmt_fn_decl {
118 continue
119 }
120 decl := c.fn_decl_signature()
121 fn_name := b.mangle_fn_name(decl)
122 func_idx := b.fn_index[fn_name] or { continue }
123 b.cur_func = func_idx
124 b.label_blocks = map[string]BlockID{}
125 b.vars = map[string]ValueID{}
126 entry := mod.add_block(func_idx, 'entry')
127 b.cur_block = entry
128 // Set up params (build_fn does this for the legacy path; need parity)
129 for param in decl.typ.params {
130 param_type := b.ast_type_to_ssa(param.typ)
131 param_val := mod.add_value_node(.argument, param_type, param.name, 0)
132 mod.func_add_param(func_idx, param_val)
133 alloca := mod.add_instr(.alloca, entry, mod.type_store.get_ptr(param_type), []ValueID{})
134 mod.add_instr(.store, entry, 0, [param_val, alloca])
135 b.vars[param.name] = alloca
136 }
137 body := c.list_at(3)
138 for bi in 0 .. body.len() {
139 stmt_c := body.at(bi)
140 if stmt_c.kind() == .stmt_return {
141 ret_expr_c := stmt_c.edge(0)
142 val := b.build_expr_from_flat(ret_expr_c)
143 mod.add_instr(.ret, b.cur_block, 0, [val])
144 } else {
145 b.build_stmt_from_flat(stmt_c)
146 }
147 }
148 if !b.block_has_terminator(b.cur_block) {
149 mod.add_instr(.ret, b.cur_block, 0, []ValueID{})
150 }
151 }
152 return mod
153}
154
155fn test_build_paren_from_flat_matches_legacy() {
156 files := make_paren_fixture()
157 env := types.Environment.new()
158 mod_legacy := build_via_legacy_unwrap(files, env, 'paren_legacy')
159 mod_flat := build_via_flat_unwrap(files, env, 'paren_flat')
160
161 assert mod_legacy.funcs.len == mod_flat.funcs.len
162 assert mod_legacy.blocks.len == mod_flat.blocks.len
163 assert mod_legacy.instrs.len == mod_flat.instrs.len
164 assert mod_legacy.values.len == mod_flat.values.len
165}
166
167fn test_build_modifier_from_flat_matches_legacy() {
168 files := make_modifier_fixture()
169 env := types.Environment.new()
170 mod_legacy := build_via_legacy_unwrap(files, env, 'mod_legacy')
171 mod_flat := build_via_flat_unwrap(files, env, 'mod_flat')
172
173 assert mod_legacy.funcs.len == mod_flat.funcs.len
174 assert mod_legacy.blocks.len == mod_flat.blocks.len
175 assert mod_legacy.instrs.len == mod_flat.instrs.len
176 assert mod_legacy.values.len == mod_flat.values.len
177}
178