Skip to content

lowering

Modules:

Classes:

ArrowVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: ArrowInt32ArrayLength) -> None
Source code in src/irx/builder/lowering/arrow.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@VisitorCore.visit.dispatch  # type: ignore[attr-defined,untyped-decorator]
def visit(self, node: astx.ArrowInt32ArrayLength) -> None:
    """
    title: Visit ArrowInt32ArrayLength nodes.
    parameters:
      node:
        type: astx.ArrowInt32ArrayLength
    """
    builder_new = self.require_runtime_symbol(
        "arrow", "irx_arrow_array_builder_int32_new"
    )
    append_int32 = self.require_runtime_symbol(
        "arrow", "irx_arrow_array_builder_append_int32"
    )
    finish_builder = self.require_runtime_symbol(
        "arrow", "irx_arrow_array_builder_finish"
    )
    array_length = self.require_runtime_symbol(
        "arrow", "irx_arrow_array_length"
    )
    release_array = self.require_runtime_symbol(
        "arrow", "irx_arrow_array_release"
    )

    builder_slot = self._llvm.ir_builder.alloca(
        self._llvm.ARROW_ARRAY_BUILDER_HANDLE_TYPE,
        name="arrow_builder_slot",
    )
    self._llvm.ir_builder.call(builder_new, [builder_slot])
    builder_handle = self._llvm.ir_builder.load(
        builder_slot, "arrow_builder"
    )

    for item in node.values:
        self.visit_child(item)
        value = safe_pop(self.result_stack)
        if value is None:
            raise Exception("Arrow helper expected an integer value")
        if not is_int_type(value.type):
            raise Exception(
                "Arrow helper supports only integer expressions"
            )

        if value.type.width < self._llvm.INT32_TYPE.width:
            value = self._llvm.ir_builder.sext(
                value, self._llvm.INT32_TYPE, "arrow_i32_promote"
            )
        elif value.type.width > self._llvm.INT32_TYPE.width:
            value = self._llvm.ir_builder.trunc(
                value, self._llvm.INT32_TYPE, "arrow_i32_trunc"
            )

        self._llvm.ir_builder.call(append_int32, [builder_handle, value])

    array_slot = self._llvm.ir_builder.alloca(
        self._llvm.ARROW_ARRAY_HANDLE_TYPE,
        name="arrow_array_slot",
    )
    self._llvm.ir_builder.call(
        finish_builder, [builder_handle, array_slot]
    )
    array_handle = self._llvm.ir_builder.load(array_slot, "arrow_array")
    length_i64 = self._llvm.ir_builder.call(
        array_length, [array_handle], "arrow_length"
    )
    self._llvm.ir_builder.call(release_array, [array_handle])

    length_i32 = self._llvm.ir_builder.trunc(
        length_i64, self._llvm.INT32_TYPE, "arrow_length_i32"
    )
    self.result_stack.append(length_i32)

BinaryOpVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: BitXorBinOp) -> None
Source code in src/irx/builder/lowering/binary_ops.py
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
@VisitorCore.visit.dispatch
def visit(self, node: BitXorBinOp) -> None:
    """
    title: Visit BitXorBinOp nodes.
    parameters:
      node:
        type: BitXorBinOp
    """
    llvm_lhs, llvm_rhs, _unsigned = self._load_binary_operands(
        node,
        unify_numeric=False,
    )
    if self._try_set_binary_op(llvm_lhs, llvm_rhs, node.op_code):
        return
    raise Exception(f"Binary op {node.op_code} not implemented yet.")

BufferVisitorMixin

Bases: VisitorMixinBase

Methods:

lower_buffer_element_pointer

