l0_backend::Backend class

Defined in module l0_backend (l0_backend.py).

Language-agnostic code generation backend.

Orchestrates code generation by:

  • Managing compilation unit structure and emission order.
  • Resolving types and symbols.
  • Tracking variable scopes and lifetimes.
  • Scheduling cleanup operations.
  • Delegating all target-specific code emission to the emitter.

The backend decides WHAT to emit and WHEN, but not HOW (that's the emitter's job). This allows the same backend logic to work with different emitters (C, LLVM IR, WASM, etc.).

Public static attributes

static AnalysisResult analysis
static CEmitter emitter
static Optional current_module

Constructors, destructors, conversion operators

__post_init__(self)
Initialize emitter with analysis data.
_iter_body_stmts(self, Optional stmt[Stmt]) protected
Yield every statement reachable from stmt, recursing into block-bearing nodes.
_resolve_type_ref(self, TypeRef tref, str module_name) protected
Resolve an AST TypeRef into an l0_types.Type.
_int_type_size(self, Type src_ty) protected
Get the byte size of an integer builtin type.

Public functions

auto generate(self) -> str
Main entry point: generate complete C source for the compilation unit.
auto ice(self, str message, *Optional node[Node] = None) -> NoReturn
Raise an internal compiler error.
auto find_variant_decl(self, str module_name, str enum_name, str variant_name) -> Optional[EnumVariant]
Find the EnumVariant AST node for a given variant in an enum.

Public attributes

analysis
current_module

Protected functions

