Skip to content

Commit f665055

Browse files
committed
ssa: array init and arr[i]
1 parent 23d4e5c commit f665055

13 files changed

Lines changed: 423 additions & 36 deletions

File tree

‎cmd/tinyv/test.v‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,37 @@ fn main() {
16161616
c := if a < b && b < 30 { a + b } else { 0 }
16171617
print_int(c) // 30
16181618

1619+
// ==================== 33. ARRAY INITIALIZATION ====================
1620+
print_str('--- 33. Array Initialization ---')
1621+
1622+
// 33.1 Basic array literal
1623+
arr1 := [10, 20, 30]
1624+
print_int(arr1[0]) // 10
1625+
print_int(arr1[1]) // 20
1626+
print_int(arr1[2]) // 30
1627+
1628+
// 33.2 Array element sum
1629+
arr2 := [5, 10, 15]
1630+
arr2_sum := arr2[0] + arr2[1] + arr2[2]
1631+
print_int(arr2_sum) // 30
1632+
1633+
// 33.3 Array with computed values
1634+
base_val := 7
1635+
arr3 := [base_val, base_val * 2, base_val * 3]
1636+
print_int(arr3[0]) // 7
1637+
print_int(arr3[1]) // 14
1638+
print_int(arr3[2]) // 21
1639+
1640+
// 33.4 Array element in expression
1641+
arr4 := [100, 200, 300]
1642+
result4 := arr4[0] * 2 + arr4[1]
1643+
print_int(result4) // 400
1644+
1645+
// 33.5 Array with function call on elements
1646+
arr5 := [3, 4, 5]
1647+
print_int(add(arr5[0], arr5[1])) // 7
1648+
print_int(mul(arr5[1], arr5[2])) // 20
1649+
16191650
print_str('=== All tests completed ===')
16201651
}
16211652