lower_buffer_element_pointer(
    view: Value,
    indices: list[Value],
    element_type: DataType,
    *,
    index_nodes: list[AST],
    bounds_policy: BufferIndexBoundsPolicy = DEFAULT,
) -> Value
Source code in src/irx/builder/lowering/buffer.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
def lower_buffer_element_pointer(
    self,
    view: ir.Value,
    indices: list[ir.Value],
    element_type: astx.DataType,
    *,
    index_nodes: list[astx.AST],
    bounds_policy: BufferIndexBoundsPolicy = (
        BufferIndexBoundsPolicy.DEFAULT
    ),
) -> ir.Value:
    """
    title: Lower a buffer view indexed access to a typed element pointer.
    parameters:
      view:
        type: ir.Value
      indices:
        type: list[ir.Value]
      element_type:
        type: astx.DataType
      index_nodes:
        type: list[astx.AST]
      bounds_policy:
        type: BufferIndexBoundsPolicy
    returns:
      type: ir.Value
    """
    _ = bounds_policy
    if view.type != self._llvm.BUFFER_VIEW_TYPE:
        raise Exception("buffer view indexing requires a BufferViewType")
    if len(indices) != len(index_nodes):
        raise Exception("buffer view index lowering arity mismatch")

    element_llvm_type = self._llvm_type_for_ast_type(element_type)
    if element_llvm_type is None:
        raise Exception(
            "buffer view indexing has unsupported element type"
        )

    data = self._extract_buffer_view_data(view)
    strides = self._extract_buffer_view_strides(view)
    total_offset = self._extract_buffer_view_offset_bytes(view)

    for axis, (index, index_node) in enumerate(
        zip(indices, index_nodes, strict=True)
    ):
        index64 = self._normalize_buffer_index_value(index, index_node)
        stride_ptr = self._llvm.ir_builder.gep(
            strides,
            [ir.Constant(self._llvm.INT64_TYPE, axis)],
            name=f"irx_buffer_index_stride_ptr_{axis}",
        )
        stride = self._llvm.ir_builder.load(
            stride_ptr,
            name=f"irx_buffer_index_stride_{axis}",
        )
        scaled_index = self._llvm.ir_builder.mul(
            index64,
            stride,
            name=f"irx_buffer_index_scaled_{axis}",
        )
        total_offset = self._llvm.ir_builder.add(
            total_offset,
            scaled_index,
            name=f"irx_buffer_index_offset_{axis}",
        )

    byte_ptr = self._llvm.ir_builder.gep(
        data,
        [total_offset],
        name="irx_buffer_index_byte_ptr",
    )
    element_ptr_type = element_llvm_type.as_pointer()
    if byte_ptr.type == element_ptr_type:
        return byte_ptr
    return self._llvm.ir_builder.bitcast(
        byte_ptr,
        element_ptr_type,
        name="irx_buffer_index_element_ptr",
    )

visit

visit(node: BufferViewRelease) -> None
Source code in src/irx/builder/lowering/buffer.py
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
@VisitorCore.visit.dispatch
def visit(self, node: astx.BufferViewRelease) -> None:
    """
    title: Visit BufferViewRelease nodes.
    parameters:
      node:
        type: astx.BufferViewRelease
    """
    release = self.require_runtime_symbol(
        "buffer",
        "irx_buffer_view_release",
    )
    view_ptr = self._buffer_view_pointer_for_call(
        node.view,
        name="irx_buffer_release_view",
    )
    result = self._llvm.ir_builder.call(
        release,
        [view_ptr],
        name="irx_buffer_release_status",
    )
    self.result_stack.append(result)

ControlFlowVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: ContinueStmt) -> None
Source code in src/irx/builder/lowering/control_flow.py
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
@VisitorCore.visit.dispatch
def visit(self, node: astx.ContinueStmt) -> None:
    """
    title: Visit ContinueStmt nodes.
    parameters:
      node:
        type: astx.ContinueStmt
    """
    if not self.loop_stack:
        raise_lowering_error(
            "continue statement used outside an active loop",
            node=node,
            code=DiagnosticCodes.LOWERING_INVALID_CONTROL_FLOW,
            hint=(
                "use `continue` only inside while, for-count, or "
                "for-range loop bodies"
            ),
        )
    self._llvm.ir_builder.branch(self.loop_stack[-1].continue_target)

FunctionVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: FunctionReturn) -> None
Source code in src/irx/builder/lowering/functions.py
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
@VisitorCore.visit.dispatch
def visit(self, node: astx.FunctionReturn) -> None:
    """
    title: Visit FunctionReturn nodes.
    parameters:
      node:
        type: astx.FunctionReturn
    """
    return_resolution = self._semantic_return_resolution(node)
    if return_resolution.returns_void:
        self._llvm.ir_builder.ret_void()
        return

    if node.value is not None:
        self.visit_child(node.value)
        retval = require_lowered_value(
            safe_pop(self.result_stack),
            node=node.value,
            context="return expression",
        )
    else:
        retval = None

    if retval is None:
        raise_lowering_internal_error(
            "return expression did not lower to a value",
            node=node,
        )

    retval = self._cast_ast_value(
        retval,
        source_type=self._resolved_ast_type(node.value),
        target_type=return_resolution.expected_type,
    )
    fn_return_type = (
        self._llvm.ir_builder.function.function_type.return_type
    )
    if is_int_type(fn_return_type) and fn_return_type.width == 1:
        if is_int_type(retval.type) and retval.type.width != 1:
            retval = self._llvm.ir_builder.trunc(retval, ir.IntType(1))
    self._llvm.ir_builder.ret(retval)

LiteralVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: LiteralInt16) -> None
Source code in src/irx/builder/lowering/literals.py
726
727
728
729
730
731
732
733
734
735
736
@VisitorCore.visit.dispatch
def visit(self, node: astx.LiteralInt16) -> None:
    """
    title: Visit LiteralInt16 nodes.
    parameters:
      node:
        type: astx.LiteralInt16
    """
    self.result_stack.append(
        ir.Constant(self._llvm.INT16_TYPE, node.value)
    )

ModuleVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: ClassDefStmt) -> None
Source code in src/irx/builder/lowering/modules.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
@VisitorCore.visit.dispatch
def visit(self, node: astx.ClassDefStmt) -> None:
    """
    title: Visit ClassDefStmt nodes.
    parameters:
      node:
        type: astx.ClassDefStmt
    """
    class_key = semantic_class_key(node, node.name)
    semantic = getattr(node, "semantic", None)
    resolved_class = getattr(semantic, "resolved_class", None)
    layout = getattr(resolved_class, "layout", None)
    initialization = getattr(resolved_class, "initialization", None)
    if layout is None:
        raise Exception("codegen: unresolved class layout.")
    if initialization is None:
        raise Exception("codegen: unresolved class initialization.")

    field_types: list[ir.Type] = [
        self._llvm.OPAQUE_POINTER_TYPE for _ in layout.header_fields
    ]
    for field in layout.instance_fields:
        llvm_type = self._llvm_type_for_ast_type(field.member.type_)
        if llvm_type is None:
            raise Exception(
                f"codegen: Unknown LLVM type for class field "
                f"'{field.member.name}'."
            )
        field_types.append(llvm_type)

    self._ensure_identified_type(
        class_key,
        semantic_class_name(node, node.name),
        field_types,
    )

    for static_initializer in initialization.static_initializers:
        storage = static_initializer.storage
        llvm_type = self._llvm_type_for_ast_type(storage.member.type_)
        if llvm_type is None:
            raise Exception(
                f"codegen: Unknown LLVM type for static class field "
                f"'{storage.member.name}'."
            )
        initializer = self._literal_global_initializer(
            static_initializer.value,
            llvm_type,
        )
        existing = self._llvm.module.globals.get(storage.global_name)
        if existing is None:
            global_var = ir.GlobalVariable(
                self._llvm.module,
                llvm_type,
                name=storage.global_name,
            )
        else:
            global_var = existing
        global_var.linkage = "internal"
        global_var.global_constant = storage.member.is_constant
        global_var.initializer = initializer

    dispatch_table = self._dispatch_table_initializer(node)
    if dispatch_table is not None:
        dispatch_type, dispatch_initializer = dispatch_table
        dispatch_global = self._llvm.module.globals.get(
            layout.dispatch_global_name
        )
        if dispatch_global is None:
            dispatch_global = ir.GlobalVariable(
                self._llvm.module,
                dispatch_type,
                name=layout.dispatch_global_name,
            )
        dispatch_global.linkage = "internal"
        dispatch_global.global_constant = True
        dispatch_global.initializer = dispatch_initializer

    for method in node.methods:
        self.visit(method)

SystemVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: PrintExpr) -> None
Source code in src/irx/builder/lowering/system.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@VisitorCore.visit.dispatch
def visit(self, node: astx.PrintExpr) -> None:
    """
    title: Visit PrintExpr nodes.
    parameters:
      node:
        type: astx.PrintExpr
    """
    self.visit_child(node.message)
    message_value = safe_pop(self.result_stack)
    if message_value is None:
        raise Exception("Invalid message in PrintExpr")

    message_source_type = self._resolved_ast_type(node.message)
    message_type = message_value.type
    ptr: ir.Value
    if (
        isinstance(message_type, ir.PointerType)
        and message_type.pointee == self._llvm.INT8_TYPE
    ):
        ptr = message_value
    elif is_int_type(message_type):
        int_arg, int_fmt = self._normalize_int_for_printf(
            message_value,
            unsigned=is_unsigned_type(message_source_type)
            or is_boolean_type(message_source_type),
        )
        int_fmt_gv = self._get_or_create_format_global(int_fmt)
        ptr = self._snprintf_heap(int_fmt_gv, [int_arg])
    elif isinstance(
        message_type, (ir.HalfType, ir.FloatType, ir.DoubleType)
    ):
        if isinstance(message_type, (ir.HalfType, ir.FloatType)):
            float_arg = self._llvm.ir_builder.fpext(
                message_value, self._llvm.DOUBLE_TYPE, "print_to_double"
            )
        else:
            float_arg = message_value
        float_fmt_gv = self._get_or_create_format_global("%.6f")
        ptr = self._snprintf_heap(float_fmt_gv, [float_arg])
    else:
        raise Exception(
            f"Unsupported message type in PrintExpr: {message_type}"
        )

    puts_fn = self.require_runtime_symbol("libc", "puts")
    self._llvm.ir_builder.call(puts_fn, [ptr])
    self.result_stack.append(ir.Constant(self._llvm.INT32_TYPE, 0))

TemporalVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: LiteralDateTime) -> None
Source code in src/irx/builder/lowering/temporal.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
@VisitorCore.visit.dispatch
def visit(self, node: astx.LiteralDateTime) -> None:
    """
    title: Visit LiteralDateTime nodes.
    parameters:
      node:
        type: astx.LiteralDateTime
    """
    hour_minute_count = 2
    hour_minute_second_count = 3
    max_hour = 23
    max_minute_second = 59

    value = node.value.strip()
    if "T" in value:
        date_part, time_part = value.split("T", 1)
    elif " " in value:
        date_part, time_part = value.split(" ", 1)
    else:
        raise ValueError(
            f"LiteralDateTime: invalid format '{node.value}'. "
            "Expected 'YYYY-MM-DDTHH:MM[:SS]' (or space instead of 'T')."
        )

    if "." in time_part:
        raise ValueError(
            "LiteralDateTime: fractional seconds not supported in "
            f"'{node.value}'. Use LiteralTimestamp instead."
        )
    if time_part.endswith("Z") or "+" in time_part or "-" in time_part[2:]:
        raise ValueError(
            "LiteralDateTime: timezone offsets not supported in "
            f"'{node.value}'. Use LiteralTimestamp for timezones."
        )

    try:
        y_str, m_str, d_str = date_part.split("-")
        year = int(y_str)
        month = int(m_str)
        day = int(d_str)
    except Exception as exc:
        raise ValueError(
            f"LiteralDateTime: invalid date part in '{node.value}'. "
            "Expected 'YYYY-MM-DD'."
        ) from exc

    int32_min, int32_max = -(2**31), 2**31 - 1
    if not (int32_min <= year <= int32_max):
        raise ValueError(
            f"LiteralDateTime: year out of 32-bit range in '{node.value}'."
        )

    try:
        parts = time_part.split(":")
        if len(parts) not in (hour_minute_count, hour_minute_second_count):
            raise ValueError("time must be HH:MM or HH:MM:SS")
        hour = int(parts[0])
        minute = int(parts[1])
        second = (
            int(parts[2]) if len(parts) == hour_minute_second_count else 0
        )
    except Exception as exc:
        raise ValueError(
            f"LiteralDateTime: invalid time part in '{node.value}'. "
            "Expected 'HH:MM' or 'HH:MM:SS'."
        ) from exc

    if not (0 <= hour <= max_hour):
        raise ValueError(
            f"LiteralDateTime: hour out of range in '{node.value}'."
        )
    if not (0 <= minute <= max_minute_second):
        raise ValueError(
            f"LiteralDateTime: minute out of range in '{node.value}'."
        )
    if not (0 <= second <= max_minute_second):
        raise ValueError(
            f"LiteralDateTime: second out of range in '{node.value}'."
        )

    try:
        datetime(year, month, day)
        time_value(hour, minute, second)
    except ValueError as exc:
        raise ValueError(
            "LiteralDateTime: invalid calendar date/time in "
            f"'{node.value}'."
        ) from exc

    i32 = self._llvm.INT32_TYPE
    const_dt = ir.Constant(
        self._llvm.DATETIME_TYPE,
        [
            ir.Constant(i32, year),
            ir.Constant(i32, month),
            ir.Constant(i32, day),
            ir.Constant(i32, hour),
            ir.Constant(i32, minute),
            ir.Constant(i32, second),
        ],
    )
    self.result_stack.append(const_dt)

UnaryOpVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: UnaryOp) -> None
Source code in src/irx/builder/lowering/unary_ops.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
@VisitorCore.visit.dispatch  # type: ignore[attr-defined,untyped-decorator]
def visit(self, node: astx.UnaryOp) -> None:
    """
    title: Visit UnaryOp nodes.
    parameters:
      node:
        type: astx.UnaryOp
    """
    if node.op_code == "++":
        self.visit_child(node.operand)
        operand_val = safe_pop(self.result_stack)
        if operand_val is None:
            raise Exception("codegen: Invalid unary operand.")
        operand_name = (
            node.operand.name
            if isinstance(node.operand, astx.Identifier)
            else getattr(node.operand, "field_name", "field")
        )
        operand_key = semantic_assignment_key(node, operand_name)

        one = ir.Constant(operand_val.type, 1)
        if is_fp_type(operand_val.type):
            result = self._llvm.ir_builder.fadd(operand_val, one, "inctmp")
        else:
            result = self._llvm.ir_builder.add(operand_val, one, "inctmp")

        if (
            isinstance(node.operand, astx.Identifier)
            and operand_key in self.const_vars
        ):
            raise Exception(
                f"Cannot mutate '{operand_name}': declared as constant"
            )
        target_addr = self._lvalue_address(node.operand)
        self._llvm.ir_builder.store(result, target_addr)

        self.result_stack.append(result)
        return

    if node.op_code == "--":
        self.visit_child(node.operand)
        operand_val = safe_pop(self.result_stack)
        if operand_val is None:
            raise Exception("codegen: Invalid unary operand.")
        operand_name = (
            node.operand.name
            if isinstance(node.operand, astx.Identifier)
            else getattr(node.operand, "field_name", "field")
        )
        operand_key = semantic_assignment_key(node, operand_name)
        one = ir.Constant(operand_val.type, 1)
        if is_fp_type(operand_val.type):
            result = self._llvm.ir_builder.fsub(operand_val, one, "dectmp")
        else:
            result = self._llvm.ir_builder.sub(operand_val, one, "dectmp")

        if (
            isinstance(node.operand, astx.Identifier)
            and operand_key in self.const_vars
        ):
            raise Exception(
                f"Cannot mutate '{operand_name}': declared as constant"
            )
        target_addr = self._lvalue_address(node.operand)
        self._llvm.ir_builder.store(result, target_addr)

        self.result_stack.append(result)
        return

    if node.op_code == "!":
        self.visit_child(node.operand)
        val = safe_pop(self.result_stack)
        if val is None:
            raise Exception("codegen: Invalid unary operand.")
        if not is_int_type(val.type) or val.type.width != 1:
            raise Exception(
                "codegen: unary operator '!' must lower a Boolean operand."
            )

        result = self._llvm.ir_builder.xor(
            val,
            ir.Constant(self._llvm.BOOLEAN_TYPE, 1),
            "nottmp",
        )

        self.result_stack.append(result)
        return

    raise Exception(f"Unary operator {node.op_code} not implemented yet.")

VariableVisitorMixin

Bases: VisitorMixinBase

Methods:

visit

visit(node: InlineVariableDeclaration) -> None
Source code in src/irx/builder/lowering/variables.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
@VisitorCore.visit.dispatch
def visit(self, node: astx.InlineVariableDeclaration) -> None:
    """
    title: Visit InlineVariableDeclaration nodes.
    parameters:
      node:
        type: astx.InlineVariableDeclaration
    """
    symbol_key = semantic_symbol_key(node, node.name)
    if self.named_values.get(symbol_key):
        raise Exception(f"Identifier already declared: {node.name}")

    type_str = node.type_.__class__.__name__.lower()
    llvm_type = self._llvm_type_for_ast_type(node.type_)
    if llvm_type is None:
        raise Exception(
            "codegen: Unknown LLVM type for inline variable "
            f"'{node.name}'."
        )
    if node.value is not None:
        self.visit_child(node.value)
        init_val = safe_pop(self.result_stack)
        if init_val is None:
            raise Exception("Initializer code generation failed.")
        init_val = self._cast_ast_value(
            init_val,
            source_type=self._resolved_ast_type(node.value),
            target_type=node.type_,
        )
    elif isinstance(node.type_, astx.StructType):
        init_val = ir.Constant(llvm_type, None)
    elif isinstance(node.type_, astx.ClassType):
        init_val = ir.Constant(llvm_type, None)
    elif "float" in type_str:
        init_val = ir.Constant(self._llvm.get_data_type(type_str), 0.0)
    else:
        init_val = ir.Constant(self._llvm.get_data_type(type_str), 0)

    if type_str == "string":
        alloca = self.create_entry_block_alloca(node.name, "stringascii")
    else:
        alloca = self.create_entry_block_alloca(node.name, llvm_type)

    self._llvm.ir_builder.store(init_val, alloca)
    if node.mutability == astx.MutabilityKind.constant:
        self.const_vars.add(symbol_key)
    self.named_values[symbol_key] = alloca
    self.result_stack.append(init_val)