Skip to content

Commit 8e76156

Browse files
author
realorko
committed
Made sure structs work with any
1 parent c0297f5 commit 8e76156

7 files changed

Lines changed: 187 additions & 2 deletions

File tree

src/code_gen/code_gen_expr.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ static const char *get_type_tag_constant(TypeKind kind)
406406
case TYPE_BYTE: return "RT_ANY_BYTE";
407407
case TYPE_ARRAY: return "RT_ANY_ARRAY";
408408
case TYPE_FUNCTION: return "RT_ANY_FUNCTION";
409+
case TYPE_STRUCT: return "RT_ANY_STRUCT";
409410
case TYPE_ANY: return "RT_ANY_NIL"; /* any has no fixed tag */
410411
default: return "RT_ANY_NIL";
411412
}
@@ -495,6 +496,14 @@ static char *code_gen_is_expression(CodeGen *gen, Expr *expr)
495496
operand_code, type_tag, operand_code, elem_tag);
496497
}
497498

499+
/* For struct types, use the struct type ID for runtime type checking */
500+
if (is_expr->check_type->kind == TYPE_STRUCT)
501+
{
502+
int type_id = get_struct_type_id(is_expr->check_type);
503+
return arena_sprintf(gen->arena, "rt_any_is_struct_type(%s, %d)",
504+
operand_code, type_id);
505+
}
506+
498507
return arena_sprintf(gen->arena, "((%s).tag == %s)", operand_code, type_tag);
499508
}
500509

src/code_gen/code_gen_util.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,33 @@ const char *get_default_value(Type *type)
372372
* Any Type Boxing/Unboxing Helpers
373373
* ============================================================================ */
374374

375+
/**
376+
* Generate a consistent type ID for a struct type.
377+
* Uses a simple hash of the struct name to generate a unique integer.
378+
* This allows runtime type checking via `a is StructType` syntax.
379+
*/
380+
int get_struct_type_id(Type *struct_type)
381+
{
382+
if (struct_type == NULL || struct_type->kind != TYPE_STRUCT)
383+
{
384+
return 0;
385+
}
386+
const char *name = struct_type->as.struct_type.name;
387+
if (name == NULL)
388+
{
389+
return 0;
390+
}
391+
/* Simple djb2 hash function */
392+
unsigned long hash = 5381;
393+
int c;
394+
while ((c = *name++))
395+
{
396+
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
397+
}
398+
/* Ensure positive int result */
399+
return (int)(hash & 0x7FFFFFFF);
400+
}
401+
375402
const char *get_boxing_function(Type *type)
376403
{
377404
DEBUG_VERBOSE("Entering get_boxing_function");
@@ -405,6 +432,8 @@ const char *get_boxing_function(Type *type)
405432
return "rt_box_array";
406433
case TYPE_FUNCTION:
407434
return "rt_box_function";
435+
case TYPE_STRUCT:
436+
return "rt_box_struct";
408437
case TYPE_NIL:
409438
case TYPE_VOID:
410439
return "rt_box_nil";
@@ -448,6 +477,8 @@ const char *get_unboxing_function(Type *type)
448477
return "rt_unbox_array";
449478
case TYPE_FUNCTION:
450479
return "rt_unbox_function";
480+
case TYPE_STRUCT:
481+
return "rt_unbox_struct";
451482
default:
452483
return NULL;
453484
}
@@ -484,6 +515,8 @@ const char *get_element_type_tag(Type *element_type)
484515
return "RT_ANY_BYTE";
485516
case TYPE_ARRAY:
486517
return "RT_ANY_ARRAY";
518+
case TYPE_STRUCT:
519+
return "RT_ANY_STRUCT";
487520
case TYPE_ANY:
488521
return "RT_ANY_NIL"; /* any[] - element types vary */
489522
default:
@@ -519,6 +552,15 @@ char *code_gen_box_value(CodeGen *gen, const char *value_str, Type *value_type)
519552
return arena_sprintf(gen->arena, "%s(%s, %s)", box_func, value_str, elem_tag);
520553
}
521554

555+
/* Structs need arena, address, size, and type ID */
556+
if (value_type->kind == TYPE_STRUCT)
557+
{
558+
int type_id = get_struct_type_id(value_type);
559+
const char *struct_name = get_c_type(gen->arena, value_type);
560+
return arena_sprintf(gen->arena, "rt_box_struct(%s, &(%s), sizeof(%s), %d)",
561+
ARENA_VAR(gen), value_str, struct_name, type_id);
562+
}
563+
522564
return arena_sprintf(gen->arena, "%s(%s)", box_func, value_str);
523565
}
524566

@@ -550,6 +592,15 @@ char *code_gen_unbox_value(CodeGen *gen, const char *any_str, Type *target_type)
550592
return arena_sprintf(gen->arena, "(%s)%s(%s)", c_type, unbox_func, any_str);
551593
}
552594