auto _fresh_label(self, str prefix) -> str
Generate a unique C label name.
auto _push_scope(self) -> ScopeContext
Enter a new scope.
auto _pop_scope(self) -> None
Exit current scope.
auto _types_equal(self, Type a, Type b) -> bool
Check if two types are structurally equal.
auto _is_int_assignable(self, Type typ) -> bool
Check if a type is assignable to an integer.
auto _is_binary_op_enabled(self, Type typ) -> bool
Check if a type supports binary operations.
auto _is_place_expr(self, Expr expr) -> bool
Check if an expression refers to an existing binding.
auto _is_unwrap_cast_from_place(self, Expr expr) -> bool
Check if a cast expression still borrows from an existing owner.
auto _needs_arc_temp(self, Expr expr) -> bool
Check if a non-place rvalue with ARC data needs temp materialization.
auto _should_materialize_arc_temp(self, Expr expr, Type expr_type) -> bool
Check if an ARC expression should be hoisted to a cleanup temp.
auto _materialize_arc_temp(self, str c_expr, Type expr_type) -> str
Materialize an ARC rvalue into a scope-owned temporary for automatic cleanup.
auto _has_side_effects(self, Expr expr) -> bool
Check if the expression has side effects or contains function calls.
auto _pointer_type_or_none(self, Optional ty[Type]) -> Optional[PointerType]
Return the represented pointer type for pointer-shaped values.
auto _sizeof_expr_for_type(self, Type ty) -> str
Return a C sizeof expression for the runtime access extent.
auto _alignof_expr_for_type(self, Type ty) -> str
Return a C alignment expression for the runtime access target.
auto _emit_checked_pointer_expr(self, str c_ptr_expr, Type ptr_ty, Optional node[Node] = None, str access_mode = "_RT_ACCESS_READ") -> str
Emit a pointer expression checked for one pointee-sized access.
auto _emit_pointer_index_lvalue(self, str c_base, str c_index, Type base_ty, Optional node[Node] = None, str access_mode = "_RT_ACCESS_WRITE") -> str
Emit a checked pointer-index lvalue expression.
auto _lookup_local_var_type(self, str var_name) -> Optional[Type]
Look up a local variable's type in the current scope chain.
auto _lookup_owned_local_name(self, VarRef expr) -> Optional[str]
Return the mangled local name when a VarRef resolves to an owned local binding.
auto _extract_value_type_dependencies(self, Type typ) -> Set[Tuple[str, str]]
Extract type dependencies for VALUE fields only.
auto _build_type_dependency_graph(self) -> Dict[Tuple[str, str], Set[Tuple[str, str]]]
Build dependency graph for type definitions.
auto _find_cycle_details(self, Dict[Tuple[str, str], Set]] graph[Tuple[str, str], List] unresolved[Tuple[str, str]) -> str
Find and format cycle details for error message.
auto _topological_sort(self, Dict[Tuple[str, str], Set]] graph[Tuple[str, str]) -> List[Tuple[str, str]]
Perform topological sort on type dependency graph using Kahn's algorithm.
auto _find_struct_decl(self, str module_name, str struct_name) -> Optional[StructDecl]
Find the StructDecl AST node for a given struct.
auto _find_enum_decl(self, str module_name, str enum_name) -> Optional[EnumDecl]
Find the EnumDecl AST node for a given enum.
auto _expect_expr_type(self, Expr expr) -> Type
Look up an expression's type and fail if missing.
auto _emit_line_directive(self, Node node) -> None
Emit #line directive if node has span info and context allows it.
auto _emit_let_declarations(self) -> None
Emit static global variables for top-level let declarations.
auto _emit_let_declaration(self, str module_name, LetDecl decl) -> None
Emit a single top-level let declaration as a static variable.
auto _emit_let_initializer(self, Expr expr, Type expected_type) -> str
Generate C initializer expression for a top-level let.
auto _emit_bare_variant_static_initializer(self, VarRef expr, Type expected_type) -> str
Emit a bare zero-argument enum variant for static initialization.
auto _emit_const_constructor(self, CallExpr expr, Type expected_type) -> str
Emit a constant struct or enum constructor for static initialization.
auto _emit_const_struct_constructor(self, CallExpr expr, StructType struct_type) -> str
Emit constant struct constructor for static initialization.
auto _emit_const_variant_constructor(self, CallExpr expr, EnumType enum_type) -> str
Emit constant enum variant constructor for static initialization.
auto _emit_function_declarations(self) -> None
Emit forward declarations for all functions.
auto _emit_function_declaration(self, str module_name, FuncDecl decl) -> None
Emit a single function declaration.
auto _emit_function_definitions(self) -> None
Emit function definitions (bodies).
auto _emit_function_definition(self, str module_name, FuncDecl decl) -> None
Emit a complete function definition with body.
auto _emit_main_wrapper_if_needed(self) -> None
If the entry module has a main function, emit a C main() wrapper.
auto _scope_chain_has_cleanup(self) -> bool
Check if any scope in the chain has cleanup requirements.
auto _emit_cleanup_for_return(self, Optional returned_var[str] = None) -> None
Emit cleanup logic for a return statement.
auto _emit_cleanup_for_loop_exit(self, *bool is_break) -> None
Emit cleanup for break/continue.
auto _emit_cleanup_at_scope_exit(self, ScopeContext scope) -> None
Emit cleanup at scope exit.
auto _emit_with_cleanup_from_scope(self, ScopeContext scope, str module_name) -> None
Emit with-statement cleanup for a scope.
auto _emit_value_cleanup(self, str c_expr, Type ty) -> None
Emit cleanup code for a by-value variable before reassignment.
auto _emit_struct_cleanup(self, str c_ptr_expr, StructType struct_type) -> None
Emit cleanup code for all owned fields in a struct.
auto _emit_enum_cleanup(self, str c_ptr_expr, EnumType enum_type) -> None
Emit cleanup code for owned fields in an enum's active variant.
auto _emit_block_sequence(self, Block block, str module_name) -> None
Emit statements in a block.
auto _emit_stmt(self, Stmt stmt, str module_name) -> None
Emit a single statement.
auto _emit_block(self, Block stmt, str module_name) -> Any
Emit a block statement with its own scope.
auto _emit_return(self, ReturnStmt stmt, Optional[Callable[[], None]] before_cleanup = None) -> Any
Emit a return statement with cleanup.
auto _register_inline_with_cleanup(self, ScopeContext scope, "WithItem" item) -> None
Register one inline with-item cleanup in LIFO order.
auto _emit_inline_with_header_item(self, "WithItem" item, str module_name, ScopeContext scope) -> None
Emit one inline with header item and register its cleanup at the committed point.
auto _emit_condition_branch(self, Expr expr, str true_label, str false_label) -> None
Emit control flow for one condition expression with short-circuit semantics.
auto _emit_condition_expr(self, Expr expr) -> str
Emit a top-level condition expression for direct statement headers.
auto _emit_condition_value(self, Expr expr) -> str
Evaluate a statement condition into a stable boolean temporary.
auto _emit_while(self, WhileStmt stmt, str module_name) -> Any
Emit a while loop.
auto _emit_for(self, ForStmt stmt, str module_name) -> Any
Emit a for loop.
auto _emit_if_else(self, IfStmt stmt, str module_name) -> Any
Emit an if-else statement.
auto _gen_if_else_branch(self, Stmt stmt, str module_name) -> bool
Emit a branch of an if/else.
auto _collect_reassigned_arc_params(self, FuncDecl decl, FuncType func_type) -> set
Collect the names of ARC-typed parameters reassigned syntactically in the body.
auto _emit_reassignment(self, AssignStmt stmt) -> None
Emit an assignment statement.
auto _emit_lvalue_with_caching(self, Expr target) -> str
Emit an lvalue expression, caching sub-expressions with side effects.
auto _emit_let(self, LetStmt stmt, str module_name) -> Any
Emit a local 'let' declaration.
auto _resolve_let_type(self, LetStmt stmt, str module_name) -> Type
Resolve concrete type for a let declaration.
auto _emit_with_cleanup_header_let_predecl(self, LetStmt stmt, str module_name) -> Optional[Type]
Predeclare a nullable with-header let for cleanup-block form.
auto _emit_with_cleanup_header_let_assign(self, LetStmt stmt, Type var_ty) -> None
Emit initializer assignment for a predeclared cleanup-block let.
auto _emit_retain_for_copied_value(self, str c_expr, Type ty) -> None
Emit retain operations for a copied owned value.
auto _emit_copy_expr_with_retains(self, str c_expr, Type ty) -> str
Materialize copied values in a temp and emit retain logic when needed.
auto _emit_match(self, MatchStmt stmt, str module_name) -> None
Emit a match statement as a switch on the tag field.
auto _emit_case(self, CaseStmt stmt, str module_name) -> None
Emit a case statement as a scalar switch or string if/else chain.
auto _emit_case_literal(self, Expr expr) -> str
Emit a constant literal for a case statement.
auto _emit_pattern_bindings(self, VariantPattern pattern, EnumType enum_type, ScopeContext arm_scope) -> None
Emit pattern variable bindings and add them to arm scope.
auto _emit_with(self, WithStmt stmt, str module_name) -> None
Emit a with statement.
auto _emit_drop(self, DropStmt stmt, str module_name) -> None
Emit drop statement with automatic cleanup of owned fields.
auto _try_emit_intrinsic(self, CallExpr expr) -> Optional[str]
Expand compiler intrinsics inline.
auto _emit_sizeof_intrinsic(self, CallExpr expr) -> str
Emit sizeof intrinsic.
auto _emit_ord_intrinsic(self, CallExpr expr) -> str
Emit ord(enum_value) intrinsic.
auto _try_emit_constructor(self, CallExpr expr) -> Optional[str]
Check if expr is a constructor call and emit appropriate initialization.
auto _emit_struct_constructor(self, CallExpr expr, StructType struct_type) -> str
Emit struct constructor as C designated initializer.
auto _emit_variant_constructor(self, CallExpr expr, EnumType enum_type) -> str
Emit enum variant constructor as C tagged union initializer.
auto _emit_new_expr(self, NewExpr expr) -> str
Emit a heap allocation new expression.
auto _convert_expr_with_expected_type(self, str c_expr, Optional natural_ty[Type], Type expected) -> str
Convert a pre-emitted expression into the expected type when required.
auto _emit_expr_with_expected_type(self, Expr e, Type expected) -> str
Emit expression with implicit type conversion to expected type.
auto _emit_owned_expr_with_expected_type(self, Expr e, Type expected) -> str
Emit expression for contexts that create a new owner.
auto _emit_expr(self, Expr expr, *bool is_statement = False) -> str
Emit an expression and return the C code.
auto _emit_unwrap(self, str c_dst, str c_inner, NullableType src_ty) -> str
Emit code to unwrap a nullable value.
auto _emit_binary_op(self, Expr expr_node, str expr_op, Expr expr_left, Expr expr_right, *bool for_condition = False) -> str
Emit code for a binary operation.
auto _lookup_symbol(self, str name, str current_module_name, Optional] module_path[List[str] = None) -> Optional[ Symbol]
Look up a symbol in the current module's environment.
auto _is_extern_function(self, Symbol sym) -> bool
Check if a symbol is an 'extern' function.

