v / vlib / v2 / ssa / build_stmts_from_flat_test.v
135 lines · 118 sloc · 4.52 KB · f3c5760b8838272e4789c38a1be9e03f9ceac351
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 s175: `build_stmts_from_flat` / `build_stmt_from_flat`
7// (the cursor-list seam) must produce the same SSA as the legacy
8// `build_stmts` / `build_stmt`. Today every cursor decodes back to a
9// legacy `ast.Stmt` and dispatches through `build_stmt`. The pin guards
10// the seam wiring so future per-stmt-kind ports inside `build_stmt_from_flat`
11// can be validated incrementally.
12module ssa
13
14import v2.ast
15import v2.types
16
17// Build a fn whose body is a single ReturnStmt. The legacy path goes
18// build_stmts -> build_stmt -> build_return; the flat path goes
19// build_stmts_from_flat -> build_stmt_from_flat (decode) -> build_stmt ->
20// build_return. Both paths must emit the same SSA instrs.
21fn make_build_stmts_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: 'answer'
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: '7'
43 }),
44 ]
45 }),
46 ]
47 }),
48 ]
49 },
50 ]
51}
52
53// build_stmts_from_flat on an empty cursor list must be a no-op — same as
54// build_stmts on an empty slice.
55fn test_build_stmts_from_flat_empty_matches_legacy() {
56 files := [
57 ast.File{
58 name: 'empty.v'
59 mod: 'main'
60 stmts: [ast.Stmt(ast.ModuleStmt{
61 name: 'main'
62 })]
63 },
64 ]
65 flat := ast.flatten_files(files)
66
67 env := types.Environment.new()
68
69 mut mod_legacy := Module.new('bs_empty_legacy')
70 mut b_legacy := Builder.new_with_env(mod_legacy, env)
71 b_legacy.build_stmts([]ast.Stmt{})
72
73 mut mod_flat := Module.new('bs_empty_flat')
74 mut b_flat := Builder.new_with_env(mod_flat, env)
75 // Pass the file's stmts list (only ModuleStmt) — neither path emits
76 // any SSA for a ModuleStmt (build_stmt has no arm for it).
77 b_flat.build_stmts_from_flat(flat.file_cursor(0).stmts())
78
79 assert mod_legacy.instrs.len == mod_flat.instrs.len
80 assert mod_legacy.blocks.len == mod_flat.blocks.len
81 assert mod_legacy.values.len == mod_flat.values.len
82}
83
84// build_stmts_from_flat dispatched through build_stmt_from_flat must build
85// the same SSA bodies as the legacy walker. Validates the cursor-list seam
86// end-to-end via build_fn_bodies (which now consumes cursors as of s174).
87fn test_build_stmts_from_flat_matches_legacy_for_return_fn() {
88 files := make_build_stmts_fixture()
89 flat := ast.flatten_files(files)
90
91 env := types.Environment.new()
92
93 mut mod_legacy := Module.new('bs_two_legacy')
94 mut b_legacy := Builder.new_with_env(mod_legacy, env)
95 b_legacy.register_fn_signatures(files[0])
96 b_legacy.build_fn_bodies(files[0])
97
98 mut mod_flat := Module.new('bs_two_flat')
99 mut b_flat := Builder.new_with_env(mod_flat, env)
100 b_flat.register_fn_signatures_from_flat(flat.file_cursor(0))
101 b_flat.build_fn_bodies_from_flat(flat.file_cursor(0))
102
103 // The body went through build_stmts_from_flat (s174 wiring inside
104 // build_fn currently calls build_stmts — once that switches to
105 // build_stmts_from_flat in s176+, this pin will also catch the
106 // dispatch wiring of the seam itself).
107 assert mod_legacy.funcs.len == mod_flat.funcs.len
108 assert mod_legacy.blocks.len == mod_flat.blocks.len
109 assert mod_legacy.instrs.len == mod_flat.instrs.len
110 assert mod_legacy.values.len == mod_flat.values.len
111}
112
113// Direct exercise of build_stmts_from_flat: dispatch the file's whole stmt
114// list (incl. a FnDecl) through the seam from outside build_fn. The flat
115// path must emit the same SSA as the legacy build_stmts call. Since neither
116// path's build_stmt has an FnDecl arm, this is also a "no-op" check, but
117// it confirms the cursor walk handles top-level stmt mixes without panic.
118fn test_build_stmts_from_flat_direct_dispatch_matches_legacy() {
119 files := make_build_stmts_fixture()
120 flat := ast.flatten_files(files)
121
122 env := types.Environment.new()
123
124 mut mod_legacy := Module.new('bs_direct_legacy')
125 mut b_legacy := Builder.new_with_env(mod_legacy, env)
126 b_legacy.build_stmts(files[0].stmts)
127
128 mut mod_flat := Module.new('bs_direct_flat')
129 mut b_flat := Builder.new_with_env(mod_flat, env)
130 b_flat.build_stmts_from_flat(flat.file_cursor(0).stmts())
131
132 assert mod_legacy.instrs.len == mod_flat.instrs.len
133 assert mod_legacy.blocks.len == mod_flat.blocks.len
134 assert mod_legacy.values.len == mod_flat.values.len
135}
136