595+
/* Structs need a cast and dereference (unbox returns void*) */
596+
if (target_type->kind == TYPE_STRUCT)
597+
{
598+
int type_id = get_struct_type_id(target_type);
599+
const char *struct_name = get_c_type(gen->arena, target_type);
600+
return arena_sprintf(gen->arena, "(*((%s *)rt_unbox_struct(%s, %d)))",
601+
struct_name, any_str, type_id);
602+
}
603+
553604
return arena_sprintf(gen->arena, "%s(%s)", unbox_func, any_str);
554605
}
555606

src/code_gen/code_gen_util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const char *get_rt_result_type(Type *type);
2727
const char *get_boxing_function(Type *type);
2828
const char *get_unboxing_function(Type *type);
2929
const char *get_element_type_tag(Type *element_type);
30+
int get_struct_type_id(Type *struct_type);
3031
char *code_gen_box_value(CodeGen *gen, const char *value_str, Type *value_type);
3132
char *code_gen_unbox_value(CodeGen *gen, const char *any_str, Type *target_type);
3233

src/runtime/runtime_any.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,18 @@ RtAny rt_box_function(void *fn) {
121121
return result;
122122
}
123123

124+
RtAny rt_box_struct(RtArena *arena, void *struct_data, size_t struct_size, int struct_type_id) {
125+
RtAny result;
126+
result.tag = RT_ANY_STRUCT;
127+
/* Allocate space in the arena and copy struct data */
128+
void *copy = rt_arena_alloc(arena, struct_size);
129+
memcpy(copy, struct_data, struct_size);
130+
result.value.obj = copy;
131+
/* Store struct type ID in element_tag (repurposed for structs) */
132+
result.element_tag = (RtAnyTag)struct_type_id;
133+
return result;
134+
}
135+
124136
/* ============================================================================
125137
* Unboxing Functions
126138
* ============================================================================ */
@@ -222,6 +234,20 @@ void *rt_unbox_function(RtAny value) {
222234
return value.value.fn;
223235
}
224236

237+
void *rt_unbox_struct(RtAny value, int expected_type_id) {
238+
if (value.tag != RT_ANY_STRUCT) {
239+
rt_any_type_error("struct", value);
240+
}
241+
/* Check struct type ID matches */
242+
int actual_type_id = (int)value.element_tag;
243+
if (actual_type_id != expected_type_id) {
244+
fprintf(stderr, "Type error: struct type mismatch (expected type id %d, got %d)\n",
245+
expected_type_id, actual_type_id);
246+
exit(1);
247+
}
248+
return value.value.obj;
249+
}
250+
225251
/* ============================================================================
226252
* Type Checking Functions
227253
* ============================================================================ */
@@ -241,6 +267,15 @@ bool rt_any_is_byte(RtAny value) { return value.tag == RT_ANY_BYTE; }
241267
bool rt_any_is_array(RtAny value) { return value.tag == RT_ANY_ARRAY; }
242268
bool rt_any_is_function(RtAny value) { return value.tag == RT_ANY_FUNCTION; }
243269

270+
bool rt_any_is_struct_type(RtAny value, int expected_type_id) {
271+
if (value.tag != RT_ANY_STRUCT) {
272+
return false;
273+
}
274+
/* Check if the struct type ID matches */
275+
int actual_type_id = (int)value.element_tag;
276+
return actual_type_id == expected_type_id;
277+
}
278+
244279
RtAnyTag rt_any_get_tag(RtAny value) {
245280
return value.tag;
246281
}
@@ -261,6 +296,7 @@ const char *rt_any_tag_name(RtAnyTag tag) {
261296
case RT_ANY_BYTE: return "byte";
262297
case RT_ANY_ARRAY: return "array";
263298
case RT_ANY_FUNCTION: return "function";
299+
case RT_ANY_STRUCT: return "struct";
264300
default: return "unknown";
265301
}
266302
}
@@ -333,6 +369,12 @@ bool rt_any_equals(RtAny a, RtAny b) {
333369
}
334370
case RT_ANY_FUNCTION:
335371
return a.value.fn == b.value.fn;
372+
case RT_ANY_STRUCT:
373+
/* For structs, compare type ID and pointer (identity comparison) */
374+
if (a.element_tag != b.element_tag) {
375+
return false; /* Different struct types */
376+
}
377+
return a.value.obj == b.value.obj;
336378
default:
337379
return false;
338380
}
@@ -392,6 +434,8 @@ char *rt_any_to_string(RtArena *arena, RtAny value) {
392434
return rt_arena_strdup(arena, buffer);
393435
case RT_ANY_FUNCTION:
394436
return rt_arena_strdup(arena, "[function]");
437+
case RT_ANY_STRUCT:
438+
return rt_arena_strdup(arena, "[struct]");
395439
default:
396440
return rt_arena_strdup(arena, "[unknown]");
397441
}
@@ -439,6 +483,11 @@ RtAny rt_any_promote(RtArena *target_arena, RtAny value) {
439483
case RT_ANY_FUNCTION:
440484
break;
441485

486+
case RT_ANY_STRUCT:
487+
/* Structs are arena-allocated; for now shallow copy pointer.
488+
* Full deep copy would require storing struct size metadata. */
489+
break;
490+
442491
default:
443492
break;
444493
}