Protected static attributes

static Optional _current_func_result
static Optional _current_scope
static List _loop_cleanup_scope_stack
static int _switch_depth
static List _loop_label_stack
static int _label_counter
static bool _next_stmt_unreachable

Protected attributes

_current_scope
_emit_let_initializer
_next_stmt_unreachable
_current_func_result

Function documentation

l0_backend::Backend::_iter_body_stmts(self, Optional stmt[Stmt]) protected

Yield every statement reachable from stmt, recursing into block-bearing nodes.

Parameters
stmt The root statement (or None).
Returns An iterator over every Stmt in the sub-tree, including stmt itself.

l0_backend::Backend::_resolve_type_ref(self, TypeRef tref, str module_name) protected

Resolve an AST TypeRef into an l0_types.Type.

Parameters
tref The TypeRef AST node.
module_name Name of current module.
Returns The resolved Type.

This is needed so "let x: int? = null;" uses the declared type (int?) instead of the initializer type (null).

l0_backend::Backend::_int_type_size(self, Type src_ty) protected

Get the byte size of an integer builtin type.

Parameters
src_ty The type to check.
Returns Byte size (1 for byte, 4 for int).
Exceptions
InternalCompilerError If type is not an integer builtin.

str l0_backend::Backend::generate(self)

Main entry point: generate complete C source for the compilation unit.

Returns C source code as a string.
Exceptions
ValueError If there is no compilation unit or if there are semantic errors.

NoReturn l0_backend::Backend::ice(self, str message, *Optional node[Node] = None)

Raise an internal compiler error.

Parameters
message The error message.
node Optional AST node associated with the error.
Exceptions
InternalCompilerError Always raised with the provided message and location.

Optional[EnumVariant] l0_backend::Backend::find_variant_decl(self, str module_name, str enum_name, str variant_name)

Find the EnumVariant AST node for a given variant in an enum.

Parameters
module_name Name of module containing the enum.
enum_name Name of the enum.
variant_name Name of the variant.
Returns The EnumVariant AST node if found, otherwise None.

