v / vlib / v2 / ssa / build_basic_literal_from_flat_test.v
160 lines · 152 sloc · 4.86 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 s186: `build_basic_literal_from_flat` is the first
7// per-kind arm inside a new `build_expr_from_flat` dispatcher — the seam
8// that future sessions will populate (Ident → Infix → Call → ...). For
9// BasicLiteral specifically, the cursor port preserves `pos` so the
10// type-checker lookup hits the same `expr_type` answer as the legacy path
11// (otherwise number-literal width promotion via `get_checked_expr_type`
12// would diverge).
13module ssa
14
15import v2.ast
16import v2.token
17import v2.types
18
19// Fixture: a fn body that returns a BasicLiteral. The pin asserts both
20// paths produce the same SSA module: same number of funcs/blocks/instrs/values.
21fn make_basic_literal_fixture() []ast.File {
22 return [
23 ast.File{
24 name: 'main.v'
25 mod: 'main'
26 stmts: [
27 ast.Stmt(ast.ModuleStmt{
28 name: 'main'
29 }),
30 ast.Stmt(ast.FnDecl{
31 name: 'forty_two'
32 typ: ast.FnType{
33 return_type: ast.Expr(ast.Ident{
34 name: 'int'
35 })
36 }
37 stmts: [
38 ast.Stmt(ast.ReturnStmt{
39 exprs: [
40 ast.Expr(ast.BasicLiteral{
41 kind: .number
42 value: '42'
43 }),
44 ]
45 }),
46 ]
47 }),
48 ]
49 },
50 ]
51}
52
53// build_via_legacy_basic_literal builds the SSA via the legacy
54// `build_expr` path (which delegates BasicLiteral → build_basic_literal).
55fn build_via_legacy_basic_literal(files []ast.File, env &types.Environment, name string) &Module {
56 mut mod := Module.new(name)
57 mut b := Builder.new_with_env(mod, env)
58 b.register_fn_signatures(files[0])
59 b.build_fn_bodies(files[0])
60 return mod
61}
62
63// build_via_flat_basic_literal builds the SSA via the new
64// `build_expr_from_flat` dispatcher: ReturnStmt cursor decodes its 0th edge
65// (the BasicLiteral) and dispatches through build_expr_from_flat. The
66// dispatcher's BasicLiteral arm reads kind/value/pos directly from the
67// cursor and calls build_basic_literal — no `decode_expr` for that node.
68fn build_via_flat_basic_literal(files []ast.File, env &types.Environment, name string) &Module {
69 flat := ast.flatten_files(files)
70 mut mod := Module.new(name)
71 mut b := Builder.new_with_env(mod, env)
72 b.register_fn_signatures_from_flat(flat.file_cursor(0))
73 stmts := flat.file_cursor(0).stmts()
74 for si in 0 .. stmts.len() {
75 c := stmts.at(si)
76 if c.kind() != .stmt_fn_decl {
77 continue
78 }
79 decl := c.fn_decl_signature()
80 fn_name := b.mangle_fn_name(decl)
81 func_idx := b.fn_index[fn_name] or { continue }
82 b.cur_func = func_idx
83 b.label_blocks = map[string]BlockID{}
84 b.vars = map[string]ValueID{}
85 entry := mod.add_block(func_idx, 'entry')
86 b.cur_block = entry
87 // Walk body stmts. For ReturnStmt's 0th edge, route the expr through
88 // build_expr_from_flat instead of decode_expr + build_expr.
89 body := c.list_at(3)
90 for bi in 0 .. body.len() {
91 stmt_c := body.at(bi)
92 if stmt_c.kind() == .stmt_return {
93 ret_expr_c := stmt_c.edge(0)
94 val := b.build_expr_from_flat(ret_expr_c)
95 mod.add_instr(.ret, b.cur_block, 0, [val])
96 } else {
97 b.build_stmt_from_flat(stmt_c)
98 }
99 }
100 if !b.block_has_terminator(b.cur_block) {
101 mod.add_instr(.ret, b.cur_block, 0, []ValueID{})
102 }
103 }
104 return mod
105}
106
107fn test_build_basic_literal_from_flat_number_matches_legacy() {
108 files := make_basic_literal_fixture()
109 env := types.Environment.new()
110 mod_legacy := build_via_legacy_basic_literal(files, env, 'bl_num_legacy')
111 mod_flat := build_via_flat_basic_literal(files, env, 'bl_num_flat')
112
113 assert mod_legacy.funcs.len == mod_flat.funcs.len
114 assert mod_legacy.funcs.len == 1
115 assert mod_legacy.blocks.len == mod_flat.blocks.len
116 assert mod_legacy.instrs.len == mod_flat.instrs.len
117 assert mod_legacy.values.len == mod_flat.values.len
118}
119
120// Same parity check on a key_true literal — covers the bool branch in
121// build_basic_literal (returns get_int(1), '1' rather than the number path).
122fn test_build_basic_literal_from_flat_bool_matches_legacy() {
123 files := [
124 ast.File{
125 name: 'main.v'
126 mod: 'main'
127 stmts: [
128 ast.Stmt(ast.ModuleStmt{
129 name: 'main'
130 }),
131 ast.Stmt(ast.FnDecl{
132 name: 'tru'
133 typ: ast.FnType{
134 return_type: ast.Expr(ast.Ident{
135 name: 'bool'
136 })
137 }
138 stmts: [
139 ast.Stmt(ast.ReturnStmt{
140 exprs: [
141 ast.Expr(ast.BasicLiteral{
142 kind: token.Token.key_true
143 value: 'true'
144 }),
145 ]
146 }),
147 ]
148 }),
149 ]
150 },
151 ]
152 env := types.Environment.new()
153 mod_legacy := build_via_legacy_basic_literal(files, env, 'bl_bool_legacy')
154 mod_flat := build_via_flat_basic_literal(files, env, 'bl_bool_flat')
155
156 assert mod_legacy.funcs.len == mod_flat.funcs.len
157 assert mod_legacy.blocks.len == mod_flat.blocks.len
158 assert mod_legacy.instrs.len == mod_flat.instrs.len
159 assert mod_legacy.values.len == mod_flat.values.len
160}
161