src/runtime/runtime_any.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ typedef enum {
2727
RT_ANY_BOOL,
2828
RT_ANY_BYTE,
2929
RT_ANY_ARRAY,
30-
RT_ANY_FUNCTION
30+
RT_ANY_FUNCTION,
31+
RT_ANY_STRUCT /* Boxed struct value */
3132
} RtAnyTag;
3233

3334
/* The any type - a tagged union */
@@ -70,6 +71,7 @@ RtAny rt_box_bool(bool value);
7071
RtAny rt_box_byte(uint8_t value);
7172
RtAny rt_box_array(void *arr, RtAnyTag element_tag);
7273
RtAny rt_box_function(void *fn);
74+
RtAny rt_box_struct(RtArena *arena, void *struct_data, size_t struct_size, int struct_type_id);
7375

7476
/* ============================================================================
7577
* Unboxing Functions - Convert any to concrete types (panic on type mismatch)
@@ -88,6 +90,7 @@ bool rt_unbox_bool(RtAny value);
8890
uint8_t rt_unbox_byte(RtAny value);
8991
void *rt_unbox_array(RtAny value);
9092
void *rt_unbox_function(RtAny value);
93+
void *rt_unbox_struct(RtAny value, int expected_type_id);
9194

9295
/* ============================================================================
9396
* Type Checking Functions
@@ -108,6 +111,7 @@ bool rt_any_is_bool(RtAny value);
108111
bool rt_any_is_byte(RtAny value);
109112
bool rt_any_is_array(RtAny value);
110113
bool rt_any_is_function(RtAny value);
114+
bool rt_any_is_struct_type(RtAny value, int expected_type_id);
111115

112116
/* Get type tag */
113117
RtAnyTag rt_any_get_tag(RtAny value);

src/type_checker/type_checker_util.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ bool is_printable_type(Type *type)
101101
type->kind == TYPE_FLOAT || type->kind == TYPE_CHAR ||
102102
type->kind == TYPE_STRING || type->kind == TYPE_BOOL ||
103103
type->kind == TYPE_BYTE || type->kind == TYPE_ARRAY ||
104-
type->kind == TYPE_ANY);
104+
type->kind == TYPE_ANY || type->kind == TYPE_STRUCT);
105105
DEBUG_VERBOSE("Checking if type is printable: %s", result ? "true" : "false");
106106
return result;
107107
}

tests/integration/any_struct.sn

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Test struct support for any type
2+
3+
struct Point =>
4+
x: double
5+
y: double
6+
7+
struct Vector =>
8+
dx: double
9+
dy: double
10+
11+
fn main(): int =>
12+
# Test boxing a struct into any
13+
var p: Point = Point { x: 10.0, y: 20.0 }
14+
var a: any = p
15+
16+
# Test 'is' operator for correct struct type
17+
if a is Point =>
18+
print("a is Point: PASS\n")
19+
else =>
20+
print("a is Point: FAIL\n")
21+
return 1
22+
23+
# Test 'is' operator for incorrect struct type
24+
if a is Vector =>
25+
print("a is not Vector: FAIL\n")
26+
return 1
27+
else =>
28+
print("a is not Vector: PASS\n")
29+
30+
# Test 'as' operator to unbox struct
31+
var p2: Point = a as Point
32+
if p2.x == 10.0 =>
33+
if p2.y == 20.0 =>
34+
print("Unboxed Point values: PASS\n")
35+
else =>
36+
print("Unboxed Point y value: FAIL\n")
37+
return 1
38+
else =>
39+
print("Unboxed Point x value: FAIL\n")
40+
return 1
41+
42+
# Test boxing a different struct
43+
var v: Vector = Vector { dx: 1.0, dy: 2.0 }
44+
var b: any = v
45+
46+
if b is Vector =>
47+
print("b is Vector: PASS\n")
48+
else =>
49+
print("b is Vector: FAIL\n")
50+
return 1
51+
52+
if b is Point =>
53+
print("b is not Point: FAIL\n")
54+
return 1
55+
else =>
56+
print("b is not Point: PASS\n")
57+
58+
# Test unboxing the other struct
59+
var v2: Vector = b as Vector
60+
if v2.dx == 1.0 =>
61+
if v2.dy == 2.0 =>
62+
print("Unboxed Vector values: PASS\n")
63+
else =>
64+
print("Unboxed Vector dy value: FAIL\n")
65+
return 1
66+
else =>
67+
print("Unboxed Vector dx value: FAIL\n")
68+
return 1
69+
70+
print("All struct any tests passed!\n")
71+
return 0

0 commit comments

Comments
 (0)