This is needed to get field names when binding pattern variables, since pattern variables are positional, but we need to access fields by name.

str l0_backend::Backend::_fresh_label(self, str prefix) protected

Generate a unique C label name.

Parameters
prefix Prefix for the label name.
Returns A unique label string.

ScopeContext l0_backend::Backend::_push_scope(self) protected

Enter a new scope.

Returns The newly created ScopeContext.

None l0_backend::Backend::_pop_scope(self) protected

Exit current scope.

Exceptions
InternalCompilerError If there is no current scope to pop.

bool l0_backend::Backend::_types_equal(self, Type a, Type b) protected

Check if two types are structurally equal.

Parameters
a First type.
b Second type.
Returns True if types are equal, False otherwise.

bool l0_backend::Backend::_is_int_assignable(self, Type typ) protected

Check if a type is assignable to an integer.

Parameters
typ The type to check.
Returns True if it's an 'int' or 'byte' builtin type.

bool l0_backend::Backend::_is_binary_op_enabled(self, Type typ) protected

Check if a type supports binary operations.

Parameters
typ The type to check.
Returns True if binary operations are supported for the type.

Currently only int, byte, and bool support binary operations.

bool l0_backend::Backend::_is_place_expr(self, Expr expr) protected

Check if an expression refers to an existing binding.

Parameters
expr The expression to check.
Returns True if expr refers to an existing binding (retain on copy). False if expr produces a fresh value (ownership transfer, no retain).

bool l0_backend::Backend::_is_unwrap_cast_from_place(self, Expr expr) protected

Check if a cast expression still borrows from an existing owner.

Parameters
expr The expression to check.
Returns True for non-owner-producing casts whose source is a place.

Outer parentheses are ownership-transparent. Owner-producing ARC value-optional wraps are excluded.

bool l0_backend::Backend::_needs_arc_temp(self, Expr expr) protected

Check if a non-place rvalue with ARC data needs temp materialization.

Parameters
expr The expression to check.
Returns True if temp materialization is needed.

String literals are static constants and don't need cleanup.

bool l0_backend::Backend::_should_materialize_arc_temp(self, Expr expr, Type expr_type) protected

Check if an ARC expression should be hoisted to a cleanup temp.

Parameters
expr The expression to check.
expr_type The type of the expression.
Returns True if the expression should be materialized into a temporary.

str l0_backend::Backend::_materialize_arc_temp(self, str c_expr, Type expr_type) protected

Materialize an ARC rvalue into a scope-owned temporary for automatic cleanup.

Parameters
c_expr The C expression string.
expr_type The type of the expression.
Returns The name of the generated temporary variable.

bool l0_backend::Backend::_has_side_effects(self, Expr expr) protected

Check if the expression has side effects or contains function calls.

Parameters
expr The expression to check.
Returns True if the expression has potential side effects.

Such expressions should be evaluated once and cached in a temporary to avoid multiple evaluation when used in contexts like assignment with ARC operations.

Optional[Type] l0_backend::Backend::_lookup_local_var_type(self, str var_name) protected

Look up a local variable's type in the current scope chain.

Parameters
var_name The name of the variable to look up.
Returns The variable's Type, or None if not found.

Searches declared_vars (includes both locals and parameters).

Optional[str] l0_backend::Backend::_lookup_owned_local_name(self, VarRef expr) protected

Return the mangled local name when a VarRef resolves to an owned local binding.

Parameters
expr The variable reference expression.
Returns The mangled local name if it's an owned binding, otherwise None.

Parameters are local VarRefs but are not owned by the callee, so they do not appear in owned_vars and return None.

Set[Tuple[str, str]] l0_backend::Backend::_extract_value_type_dependencies(self, Type typ) protected

Extract type dependencies for VALUE fields only.

Parameters
typ The type to extract dependencies from.
Returns Set of (module, name) tuples for types that must be defined first.

Value-type fields create dependencies (types must be fully defined). Pointer-type fields do NOT create dependencies (forward declarations suffice).

Examples:

  • StructType("main", "Point") -> {("main", "Point")}
  • EnumType("main", "Status") -> {("main", "Status")}
  • PointerType(StructType("main", "Node")) -> {} (no dependency, forward decl works)
  • NullableType(PointerType(...)) -> {} (pointer-optional, no dependency)
  • NullableType(BuiltinType("int")) -> {} (value-optional of builtin, no dependency)
  • NullableType(StructType("main", "Point")) -> {("main", "Point")} (value-optional of struct)
  • BuiltinType("int") -> {} (no dependency)

Dict[Tuple[str, str], Set[Tuple[str, str]]] l0_backend::Backend::_build_type_dependency_graph(self) protected

Build dependency graph for type definitions.

Returns Dict mapping (module, type_name) -> Set of (module, type_name) dependencies.

A type X depends on type Y if X has a VALUE field of type Y. Pointer fields do NOT create dependencies (forward declarations handle them).