‎cmd/tinyv/test_ssa_backends.v‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import v2.parser
88
import v2.token
99
import v2.pref
1010
import v2.ssa
11+
import v2.transform
1112
import v2.gen.x64
1213
import v2.gen.arm64
1314
import v2.gen.cleanc
@@ -32,10 +33,14 @@ fn main() {
3233
return
3334
}
3435
println('[*] Parsing ${input_file}...')
35-
file := p.parse_file(input_file, mut file_set)
36-
if file.stmts.len == 0 {
36+
parsed_file := p.parse_file(input_file, mut file_set)
37+
if parsed_file.stmts.len == 0 {
3738
println('Warning: No statements found in ${input_file}')
3839
}
40+
// Transform AST (lower complex constructs like ArrayInitExpr)
41+
println('[*] Transforming AST...')
42+
mut transformer := transform.Transformer.new()
43+
file := transformer.transform(parsed_file)
3944
// Initialize SSA Module
4045
mut mod := ssa.Module.new('main')
4146
// Build SSA from AST

‎vlib/v/gen/c/auto_str_methods.v‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,15 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
10341034
if base_typ.has_flag(.shared_f) {
10351035
base_typ = base_typ.clear_flag(.shared_f).deref()
10361036
}
1037-
base_fmt := g.type_to_fmt(base_typ)
1037+
// For C structs, voidptr/byteptr fields in V may have different actual types in C headers,
1038+
// so we use string format instead of pointer format to avoid invalid casts.
1039+
// charptr fields are excluded as they are properly handled by builtin__tos4.
1040+
mut base_fmt := g.type_to_fmt(base_typ)
1041+
is_c_voidptr_field := is_c_struct && field.typ in ast.cptr_types
1042+
&& field.typ !in ast.charptr_types
1043+
if is_c_voidptr_field {
1044+
base_fmt = .si_s
1045+
}
10381046
is_opt_field := field.typ.has_flag(.option)
10391047

10401048
// manage prefix and quote symbol for the filed
@@ -1096,6 +1104,11 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
10961104
it_field_name := 'it${op}${field_name}'
10971105
if ftyp_nr_muls > 1 || field.typ in ast.cptr_types {
10981106
if is_opt_field {
1107+
} else if is_c_voidptr_field {
1108+
// For C structs, the actual type may differ from V's declaration,
1109+
// so we just output a placeholder instead of trying to cast
1110+
func = '_S("<cptr>")'
1111+
caller_should_free = false
10991112
} else {
11001113
func = '(voidptr) ${it_field_name}'
11011114
caller_should_free = false
@@ -1136,8 +1149,8 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
11361149
fn_body.write_string('${funcprefix}_S("<circular>")')
11371150
}
11381151
} else {
1139-
// manage C charptr
1140-
if field.typ in ast.charptr_types {
1152+
// manage C charptr (but not our placeholder for C struct voidptr fields)
1153+
if field.typ in ast.charptr_types && !is_c_voidptr_field {
11411154
fn_body.write_string('builtin__tos4((byteptr)${func})')
11421155
} else {
11431156
if field.typ.is_ptr() && sym.kind in [.struct, .interface] {

‎vlib/v/gen/c/str_intp.v‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ fn (mut g Gen) str_format(node ast.StringInterLiteral, i int, fmts []u8) (u64, s
165165
}
166166
}
167167
} else {
168-
// TODO: better check this case
169-
fmt_type = .si_p
168+
// For unknown types (like C structs), use string format
169+
// so that their str() method will be called
170+
fmt_type = .si_s
170171
}
171172

172173
/*

‎vlib/v/tests/printing_c_structs/cstruct.h‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,16 @@
22
struct Abc {
33
char *char_pointer_field;
44
};
5+
6+
// Simulates a struct like FT_Generic which is a struct, not a pointer
7+
struct InnerStruct {
8+
int x;
9+
int y;
10+
};
11+
12+
// Simulates a struct like FT_FaceRec which has InnerStruct fields
13+
// that might be declared as voidptr in V bindings
14+
struct OuterStruct {
15+
int id;
16+
struct InnerStruct inner; // This is a struct value, not a pointer
17+
};

‎vlib/v/tests/printing_c_structs/string_interpolation_test.c.v‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,30 @@ fn test_interpolation_of_v_structs_containing_c_structs() {
2424
}
2525
}'
2626
}
27+
28+
// Test for C structs where voidptr fields in V have different actual C types.
29+
// This simulates the FreeType case where FT_Generic/FT_BBox are structs in C
30+
// but may be declared as voidptr in V bindings.
31+
pub struct C.OuterStruct {
32+
id int
33+
inner voidptr // Declared as voidptr in V, but actual C type is struct InnerStruct
34+
}
35+
36+
struct VStructWithCStruct {
37+
outer C.OuterStruct
38+
}
39+
40+
fn test_c_struct_with_voidptr_field_that_is_actually_struct() {
41+
// This test verifies that printing a C struct with a voidptr field
42+
// that is actually a struct in C does not cause C compilation errors.
43+
outer := C.OuterStruct{
44+
id: 42
45+
}
46+
wrapper := VStructWithCStruct{
47+
outer: outer
48+
}
49+
s := wrapper.str()
50+
// The voidptr field should be printed as <cptr> placeholder
51+
assert s.contains('id: 42')
52+
assert s.contains('inner: <cptr>')
53+
}

‎vlib/v2/builder/builder.v‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import v2.gen.x64
1313
import v2.pref
1414
import v2.ssa
1515
import v2.token
16+
import v2.transform
1617
import time
1718

1819
struct Builder {
@@ -106,9 +107,13 @@ fn (mut b Builder) gen_cleanc() {
106107
fn (mut b Builder) gen_ssa_c() {
107108
// SSA -> C Backend
108109
for file in b.files {
110+
// Run AST transformations before SSA generation
111+
mut t := transform.Transformer.new()
112+
transformed_file := t.transform(file)
113+
109114
mut mod := ssa.Module.new('main')
110115
mut ssa_builder := ssa.Builder.new(mod)
111-
ssa_builder.build(file)
116+
ssa_builder.build(transformed_file)
112117
mod.optimize()
113118

114119
mut gen := c.Gen.new(mod)
@@ -145,9 +150,13 @@ fn (mut b Builder) gen_native(backend_arch pref.Arch) {
145150
arch := if backend_arch == .auto { b.pref.get_effective_arch() } else { backend_arch }
146151

147152
for file in b.files {
153+
// Run AST transformations before SSA generation
154+
mut t := transform.Transformer.new()
155+
transformed_file := t.transform(file)
156+
148157
mut mod := ssa.Module.new('main')
149158
mut ssa_builder := ssa.Builder.new(mod)
150-
ssa_builder.build(file)
159+
ssa_builder.build(transformed_file)
151160
mod.optimize()
152161

153162
obj_file := 'main.o'

‎vlib/v2/gen/arm64/arm64.v‎

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,23 +93,25 @@ fn (mut g Gen) gen_func(func ssa.Function) {
9393
instr := g.mod.instrs[val.index]
9494

9595
if instr.op == .alloca {
96-
// Reserve 64 bytes for data.
96+
// Calculate allocation size based on the type
97+
// The alloca result type is ptr(T), so get the element type
98+
ptr_type := g.mod.type_store.types[val.typ]
99+
elem_type := g.mod.type_store.types[ptr_type.elem_type]
100+
101+
// Calculate size: arrays use elem_count * 8, others use fixed 64 bytes
102+
alloc_size := if elem_type.kind == .array_t {
103+
elem_type.len * 8 // array length * element size (assuming 64-bit)
104+
} else {
105+
64 // Default for non-array types
106+
}
107+
97108
// Align to 16 bytes.
98109
slot_offset = (slot_offset + 15) & ~0xF
99-
slot_offset += 64
110+
slot_offset += alloc_size
100111
g.alloca_offsets[val_id] = -slot_offset
101112

102-
// CRITICAL FIX: Ensure the next instruction does not use the slot
113+
// Ensure the next instruction does not use the slot
103114
// overlapping with the base of the alloca data.
104-
// alloca_offsets points to the bottom of the block.
105-
// The next instruction would get -slot_offset (which is the bottom).
106-
// We advance slot_offset to skip the block completely.
107-
// Note: slot_offset is already at the bottom.
108-
// But we need to ensure the *next* usage doesn't pick this address.
109-
// Since stack_map assignment comes *before* this increment, the next instr
110-
// will use the current slot_offset.
111-
// If slot_offset is 96, next gets -96. Data is -96..-32.
112-
// So we need to bump it so next gets -104.
113115
slot_offset += 8
114116
}
115117

‎vlib/v2/gen/c/c.v‎

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,19 @@ fn (mut g Gen) gen_instr(val_id int) {
163163
g.sb.writeln('\t${dest} = ${src};')
164164
}
165165
.alloca {
166-
elem_type := g.type_name(g.mod.type_store.types[val.typ].elem_type)
167-
g.sb.writeln('\t${elem_type} _stack_${val.id};')
168-
g.sb.writeln('\t${res} = &_stack_${val.id};')
166+
elem_type_id := g.mod.type_store.types[val.typ].elem_type
167+
elem_type_info := g.mod.type_store.types[elem_type_id]
168+
if elem_type_info.kind == .array_t {
169+
// Array allocation: int64_t arr[N]
170+
arr_elem_type := g.type_name(elem_type_info.elem_type)
171+
g.sb.writeln('\t${arr_elem_type} _stack_${val.id}[${elem_type_info.len}];')
172+
g.sb.writeln('\t${res} = _stack_${val.id};')
173+
} else {
174+
// Regular allocation
175+
elem_type := g.type_name(elem_type_id)
176+
g.sb.writeln('\t${elem_type} _stack_${val.id};')
177+
g.sb.writeln('\t${res} = &_stack_${val.id};')
178+
}
169179
}
170180
.load {
171181
ptr_id := instr.operands[0]
@@ -300,12 +310,32 @@ fn (mut g Gen) gen_instr(val_id int) {
300310
fn (g Gen) type_name(id int) string {
301311
t := g.mod.type_store.types[id]
302312
match t.kind {
303-
.void_t { return 'void' }
304-
.int_t { return 'int${t.width}_t' }
305-
.float_t { return if t.width == 32 { 'float' } else { 'double' } }
306-
.ptr_t { return g.type_name(t.elem_type) + '*' }
307-
.struct_t { return 'Struct_${id}' }
308-
else { return 'void' }
313+
.void_t {
314+
return 'void'
315+
}
316+
.int_t {
317+
return 'int${t.width}_t'
318+
}
319+
.float_t {
320+
return if t.width == 32 { 'float' } else { 'double' }
321+
}
322+
.ptr_t {
323+
// Check if pointing to an array - if so, just use pointer to element type
324+
elem_t := g.mod.type_store.types[t.elem_type]
325+
if elem_t.kind == .array_t {
326+
return g.type_name(elem_t.elem_type) + '*'
327+
}
328+
return g.type_name(t.elem_type) + '*'
329+
}
330+
.array_t {
331+
return g.type_name(t.elem_type) + '*'
332+
} // Arrays decay to pointers
333+
.struct_t {
334+
return 'Struct_${id}'
335+
}
336+
else {
337+
return 'void'
338+
}
309339
}
310340
}
311341

0 commit comments

Comments
 (0)