str l0_backend::Backend::_find_cycle_details(self, Dict[Tuple[str, str], Set]] graph[Tuple[str, str], List] unresolved[Tuple[str, str]) protected

Find and format cycle details for error message.

Parameters
graph The type dependency graph.
unresolved List of unresolved nodes.
Returns A string describing the detected cycle details.

List[Tuple[str, str]] l0_backend::Backend::_topological_sort(self, Dict[Tuple[str, str], Set]] graph[Tuple[str, str]) protected

Perform topological sort on type dependency graph using Kahn's algorithm.

Parameters
graph The type dependency graph.
Returns List of (module, type_name) in dependency order (dependencies first).
Exceptions
InternalCompilerError On cycles (value-type cycles are impossible in valid L0).

Optional[StructDecl] l0_backend::Backend::_find_struct_decl(self, str module_name, str struct_name) protected

Find the StructDecl AST node for a given struct.

Parameters
module_name Name of the module.
struct_name Name of the struct.
Returns The StructDecl if found, otherwise None.

Optional[EnumDecl] l0_backend::Backend::_find_enum_decl(self, str module_name, str enum_name) protected

Find the EnumDecl AST node for a given enum.

Parameters
module_name Name of the module.
enum_name Name of the enum.
Returns The EnumDecl if found, otherwise None.

Type l0_backend::Backend::_expect_expr_type(self, Expr expr) protected

Look up an expression's type and fail if missing.

Parameters
expr The expression to look up.
Returns The resolved Type of the expression.
Exceptions
InternalCompilerError If the type is missing from the analysis.

None l0_backend::Backend::_emit_line_directive(self, Node node) protected

Emit #line directive if node has span info and context allows it.

Parameters
node The AST node containing span information.

None l0_backend::Backend::_emit_let_declaration(self, str module_name, LetDecl decl) protected

Emit a single top-level let declaration as a static variable.

Parameters
module_name Name of the module containing the declaration.
decl The LetDecl AST node.

str l0_backend::Backend::_emit_let_initializer(self, Expr expr, Type expected_type) protected

Generate C initializer expression for a top-level let.

Parameters
expr The initializer expression.
expected_type The expected type of the constant.
Returns A C initializer expression string.
Exceptions
InternalCompilerError If the initializer is not constant or supported.

Supports compile-time constant literals and struct/enum construction.

str l0_backend::Backend::_emit_const_constructor(self, CallExpr expr, Type expected_type) protected

Emit a constant struct or enum constructor for static initialization.

Parameters
expr The constructor call expression.
expected_type The expected struct or enum type.
Returns A C initializer string.
Exceptions
InternalCompilerError If symbol or constructor type is invalid.

Similar to _try_emit_constructor but only handles constant expressions.

str l0_backend::Backend::_emit_const_struct_constructor(self, CallExpr expr, StructType struct_type) protected

Emit constant struct constructor for static initialization.

Parameters
expr The constructor call expression.
struct_type The struct type.
Returns A C struct initializer string.
Exceptions
InternalCompilerError If struct info is missing or argument count mismatches.

str l0_backend::Backend::_emit_const_variant_constructor(self, CallExpr expr, EnumType enum_type) protected

Emit constant enum variant constructor for static initialization.

Parameters
expr The variant call expression.
enum_type The enum type.
Returns A C enum variant initializer string.
Exceptions
InternalCompilerError If variant info or declaration is missing.

None l0_backend::Backend::_emit_function_declaration(self, str module_name, FuncDecl decl) protected

Emit a single function declaration.

Parameters
module_name Name of the module.
decl The FuncDecl AST node.
Exceptions
InternalCompilerError If FuncType is missing.

None l0_backend::Backend::_emit_function_definition(self, str module_name, FuncDecl decl) protected

Emit a complete function definition with body.

Parameters
module_name Name of the module.
decl The FuncDecl AST node.
Exceptions
InternalCompilerError If scope is not reset or FuncType is missing.

None l0_backend::Backend::_emit_main_wrapper_if_needed(self) protected

If the entry module has a main function, emit a C main() wrapper.

This allows us to consistently mangle all L0 functions (including main) while still providing the expected C entry point.

bool l0_backend::Backend::_scope_chain_has_cleanup(self) protected

Check if any scope in the chain has cleanup requirements.

Returns True if any scope has a with-cleanup or owned ARC variables.

None l0_backend::Backend::_emit_cleanup_for_return(self, Optional returned_var[str] = None) protected

Emit cleanup logic for a return statement.

Parameters
returned_var Mangled name of variable being returned (to skip cleanup).

Walks up scope chain, executes any with-statement cleanup data, then cleans ALL owned variables (except return value). The with-cleanup runs first because user cleanup code may reference variables whose owned resources (e.g. string refcounts) are released by the automatic owned-var cleanup.

None l0_backend::Backend::_emit_cleanup_for_loop_exit(self, *bool is_break) protected

Emit cleanup for break/continue.

Parameters
is_break True if cleaning for 'break', False for 'continue'.
Exceptions
InternalCompilerError If called outside of a loop.

Walks from current scope up to and including the innermost loop cleanup target, executing any with-statement cleanup data along the way. The with-cleanup runs before owned-var cleanup (see _emit_cleanup_for_return for rationale).

None l0_backend::Backend::_emit_cleanup_at_scope_exit(self, ScopeContext scope) protected

Emit cleanup at scope exit.

Parameters
scope The scope being exited.

Only cleans variables declared in THIS scope that have owned fields.

None l0_backend::Backend::_emit_with_cleanup_from_scope(self, ScopeContext scope, str module_name) protected

Emit with-statement cleanup for a scope.

Parameters
scope The scope containing cleanup logic.
module_name Name of current module.

None l0_backend::Backend::_emit_value_cleanup(self, str c_expr, Type ty) protected

Emit cleanup code for a by-value variable before reassignment.

Parameters
c_expr C expression for the value (e.g., "x__v", "obj.field")
ty The type of the value being cleaned up

Similar to _emit_field_cleanup, but expects c_expr to be a direct value reference (not a pointer), so uses '.' instead of '->'.

None l0_backend::Backend::_emit_struct_cleanup(self, str c_ptr_expr, StructType struct_type) protected

Emit cleanup code for all owned fields in a struct.

Parameters
c_ptr_expr C expression evaluating to a pointer to the struct.
struct_type The struct type.

Recursively handles nested structs (by-value fields).

None l0_backend::Backend::_emit_enum_cleanup(self, str c_ptr_expr, EnumType enum_type) protected

Emit cleanup code for owned fields in an enum's active variant.

Parameters
c_ptr_expr C expression evaluating to a pointer to the enum.
enum_type The enum type.

Uses switch on tag to only clean up the fields that are actually present.

None l0_backend::Backend::_emit_block_sequence(self, Block block, str module_name) protected

Emit statements in a block.

Parameters
block The Block AST node.
module_name Name of current module.

None l0_backend::Backend::_emit_stmt(self, Stmt stmt, str module_name) protected

Emit a single statement.

Parameters
stmt The Stmt AST node.
module_name Name of current module.
Exceptions
InternalCompilerError If statement type is unsupported.

Any l0_backend::Backend::_emit_block(self, Block stmt, str module_name) protected

Emit a block statement with its own scope.

Parameters
stmt The Block AST node.
module_name Name of current module.

Any l0_backend::Backend::_emit_return(self, ReturnStmt stmt, Optional[Callable[[], None]] before_cleanup = None) protected

Emit a return statement with cleanup.

Parameters
stmt The ReturnStmt AST node.
before_cleanup Optional hook to run after the return value is evaluated and before scope cleanup is emitted.

None l0_backend::Backend::_emit_condition_branch(self, Expr expr, str true_label, str false_label) protected

Emit control flow for one condition expression with short-circuit semantics.

Parameters
expr Condition expression to lower.
true_label Jump target when the condition is true.
false_label Jump target when the condition is false.

This path is used only for statement conditions so ARC temps emitted by expression lowering stay inside the correct structural block instead of being hoisted into an enclosing "if (...)" or "while (...)" header.

str l0_backend::Backend::_emit_condition_value(self, Expr expr) protected

Evaluate a statement condition into a stable boolean temporary.

Parameters
expr Condition expression to lower.
Returns Name of the generated boolean temp.

The returned temp is safe to reference from an if/while header because any ARC temps created while evaluating the condition are scoped to the emitted condition block and cleaned before control continues.

Any l0_backend::Backend::_emit_while(self, WhileStmt stmt, str module_name) protected

Emit a while loop.

Parameters
stmt The WhileStmt AST node.
module_name Name of current module.

Any l0_backend::Backend::_emit_for(self, ForStmt stmt, str module_name) protected

Emit a for loop.

Parameters
stmt The ForStmt AST node.
module_name Name of current module.

Any l0_backend::Backend::_emit_if_else(self, IfStmt stmt, str module_name) protected

Emit an if-else statement.

Parameters
stmt The IfStmt AST node.
module_name Name of current module.

bool l0_backend::Backend::_gen_if_else_branch(self, Stmt stmt, str module_name) protected

Emit a branch of an if/else.

Parameters
stmt The statement in the branch.
module_name Name of current module.
Returns True if branch is unreachable at end.

set l0_backend::Backend::_collect_reassigned_arc_params(self, FuncDecl decl, FuncType func_type) protected

Collect the names of ARC-typed parameters reassigned syntactically in the body.

Parameters
decl The FuncDecl AST node.
func_type The resolved FuncType for the declaration.
Returns Set of parameter names (source names, not mangled) to retain at entry.

Any function whose body contains an AssignStmt whose target is a bare VarRef naming an ARC-typed parameter needs a defensive retain on that parameter at entry.

None l0_backend::Backend::_emit_reassignment(self, AssignStmt stmt) protected

Emit an assignment statement.

Parameters
stmt The AssignStmt AST node.
Exceptions
InternalCompilerError If types are missing or emission fails.

str l0_backend::Backend::_emit_lvalue_with_caching(self, Expr target) protected

Emit an lvalue expression, caching sub-expressions with side effects.

Parameters
target The lvalue expression.
Returns A C lvalue expression string.
Exceptions
InternalCompilerError If types are missing for complex lvalues.

For targets like *(func_call()), the pointer expression func_call() must be evaluated exactly once, not multiple times during release/assign/retain.

Any l0_backend::Backend::_emit_let(self, LetStmt stmt, str module_name) protected

Emit a local 'let' declaration.

Parameters
stmt The LetStmt AST node.
module_name Name of current module.
Exceptions
InternalCompilerError If type cannot be inferred.

Type l0_backend::Backend::_resolve_let_type(self, LetStmt stmt, str module_name) protected

Resolve concrete type for a let declaration.

Parameters
stmt The LetStmt AST node.
module_name Name of current module.
Returns The resolved Type.
Exceptions
InternalCompilerError If type cannot be inferred.

Optional[Type] l0_backend::Backend::_emit_with_cleanup_header_let_predecl(self, LetStmt stmt, str module_name) protected

Predeclare a nullable with-header let for cleanup-block form.

Parameters
stmt The LetStmt AST node.
module_name Name of current module.
Returns The Type if it was a nullable let, otherwise None.

Nullable lets are predeclared as null so cleanup code can reference them on header ? failure paths.

Non-nullable lets use the normal declaration+initializer path and return None here.

None l0_backend::Backend::_emit_with_cleanup_header_let_assign(self, LetStmt stmt, Type var_ty) protected

Emit initializer assignment for a predeclared cleanup-block let.

Parameters
stmt The LetStmt AST node.
var_ty The resolved type of the let.

None l0_backend::Backend::_emit_retain_for_copied_value(self, str c_expr, Type ty) protected

Emit retain operations for a copied owned value.

Parameters
c_expr C expression evaluating to the value.
ty The type of the value.
Exceptions
InternalCompilerError If variant decl is missing for enum types.

Used when copying from place expressions so source and destination own independent references.

str l0_backend::Backend::_emit_copy_expr_with_retains(self, str c_expr, Type ty) protected

Materialize copied values in a temp and emit retain logic when needed.

Parameters
c_expr C expression evaluating to the value.
ty The type of the value.
Returns The name of the temporary containing the copied and retained value.

None l0_backend::Backend::_emit_match(self, MatchStmt stmt, str module_name) protected

Emit a match statement as a switch on the tag field.

Parameters
stmt The MatchStmt AST node.
module_name Name of current module.
Exceptions
InternalCompilerError If types or patterns are unsupported.

None l0_backend::Backend::_emit_case(self, CaseStmt stmt, str module_name) protected

Emit a case statement as a scalar switch or string if/else chain.

Parameters
stmt The CaseStmt AST node.
module_name Name of current module.
Exceptions
InternalCompilerError If types or literals are unsupported.

str l0_backend::Backend::_emit_case_literal(self, Expr expr) protected

Emit a constant literal for a case statement.

Parameters
expr The literal expression.
Returns A C constant string.
Exceptions
InternalCompilerError If literal type is unsupported.

None l0_backend::Backend::_emit_pattern_bindings(self, VariantPattern pattern, EnumType enum_type, ScopeContext arm_scope) protected

Emit pattern variable bindings and add them to arm scope.

Parameters
pattern The variant pattern.
enum_type The enum type.
arm_scope The scope for the match arm.
Exceptions
InternalCompilerError If variant decl is missing.

None l0_backend::Backend::_emit_with(self, WithStmt stmt, str module_name) protected

Emit a with statement.

Parameters
stmt The WithStmt AST node.
module_name Name of current module.

Inline => form (LIFO cleanup): Emit init statements, then body, then cleanup statements in reverse order.

Cleanup block form: Emit init statements, then body, then cleanup block statements.

Cleanup is emitted at block end and before every early exit (return, break, continue). The scope stores cleanup data so _emit_cleanup_for_return and _emit_cleanup_for_loop_exit can emit it before leaving.

The body and cleanup block are each emitted as real nested C blocks so that any declarations inside them do not collide with the header scope (e.g., legal L0 shadowing like "let x" in both the header and body).

None l0_backend::Backend::_emit_drop(self, DropStmt stmt, str module_name) protected

Emit drop statement with automatic cleanup of owned fields.

Parameters
stmt The DropStmt AST node.
module_name Name of current module.
Exceptions
InternalCompilerError If variable is undefined or not a pointer.

For structs: releases all string fields. For enums: switches on tag, releases strings in active variant. Then calls the drop-finish helper to release the memory.

Optional[str] l0_backend::Backend::_try_emit_intrinsic(self, CallExpr expr) protected

Expand compiler intrinsics inline.

Parameters
expr The call expression to check.
Returns C code string if it is an intrinsic, otherwise None.

str l0_backend::Backend::_emit_sizeof_intrinsic(self, CallExpr expr) protected

Emit sizeof intrinsic.

Parameters
expr The sizeof call expression.
Returns A C sizeof expression string.
Exceptions
InternalCompilerError If target type cannot be resolved.

str l0_backend::Backend::_emit_ord_intrinsic(self, CallExpr expr) protected

Emit ord(enum_value) intrinsic.

Parameters
expr The ord call expression.
Returns A C expression string for the ordinal value.
Exceptions
InternalCompilerError If argument count is incorrect.

Returns 0-based ordinal of enum variant.

Optional[str] l0_backend::Backend::_try_emit_constructor(self, CallExpr expr) protected

Check if expr is a constructor call and emit appropriate initialization.

Parameters
expr The call expression to check.
Returns C code string if this is a constructor, otherwise None.

Struct: Point(1, 2) -> { .x = 1, .y = 2 } Enum: Int(42) -> { .tag = Expr_Int, .data = { .Int = { .value = 42 } } }

str l0_backend::Backend::_emit_struct_constructor(self, CallExpr expr, StructType struct_type) protected

Emit struct constructor as C designated initializer.

Parameters
expr The constructor call expression.
struct_type The struct type.
Returns A C struct initializer expression string.
Exceptions
InternalCompilerError If struct info is missing or argument count mismatches.

Point(1, 2) -> (struct l0_modulename_Point){ .x = 1, .y = 2 }

str l0_backend::Backend::_emit_variant_constructor(self, CallExpr expr, EnumType enum_type) protected

Emit enum variant constructor as C tagged union initializer.

Parameters
expr The variant call expression.
enum_type The enum type.
Returns A C variant initializer expression string.
Exceptions
InternalCompilerError If variant info or declaration is missing.

Example: Int(42) -> (struct l0_modulename_Int){ .tag = l0_modulename_Int_Int, .data.Int.value = 42 }

str l0_backend::Backend::_emit_new_expr(self, NewExpr expr) protected

Emit a heap allocation new expression.

Parameters
expr The NewExpr AST node.
Returns A C expression string for the newly allocated pointer.
Exceptions
InternalCompilerError If type or symbol resolution fails, or if args mismatch.

str l0_backend::Backend::_convert_expr_with_expected_type(self, str c_expr, Optional natural_ty[Type], Type expected) protected

Convert a pre-emitted expression into the expected type when required.

Parameters
c_expr The C expression string.
natural_ty The natural type of the expression.
expected The expected type.
Returns A C expression string, potentially wrapped or widened.

str l0_backend::Backend::_emit_expr_with_expected_type(self, Expr e, Type expected) protected

Emit expression with implicit type conversion to expected type.

Parameters
e The expression to emit.
expected The expected type.
Returns A C expression string.
Exceptions
InternalCompilerError If null literal is assigned to invalid type.

str l0_backend::Backend::_emit_owned_expr_with_expected_type(self, Expr e, Type expected) protected

Emit expression for contexts that create a new owner.

Parameters
e The expression to emit.
expected The expected type.
Returns A C expression string.

This applies retain-on-copy when a place expression is copied into an owned destination, while delegating regular type conversion to _emit_expr_with_expected_type.

str l0_backend::Backend::_emit_expr(self, Expr expr, *bool is_statement = False) protected

Emit an expression and return the C code.

Parameters
expr The expression to emit.
is_statement True if the expression is used as a statement.
Returns A C expression string, or empty string if used as a statement and no-op.
Exceptions
InternalCompilerError If expression type is unsupported or resolution fails.

str l0_backend::Backend::_emit_unwrap(self, str c_dst, str c_inner, NullableType src_ty) protected

Emit code to unwrap a nullable value.

Parameters
c_dst C type of destination.
c_inner C expression of nullable value.
src_ty The NullableType.
Returns C code for unwrapped value.

str l0_backend::Backend::_emit_binary_op(self, Expr expr_node, str expr_op, Expr expr_left, Expr expr_right, *bool for_condition = False) protected

Emit code for a binary operation.

Parameters
expr_node The BinaryOp AST node.
expr_op The operator string.
expr_left The left operand.
expr_right The right operand.
for_condition Whether to preserve condition-context code generation.
Returns C code string for the operation.
Exceptions
InternalCompilerError If types are missing, mismatch, or operation is unsupported.

Optional[ Symbol] l0_backend::Backend::_lookup_symbol(self, str name, str current_module_name, Optional] module_path[List[str] = None) protected

Look up a symbol in the current module's environment.

Parameters
name The name of the symbol.
current_module_name Name of current module.
module_path Optional module path for qualified names.
Returns The resolved Symbol, or None if not found.

This is used to determine which module a function is defined in so we can generate the correct mangled name.

bool l0_backend::Backend::_is_extern_function(self, Symbol sym) protected

Check if a symbol is an 'extern' function.

Parameters
sym The symbol to check.
Returns True if it is an extern function.