From 25d7e38f045bbbb1c970190f9df914eb79297735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Wed, 28 Jan 2026 15:43:14 +0000 Subject: [PATCH 01/19] Lowering: collect variant types along with record types In prep for generating structs for variant types in the C backend, start collecting variant types in the record type collector. Record type collector renamed as type collector as it collects both records and variants now. --- src/lib.rs | 2 +- src/lowering.rs | 6 +- src/record_collector.rs | 306 --------------------------------- src/type_collector.rs | 363 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 367 insertions(+), 310 deletions(-) delete mode 100644 src/record_collector.rs create mode 100644 src/type_collector.rs diff --git a/src/lib.rs b/src/lib.rs index 93121848..9d3eacf4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,12 +17,12 @@ mod mono_ast; mod monomorph; mod parser; mod parser_utils; -mod record_collector; mod scanner; mod scope_map; mod to_c; mod token; mod type_checker; +mod type_collector; mod utils; use lexgen_util::Loc; diff --git a/src/lowering.rs b/src/lowering.rs index e3b5814b..c5bf06c0 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -6,8 +6,8 @@ pub mod printer; use crate::ast; use crate::collections::*; use crate::mono_ast::{self as mono, Id, L, Loc}; -pub(crate) use crate::record_collector::RecordType; -use crate::record_collector::collect_records; +pub(crate) use crate::type_collector::RecordType; +use crate::type_collector::collect_anonymous_types; use crate::utils::loc_display; use smol_str::SmolStr; @@ -866,7 +866,7 @@ pub fn lower(mono_pgm: &mut mono::MonoPgm) -> LoweredPgm { } // Assign indices to record shapes. - let record_types = collect_records(mono_pgm); + let (record_types, _variant_types) = collect_anonymous_types(mono_pgm); // TODO: We could assign indices to records as we see them during lowering below. let mut record_indices: HashMap = Default::default(); diff --git a/src/record_collector.rs b/src/record_collector.rs deleted file mode 100644 index 8ec29b3d..00000000 --- a/src/record_collector.rs +++ /dev/null @@ -1,306 +0,0 @@ -use crate::ast::Id; -use crate::collections::*; -use crate::mono_ast as mono; - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct RecordType { - pub(crate) fields: OrdMap, -} - -impl RecordType { - pub fn unit() -> RecordType { - RecordType { - fields: Default::default(), - } - } -} - -pub fn collect_records(pgm: &mono::MonoPgm) -> HashSet { - let mut records: HashSet = Default::default(); - - for ty_map in pgm.funs.values() { - for (tys, fun) in ty_map { - for ty in tys { - visit_ty(ty, &mut records); - } - visit_fun_decl(fun, &mut records); - } - } - - for method_map in pgm.associated.values() { - for ty_map in method_map.values() { - for (tys, fun) in ty_map { - for ty in tys { - visit_ty(ty, &mut records); - } - visit_fun_decl(fun, &mut records); - } - } - } - - for ty in pgm.ty.values() { - for (tys, ty) in ty { - for ty in tys { - visit_ty(ty, &mut records); - } - visit_ty_decl(ty, &mut records); - } - } - - records -} - -fn visit_ty_decl(ty_decl: &mono::TypeDecl, records: &mut HashSet) { - match &ty_decl.rhs { - None => {} - - Some(mono::TypeDeclRhs::Sum(cons)) => { - for con in cons { - visit_fields(&con.fields, records); - } - } - - Some(mono::TypeDeclRhs::Product(fields)) => { - visit_fields(fields, records); - } - } -} - -fn visit_fun_decl(fun_decl: &mono::FunDecl, records: &mut HashSet) { - visit_fun_sig(&fun_decl.sig, records); - - if let Some(body) = &fun_decl.body { - for stmt in body { - visit_stmt(&stmt.node, records); - } - } -} - -fn visit_fun_sig(sig: &mono::FunSig, records: &mut HashSet) { - for (_param_name, param_ty) in &sig.params { - visit_ty(¶m_ty.node, records); - } - - match &sig.return_ty { - Some(ty) => { - visit_ty(&ty.node, records); - } - None => { - visit_ty(&mono::Type::unit(), records); - } - } - - if let Some(ty) = &sig.exceptions { - visit_ty(&ty.node, records); - } -} - -fn visit_fields(fields: &mono::ConFields, records: &mut HashSet) { - match fields { - mono::ConFields::Empty => {} - - mono::ConFields::Named(named_fields) => named_fields - .iter() - .for_each(|(_name, ty)| visit_ty(ty, records)), - - mono::ConFields::Unnamed(fields) => fields.iter().for_each(|ty| visit_ty(ty, records)), - } -} - -fn visit_ty(ty: &mono::Type, records: &mut HashSet) { - match ty { - mono::Type::Named(mono::NamedType { name: _, args }) => { - args.iter().for_each(|arg| visit_ty(arg, records)) - } - - mono::Type::Record { fields } => { - records.insert(RecordType { - fields: fields.clone(), - }); - } - - mono::Type::Variant { alts } => { - for mono::NamedType { name: _, args } in alts.values() { - args.iter().for_each(|arg| visit_ty(arg, records)) - } - } - - mono::Type::Fn(mono::FnType { args, ret, exn }) => { - match args { - mono::FunArgs::Positional(args) => { - args.iter().for_each(|ty| visit_ty(ty, records)); - } - mono::FunArgs::Named(args) => { - args.values().for_each(|ty| visit_ty(ty, records)); - } - } - visit_ty(ret, records); - visit_ty(exn, records); - } - } -} - -fn visit_stmt(stmt: &mono::Stmt, records: &mut HashSet) { - match stmt { - mono::Stmt::Break { .. } | mono::Stmt::Continue { .. } => {} - - mono::Stmt::Let(mono::LetStmt { lhs, rhs }) => { - visit_pat(&lhs.node, records); - visit_expr(&rhs.node, records); - } - - mono::Stmt::Assign(mono::AssignStmt { lhs, rhs }) => { - visit_expr(&lhs.node, records); - visit_expr(&rhs.node, records); - } - - mono::Stmt::Expr(expr) => visit_expr(expr, records), - - mono::Stmt::While(mono::WhileStmt { - label: _, - cond, - body, - }) => { - visit_expr(&cond.node, records); - for stmt in body { - visit_stmt(&stmt.node, records); - } - } - } -} - -fn visit_pat(pat: &mono::Pat, records: &mut HashSet) { - match pat { - mono::Pat::Var(_) | mono::Pat::Ignore | mono::Pat::Str(_) | mono::Pat::Char(_) => {} - - mono::Pat::Con(mono::ConPat { con: _, fields }) => { - for field in fields { - visit_pat(&field.node.node, records); - } - } - - mono::Pat::Record(mono::RecordPat { fields, ty }) => { - records.insert(RecordType { fields: ty.clone() }); - for field in fields { - visit_pat(&field.node.node, records); - } - } - - mono::Pat::Or(pat1, pat2) => { - visit_pat(&pat1.node, records); - visit_pat(&pat2.node, records); - } - - mono::Pat::Variant(mono::VariantPat { pat, ty }) => { - visit_ty(&mono::Type::Variant { alts: ty.clone() }, records); - visit_pat(&pat.node, records); - } - } -} - -fn visit_expr(expr: &mono::Expr, records: &mut HashSet) { - match expr { - mono::Expr::LocalVar(_, _) - | mono::Expr::TopVar(_) - | mono::Expr::ConSel(_) - | mono::Expr::AssocFnSel(_) - | mono::Expr::Int(_) - | mono::Expr::Char(_) - | mono::Expr::Str(_) => {} - - mono::Expr::FieldSel(mono::FieldSelExpr { - object, - field: _, - ty, - }) => { - visit_ty(ty, records); - visit_expr(&object.node, records); - } - - mono::Expr::Call(mono::CallExpr { fun, args, ty }) => { - visit_ty(ty, records); - visit_expr(&fun.node, records); - for arg in args { - visit_expr(&arg.expr.node, records); - } - } - - mono::Expr::BoolOr(left, right) | mono::Expr::BoolAnd(left, right) => { - visit_expr(&left.node, records); - visit_expr(&right.node, records); - } - - mono::Expr::Return(expr, ty) => { - visit_ty(ty, records); - visit_expr(&expr.node, records) - } - - mono::Expr::Match(mono::MatchExpr { - scrutinee, - alts, - ty, - }) => { - visit_ty(ty, records); - visit_expr(&scrutinee.node, records); - for alt in alts { - visit_pat(&alt.pat.node, records); - if let Some(guard) = &alt.guard { - visit_expr(&guard.node, records); - } - for stmt in &alt.rhs { - visit_stmt(&stmt.node, records); - } - } - } - - mono::Expr::If(mono::IfExpr { - branches, - else_branch, - ty, - }) => { - visit_ty(ty, records); - for (expr, stmts) in branches { - visit_expr(&expr.node, records); - for stmt in stmts { - visit_stmt(&stmt.node, records); - } - } - if let Some(else_branch) = else_branch { - for stmt in else_branch { - visit_stmt(&stmt.node, records); - } - } - } - - mono::Expr::Fn(mono::FnExpr { sig, body }) => { - visit_fun_sig(sig, records); - for stmt in body { - visit_stmt(&stmt.node, records); - } - } - - mono::Expr::Is(mono::IsExpr { expr, pat }) => { - visit_expr(&expr.node, records); - visit_pat(&pat.node, records); - } - - mono::Expr::Do(stmts, ty) => { - visit_ty(ty, records); - for stmt in stmts { - visit_stmt(&stmt.node, records); - } - } - - mono::Expr::Record(mono::RecordExpr { fields, ty }) => { - records.insert(RecordType { fields: ty.clone() }); - for (_field_name, field_expr) in fields { - visit_expr(&field_expr.node, records); - } - } - - mono::Expr::Variant(mono::VariantExpr { expr, ty }) => { - visit_ty(&mono::Type::Variant { alts: ty.clone() }, records); - visit_expr(&expr.node, records); - } - } -} diff --git a/src/type_collector.rs b/src/type_collector.rs new file mode 100644 index 00000000..47cf4b56 --- /dev/null +++ b/src/type_collector.rs @@ -0,0 +1,363 @@ +use crate::ast::Id; +use crate::collections::*; +use crate::mono_ast as mono; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct RecordType { + pub(crate) fields: OrdMap, +} + +impl RecordType { + pub fn unit() -> RecordType { + RecordType { + fields: Default::default(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct VariantType { + pub(crate) alts: OrdMap, +} + +pub fn collect_anonymous_types(pgm: &mono::MonoPgm) -> (HashSet, HashSet) { + let mut records: HashSet = Default::default(); + let mut variants: HashSet = Default::default(); + + for ty_map in pgm.funs.values() { + for (tys, fun) in ty_map { + for ty in tys { + visit_ty(ty, &mut records, &mut variants); + } + visit_fun_decl(fun, &mut records, &mut variants); + } + } + + for method_map in pgm.associated.values() { + for ty_map in method_map.values() { + for (tys, fun) in ty_map { + for ty in tys { + visit_ty(ty, &mut records, &mut variants); + } + visit_fun_decl(fun, &mut records, &mut variants); + } + } + } + + for ty in pgm.ty.values() { + for (tys, ty) in ty { + for ty in tys { + visit_ty(ty, &mut records, &mut variants); + } + visit_ty_decl(ty, &mut records, &mut variants); + } + } + + (records, variants) +} + +fn visit_ty_decl( + ty_decl: &mono::TypeDecl, + records: &mut HashSet, + variants: &mut HashSet, +) { + match &ty_decl.rhs { + None => {} + + Some(mono::TypeDeclRhs::Sum(cons)) => { + for con in cons { + visit_fields(&con.fields, records, variants); + } + } + + Some(mono::TypeDeclRhs::Product(fields)) => { + visit_fields(fields, records, variants); + } + } +} + +fn visit_fun_decl( + fun_decl: &mono::FunDecl, + records: &mut HashSet, + variants: &mut HashSet, +) { + visit_fun_sig(&fun_decl.sig, records, variants); + + if let Some(body) = &fun_decl.body { + for stmt in body { + visit_stmt(&stmt.node, records, variants); + } + } +} + +fn visit_fun_sig( + sig: &mono::FunSig, + records: &mut HashSet, + variants: &mut HashSet, +) { + for (_param_name, param_ty) in &sig.params { + visit_ty(¶m_ty.node, records, variants); + } + + match &sig.return_ty { + Some(ty) => { + visit_ty(&ty.node, records, variants); + } + None => { + visit_ty(&mono::Type::unit(), records, variants); + } + } + + if let Some(ty) = &sig.exceptions { + visit_ty(&ty.node, records, variants); + } +} + +fn visit_fields( + fields: &mono::ConFields, + records: &mut HashSet, + variants: &mut HashSet, +) { + match fields { + mono::ConFields::Empty => {} + + mono::ConFields::Named(named_fields) => named_fields + .iter() + .for_each(|(_name, ty)| visit_ty(ty, records, variants)), + + mono::ConFields::Unnamed(fields) => { + fields.iter().for_each(|ty| visit_ty(ty, records, variants)) + } + } +} + +fn visit_ty( + ty: &mono::Type, + records: &mut HashSet, + variants: &mut HashSet, +) { + match ty { + mono::Type::Named(ty) => visit_named_ty(ty, records, variants), + + mono::Type::Record { fields } => { + fields + .values() + .for_each(|field| visit_ty(field, records, variants)); + records.insert(RecordType { + fields: fields.clone(), + }); + } + + mono::Type::Variant { alts } => { + alts.values() + .for_each(|ty| visit_named_ty(ty, records, variants)); + variants.insert(VariantType { alts: alts.clone() }); + } + + mono::Type::Fn(mono::FnType { args, ret, exn }) => { + match args { + mono::FunArgs::Positional(args) => { + args.iter().for_each(|ty| visit_ty(ty, records, variants)); + } + mono::FunArgs::Named(args) => { + args.values().for_each(|ty| visit_ty(ty, records, variants)); + } + } + visit_ty(ret, records, variants); + visit_ty(exn, records, variants); + } + } +} + +fn visit_named_ty( + ty: &mono::NamedType, + records: &mut HashSet, + variants: &mut HashSet, +) { + ty.args + .iter() + .for_each(|arg| visit_ty(arg, records, variants)) +} + +fn visit_stmt( + stmt: &mono::Stmt, + records: &mut HashSet, + variants: &mut HashSet, +) { + match stmt { + mono::Stmt::Break { .. } | mono::Stmt::Continue { .. } => {} + + mono::Stmt::Let(mono::LetStmt { lhs, rhs }) => { + visit_pat(&lhs.node, records, variants); + visit_expr(&rhs.node, records, variants); + } + + mono::Stmt::Assign(mono::AssignStmt { lhs, rhs }) => { + visit_expr(&lhs.node, records, variants); + visit_expr(&rhs.node, records, variants); + } + + mono::Stmt::Expr(expr) => visit_expr(expr, records, variants), + + mono::Stmt::While(mono::WhileStmt { + label: _, + cond, + body, + }) => { + visit_expr(&cond.node, records, variants); + for stmt in body { + visit_stmt(&stmt.node, records, variants); + } + } + } +} + +fn visit_pat( + pat: &mono::Pat, + records: &mut HashSet, + variants: &mut HashSet, +) { + match pat { + mono::Pat::Var(_) | mono::Pat::Ignore | mono::Pat::Str(_) | mono::Pat::Char(_) => {} + + mono::Pat::Con(mono::ConPat { con: _, fields }) => { + for field in fields { + visit_pat(&field.node.node, records, variants); + } + } + + mono::Pat::Or(pat1, pat2) => { + visit_pat(&pat1.node, records, variants); + visit_pat(&pat2.node, records, variants); + } + + mono::Pat::Record(mono::RecordPat { fields, ty }) => { + ty.values().for_each(|ty| visit_ty(ty, records, variants)); + records.insert(RecordType { fields: ty.clone() }); + for field in fields { + visit_pat(&field.node.node, records, variants); + } + } + + mono::Pat::Variant(mono::VariantPat { pat, ty }) => { + ty.values() + .for_each(|ty| visit_named_ty(ty, records, variants)); + variants.insert(VariantType { alts: ty.clone() }); + visit_pat(&pat.node, records, variants); + } + } +} + +fn visit_expr( + expr: &mono::Expr, + records: &mut HashSet, + variants: &mut HashSet, +) { + match expr { + mono::Expr::LocalVar(_, _) + | mono::Expr::TopVar(_) + | mono::Expr::ConSel(_) + | mono::Expr::AssocFnSel(_) + | mono::Expr::Int(_) + | mono::Expr::Char(_) + | mono::Expr::Str(_) => {} + + mono::Expr::FieldSel(mono::FieldSelExpr { + object, + field: _, + ty, + }) => { + visit_ty(ty, records, variants); + visit_expr(&object.node, records, variants); + } + + mono::Expr::Call(mono::CallExpr { fun, args, ty }) => { + visit_ty(ty, records, variants); + visit_expr(&fun.node, records, variants); + for arg in args { + visit_expr(&arg.expr.node, records, variants); + } + } + + mono::Expr::BoolOr(left, right) | mono::Expr::BoolAnd(left, right) => { + visit_expr(&left.node, records, variants); + visit_expr(&right.node, records, variants); + } + + mono::Expr::Return(expr, ty) => { + visit_ty(ty, records, variants); + visit_expr(&expr.node, records, variants) + } + + mono::Expr::Match(mono::MatchExpr { + scrutinee, + alts, + ty, + }) => { + visit_ty(ty, records, variants); + visit_expr(&scrutinee.node, records, variants); + for alt in alts { + visit_pat(&alt.pat.node, records, variants); + if let Some(guard) = &alt.guard { + visit_expr(&guard.node, records, variants); + } + for stmt in &alt.rhs { + visit_stmt(&stmt.node, records, variants); + } + } + } + + mono::Expr::If(mono::IfExpr { + branches, + else_branch, + ty, + }) => { + visit_ty(ty, records, variants); + for (expr, stmts) in branches { + visit_expr(&expr.node, records, variants); + for stmt in stmts { + visit_stmt(&stmt.node, records, variants); + } + } + if let Some(else_branch) = else_branch { + for stmt in else_branch { + visit_stmt(&stmt.node, records, variants); + } + } + } + + mono::Expr::Fn(mono::FnExpr { sig, body }) => { + visit_fun_sig(sig, records, variants); + for stmt in body { + visit_stmt(&stmt.node, records, variants); + } + } + + mono::Expr::Is(mono::IsExpr { expr, pat }) => { + visit_expr(&expr.node, records, variants); + visit_pat(&pat.node, records, variants); + } + + mono::Expr::Do(stmts, ty) => { + visit_ty(ty, records, variants); + for stmt in stmts { + visit_stmt(&stmt.node, records, variants); + } + } + + mono::Expr::Record(mono::RecordExpr { fields, ty }) => { + ty.values().for_each(|ty| visit_ty(ty, records, variants)); + records.insert(RecordType { fields: ty.clone() }); + for (_field_name, field_expr) in fields { + visit_expr(&field_expr.node, records, variants); + } + } + + mono::Expr::Variant(mono::VariantExpr { expr, ty }) => { + ty.values() + .for_each(|ty| visit_named_ty(ty, records, variants)); + variants.insert(VariantType { alts: ty.clone() }); + visit_expr(&expr.node, records, variants); + } + } +} From d9bdc430f1664fc8913bd1e2d6fc3f7fa9ffbbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Sun, 1 Feb 2026 09:53:16 +0000 Subject: [PATCH 02/19] Start assigning heap obj indices to variants --- src/interpreter.rs | 2 +- src/lowering.rs | 14 +++++++++++--- src/lowering/printer.rs | 2 ++ src/to_c.rs | 20 +++++++++++++++++++- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 807a3829..1367e052 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -77,7 +77,7 @@ impl Pgm { // Allocate singletons for constructors without fields. for (i, heap_obj) in heap_objs.iter().enumerate() { match heap_obj { - HeapObj::Builtin(_) => continue, + HeapObj::Builtin(_) | HeapObj::Variant(_) => continue, HeapObj::Source(source_con) => { if source_con.fields.is_empty() { diff --git a/src/lowering.rs b/src/lowering.rs index c5bf06c0..6c8cfe0f 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -6,8 +6,8 @@ pub mod printer; use crate::ast; use crate::collections::*; use crate::mono_ast::{self as mono, Id, L, Loc}; -pub(crate) use crate::type_collector::RecordType; use crate::type_collector::collect_anonymous_types; +pub(crate) use crate::type_collector::{RecordType, VariantType}; use crate::utils::loc_display; use smol_str::SmolStr; @@ -291,6 +291,7 @@ pub enum HeapObj { Builtin(BuiltinConDecl), Source(SourceConDecl), Record(RecordType), + Variant(VariantType), } #[derive(Debug)] @@ -866,9 +867,8 @@ pub fn lower(mono_pgm: &mut mono::MonoPgm) -> LoweredPgm { } // Assign indices to record shapes. - let (record_types, _variant_types) = collect_anonymous_types(mono_pgm); + let (record_types, variant_types) = collect_anonymous_types(mono_pgm); - // TODO: We could assign indices to records as we see them during lowering below. let mut record_indices: HashMap = Default::default(); for record_type in record_types { let idx = next_con_idx; @@ -877,6 +877,14 @@ pub fn lower(mono_pgm: &mut mono::MonoPgm) -> LoweredPgm { lowered_pgm.heap_objs.push(HeapObj::Record(record_type)); } + let mut variant_indices: HashMap = Default::default(); + for variant_type in variant_types { + let idx = next_con_idx; + next_con_idx = HeapObjIdx(next_con_idx.0 + 1); + variant_indices.insert(variant_type.clone(), idx); + lowered_pgm.heap_objs.push(HeapObj::Variant(variant_type)); + } + lowered_pgm.unit_con_idx = *record_indices .get(&RecordType::unit()) .unwrap_or_else(|| panic!("BUG: Unit record not defined {record_indices:#?}")); diff --git a/src/lowering/printer.rs b/src/lowering/printer.rs index 79a9743a..0e4d0211 100644 --- a/src/lowering/printer.rs +++ b/src/lowering/printer.rs @@ -37,6 +37,8 @@ impl LoweredPgm { } HeapObj::Record(record) => write!(buf, "{record:?}").unwrap(), + + HeapObj::Variant(variant) => write!(buf, "{variant:?}").unwrap(), } buf.push('\n'); } diff --git a/src/to_c.rs b/src/to_c.rs index 3be891d5..d9a371a5 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -116,6 +116,10 @@ pub(crate) fn to_c(pgm: &LoweredPgm, main: &str) -> String { let struct_name = record_struct_name(record); wln!(p, "typedef struct {} {};", struct_name, struct_name); } + HeapObj::Variant(variant) => { + let struct_name = variant_struct_name(variant); + wln!(p, "typedef struct {struct_name} {struct_name};"); + } HeapObj::Builtin(BuiltinConDecl::Array { t }) => { let struct_name = array_struct_name(t); wln!(p, "typedef struct {struct_name} {struct_name};"); @@ -474,6 +478,7 @@ fn heap_obj_to_c(heap_obj: &HeapObj, tag: u32, p: &mut Printer) { HeapObj::Builtin(builtin) => builtin_con_decl_to_c(builtin, tag, p), HeapObj::Source(source_con) => source_con_decl_to_c(source_con, tag, p), HeapObj::Record(record) => record_decl_to_c(record, tag, p), + HeapObj::Variant(_) => todo!(), } } @@ -583,10 +588,15 @@ fn array_struct_name(t: &mono::Type) -> String { name } +fn variant_struct_name(t: &VariantType) -> String { + todo!() +} + fn heap_obj_struct_name(pgm: &LoweredPgm, idx: HeapObjIdx) -> String { match &pgm.heap_objs[idx.0 as usize] { HeapObj::Source(source_con) => source_con_struct_name(source_con), HeapObj::Record(record) => record_struct_name(record), + HeapObj::Variant(variant) => variant_struct_name(variant), HeapObj::Builtin(_) => panic!("Builtin in heap_obj_struct_name"), } } @@ -595,6 +605,7 @@ fn heap_obj_tag_name(pgm: &LoweredPgm, idx: HeapObjIdx) -> String { match &pgm.heap_objs[idx.0 as usize] { HeapObj::Source(source_con) => source_con_tag_name(source_con), HeapObj::Record(record) => format!("TAG_{}", record_struct_name(record)), + HeapObj::Variant(_) => panic!("Variants don't have runtime tags"), HeapObj::Builtin(_) => panic!("Builtin in heap_obj_tag_name"), } } @@ -624,6 +635,7 @@ fn heap_obj_singleton_name(pgm: &LoweredPgm, idx: HeapObjIdx) -> String { match &pgm.heap_objs[idx.0 as usize] { HeapObj::Source(source_con) => source_con_singleton_name(source_con), HeapObj::Record(record) => format!("_singleton_{}", record_struct_name(record)), + HeapObj::Variant(_) => panic!("Variants don't have singletons"), HeapObj::Builtin(_) => panic!("Builtin heap objects don't have singletons"), } } @@ -2144,7 +2156,7 @@ fn top_sort( output.push(std::iter::once(HeapObjIdx(heap_obj_idx as u32)).collect()); Some(idx_gen.next()) } - HeapObj::Source(_) | HeapObj::Record(_) => None, + HeapObj::Source(_) | HeapObj::Record(_) | HeapObj::Variant(_) => None, }, low_link: None, on_stack: false, @@ -2280,6 +2292,12 @@ fn heap_obj_deps( type_heap_obj_deps(type_objs, record_objs, field, &mut deps); } } + + HeapObj::Variant(variant_type) => { + for named_ty in variant_type.alts.values() { + named_type_heap_obj_deps(type_objs, named_ty, &mut deps); + } + } } deps From 77e8bd6baf6038a3a7b1aca7fdfb8f7b42d063ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Sun, 1 Feb 2026 13:34:11 +0000 Subject: [PATCH 03/19] Start defining variant structs --- src/to_c.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/to_c.rs b/src/to_c.rs index d9a371a5..55656d15 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -478,7 +478,7 @@ fn heap_obj_to_c(heap_obj: &HeapObj, tag: u32, p: &mut Printer) { HeapObj::Builtin(builtin) => builtin_con_decl_to_c(builtin, tag, p), HeapObj::Source(source_con) => source_con_decl_to_c(source_con, tag, p), HeapObj::Record(record) => record_decl_to_c(record, tag, p), - HeapObj::Variant(_) => todo!(), + HeapObj::Variant(variant) => variant_decl_to_c(variant, tag, p), } } @@ -580,6 +580,15 @@ fn record_struct_name(record: &RecordType) -> String { name } +fn variant_struct_name(variant: &VariantType) -> String { + let mut name = String::from("Variant"); + for named_ty in variant.alts.values() { + name.push('_'); + named_ty_to_c(named_ty, &mut name); + } + name +} + fn array_struct_name(t: &mono::Type) -> String { let mut name = String::from("Array_"); let t = c_ty(t); @@ -588,10 +597,6 @@ fn array_struct_name(t: &mono::Type) -> String { name } -fn variant_struct_name(t: &VariantType) -> String { - todo!() -} - fn heap_obj_struct_name(pgm: &LoweredPgm, idx: HeapObjIdx) -> String { match &pgm.heap_objs[idx.0 as usize] { HeapObj::Source(source_con) => source_con_struct_name(source_con), @@ -686,6 +691,10 @@ fn record_decl_to_c(record: &RecordType, tag: u32, p: &mut Printer) { wln!(p, "}} {};", struct_name); } +fn variant_decl_to_c(variant: &VariantType, tag: u32, p: &mut Printer) { + let struct_name = variant_struct_name(variant); +} + fn named_ty_to_c(named_ty: &mono::NamedType, out: &mut String) { out.push_str(&named_ty.name); for arg in &named_ty.args { From 4dfe8faccd9ccb49011f9d4883a63dc5c9376598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Sun, 1 Feb 2026 14:03:57 +0000 Subject: [PATCH 04/19] Updates --- src/interpreter.rs | 1 + src/lowering.rs | 11 ++++++- src/to_c.rs | 79 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 1367e052..fe643ead 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -59,6 +59,7 @@ impl Pgm { closures, type_objs: _, record_objs: _, + variant_objs: _, true_con_idx, false_con_idx, char_con_idx, diff --git a/src/lowering.rs b/src/lowering.rs index 6c8cfe0f..cf09c101 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -23,9 +23,16 @@ pub struct LoweredPgm { /// Product types will have one index per type. Sum types may have multiple. pub type_objs: HashMap, TypeObjs>>, - /// Maps record types to their heap object indices. + /// For C backend: maps record types to their heap object indices. pub record_objs: HashMap, + /// For C backend: maps variant types to their heap object indices. + /// + /// Note: variants don't have their own tags, they use the tags of the types in the variant + /// instead. These tags are to make it easy to refer to a variant type in AST nodes, dependency + /// analysis etc. + pub variant_objs: HashMap, + // Ids of some special cons that the interpreter needs to know. // // Note that for product types, type and con tags are the same. @@ -689,6 +696,7 @@ pub fn lower(mono_pgm: &mut mono::MonoPgm) -> LoweredPgm { closures: vec![], type_objs: Default::default(), record_objs: Default::default(), + variant_objs: Default::default(), true_con_idx: *sum_con_nums .get("Bool") .unwrap() @@ -1428,6 +1436,7 @@ pub fn lower(mono_pgm: &mut mono::MonoPgm) -> LoweredPgm { } lowered_pgm.record_objs = indices.records; + lowered_pgm.variant_objs = variant_indices; lowered_pgm } diff --git a/src/to_c.rs b/src/to_c.rs index 55656d15..4d7ff85b 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -65,10 +65,6 @@ pub(crate) fn to_c(pgm: &LoweredPgm, main: &str) -> String { #include #include - typedef struct {{ - uint64_t tag; - }} Variant; - " ); @@ -102,7 +98,12 @@ pub(crate) fn to_c(pgm: &LoweredPgm, main: &str) -> String { } } - let heap_objs_sorted = top_sort(&pgm.type_objs, &pgm.record_objs, &pgm.heap_objs); + let heap_objs_sorted = top_sort( + &pgm.type_objs, + &pgm.record_objs, + &pgm.variant_objs, + &pgm.heap_objs, + ); for scc in &heap_objs_sorted { // If SCC has more than one element, forward-declare the structs. if scc.len() > 1 { @@ -676,23 +677,42 @@ fn source_con_decl_to_c(source_con: &SourceConDecl, tag: u32, p: &mut Printer) { fn record_decl_to_c(record: &RecordType, tag: u32, p: &mut Printer) { let struct_name = record_struct_name(record); - wln!(p, "#define TAG_{} {}", struct_name, tag); + wln!(p, "#define TAG_{struct_name} {tag}"); - w!(p, "typedef struct {} {{", struct_name); + w!(p, "typedef struct {struct_name} {{"); p.indent(); p.nl(); w!(p, "uint64_t _tag;"); for (i, (_field_name, field_ty)) in record.fields.iter().enumerate() { p.nl(); - w!(p, "{} _{};", c_ty(field_ty), i); + w!(p, "{} _{i};", c_ty(field_ty)); } p.dedent(); p.nl(); - wln!(p, "}} {};", struct_name); + wln!(p, "}} {struct_name};"); } fn variant_decl_to_c(variant: &VariantType, tag: u32, p: &mut Printer) { let struct_name = variant_struct_name(variant); + wln!(p, "// tag = {tag}"); + w!(p, "typedef struct {} {{", struct_name); + p.indent(); + p.nl(); + wln!(p, "uint64_t _tag;"); + w!(p, "union {{"); + p.indent(); + for (i, alt) in variant.alts.values().enumerate() { + p.nl(); + let mut ty = String::new(); + named_ty_to_c(alt, &mut ty); + w!(p, "{ty} _{i};"); + } + p.dedent(); + p.nl(); + w!(p, "}} _alt;"); + p.dedent(); + p.nl(); + wln!(p, "}} {struct_name};"); } fn named_ty_to_c(named_ty: &mono::NamedType, out: &mut String) { @@ -728,9 +748,6 @@ fn c_ty(ty: &mono::Type) -> String { if let mono::Type::Fn(_) = ty { return "CLOSURE*".to_string(); } - if let mono::Type::Variant { .. } = ty { - return "Variant*".to_string(); - } let mut s = String::new(); ty_to_c(ty, &mut s); s.push('*'); // make pointer @@ -2142,6 +2159,7 @@ impl Write for Printer { fn top_sort( type_objs: &HashMap, TypeObjs>>, record_objs: &HashMap, + variant_objs: &HashMap, heap_objs: &[HeapObj], ) -> Vec> { let mut idx_gen = SccIdxGen::default(); @@ -2179,6 +2197,7 @@ fn top_sort( _scc( type_objs, record_objs, + variant_objs, heap_objs, HeapObjIdx(heap_obj_idx as u32), &mut idx_gen, @@ -2218,6 +2237,7 @@ impl SccIdxGen { fn _scc( type_objs: &HashMap, TypeObjs>>, record_objs: &HashMap, + variant_objs: &HashMap, heap_objs: &[HeapObj], heap_obj_idx: HeapObjIdx, idx_gen: &mut SccIdxGen, @@ -2234,13 +2254,20 @@ fn _scc( stack.push(heap_obj_idx); // Add dependencies to the output. - let deps = heap_obj_deps(type_objs, record_objs, heap_objs, heap_obj_idx); + let deps = heap_obj_deps( + type_objs, + record_objs, + variant_objs, + heap_objs, + heap_obj_idx, + ); for dep_obj in deps { if nodes[dep_obj.as_usize()].idx.is_none() { // Dependency not visited yet. _scc( type_objs, record_objs, + variant_objs, heap_objs, dep_obj, idx_gen, @@ -2278,6 +2305,7 @@ fn _scc( fn heap_obj_deps( type_objs: &HashMap, TypeObjs>>, record_objs: &HashMap, + variant_objs: &HashMap, heap_objs: &[HeapObj], heap_obj_idx: HeapObjIdx, ) -> HashSet { @@ -2285,20 +2313,20 @@ fn heap_obj_deps( match &heap_objs[heap_obj_idx.as_usize()] { HeapObj::Builtin(BuiltinConDecl::Array { t }) => { - type_heap_obj_deps(type_objs, record_objs, t, &mut deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, t, &mut deps); } HeapObj::Builtin(_) => {} HeapObj::Source(source_decl) => { for field in source_decl.fields.iter() { - type_heap_obj_deps(type_objs, record_objs, field, &mut deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, field, &mut deps); } } HeapObj::Record(record_type) => { for field in record_type.fields.values() { - type_heap_obj_deps(type_objs, record_objs, field, &mut deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, field, &mut deps); } } @@ -2315,6 +2343,7 @@ fn heap_obj_deps( fn type_heap_obj_deps( type_objs: &HashMap, TypeObjs>>, record_objs: &HashMap, + variant_objs: &HashMap, ty: &mono::Type, deps: &mut HashSet, ) { @@ -2324,18 +2353,22 @@ fn type_heap_obj_deps( } mono::Type::Record { fields } => { - let record_idx = record_objs + let record_idx = *record_objs .get(&RecordType { fields: fields.clone(), }) .unwrap(); - deps.insert(*record_idx); + deps.insert(record_idx); for ty in fields.values() { - type_heap_obj_deps(type_objs, record_objs, ty, deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, ty, deps); } } mono::Type::Variant { alts } => { + let variant_idx = *variant_objs + .get(&VariantType { alts: alts.clone() }) + .unwrap(); + deps.insert(variant_idx); for ty in alts.values() { named_type_heap_obj_deps(type_objs, ty, deps); } @@ -2345,18 +2378,18 @@ fn type_heap_obj_deps( match args { mono::FunArgs::Positional(args) => { for arg in args { - type_heap_obj_deps(type_objs, record_objs, arg, deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, arg, deps); } } mono::FunArgs::Named(args) => { for arg in args.values() { - type_heap_obj_deps(type_objs, record_objs, arg, deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, arg, deps); } } } - type_heap_obj_deps(type_objs, record_objs, ret, deps); - type_heap_obj_deps(type_objs, record_objs, exn, deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, ret, deps); + type_heap_obj_deps(type_objs, record_objs, variant_objs, exn, deps); } } } From 20ad2c65c090669b749a913bf8c0f0f2d8dda894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Sun, 1 Feb 2026 14:29:21 +0000 Subject: [PATCH 05/19] Code --- src/to_c.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/to_c.rs b/src/to_c.rs index 4d7ff85b..36164d62 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -659,19 +659,19 @@ fn source_con_decl_to_c(source_con: &SourceConDecl, tag: u32, p: &mut Printer) { let tag_name = source_con_tag_name(source_con); let struct_name = source_con_struct_name(source_con); - wln!(p, "#define {} {}", tag_name, tag); + wln!(p, "#define {tag_name} {tag}"); - w!(p, "typedef struct {} {{", struct_name); + w!(p, "typedef struct {struct_name} {{"); p.indent(); p.nl(); w!(p, "uint64_t _tag;"); for (i, ty) in fields.iter().enumerate() { p.nl(); - w!(p, "{} _{};", c_ty(ty), i); + w!(p, "{} _{i};", c_ty(ty)); } p.dedent(); p.nl(); - wln!(p, "}} {};", struct_name); + wln!(p, "}} {struct_name};"); } fn record_decl_to_c(record: &RecordType, tag: u32, p: &mut Printer) { From cbe542fe3df9e9897e4fd1cc8ef3d81c029651e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Sun, 1 Feb 2026 15:32:02 +0000 Subject: [PATCH 06/19] Annotate lowered variant exprs with types --- src/interpreter.rs | 2 +- src/lowering.rs | 15 ++++++++++++--- src/lowering/printer.rs | 2 +- src/to_c.rs | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 714a1b17..2d39ef8f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -665,7 +665,7 @@ fn eval( Expr::Do(stmts, _) => exec(w, pgm, heap, locals, stmts, call_stack), - Expr::Variant(expr) => { + Expr::Variant { expr, ty: _ } => { // Note: the interpreter can only deal with variants of boxed types. If `expr` is an // unboxed type things will go wrong. // diff --git a/src/lowering.rs b/src/lowering.rs index 6441a843..5f00d3b8 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -437,7 +437,10 @@ pub enum Expr { mono::Type, ), - Variant(Box>), + Variant { + expr: Box>, + ty: OrdMap, + }, } #[derive(Debug, Clone)] @@ -2111,11 +2114,17 @@ fn lower_expr( (expr, Default::default()) } - mono::Expr::Variant(mono::VariantExpr { expr, ty: _ }) => { + mono::Expr::Variant(mono::VariantExpr { expr, ty }) => { // Note: Type of the expr in the variant won't be a variant type. Use the // `VariantExpr`'s type. let (expr, vars) = lower_bl_expr(expr, closures, indices, scope, mono_pgm); - (Expr::Variant(expr), vars) + ( + Expr::Variant { + expr, + ty: ty.clone(), + }, + vars, + ) } } } diff --git a/src/lowering/printer.rs b/src/lowering/printer.rs index a65b6bb5..994b6d30 100644 --- a/src/lowering/printer.rs +++ b/src/lowering/printer.rs @@ -387,7 +387,7 @@ impl Expr { } } - Expr::Variant(expr) => { + Expr::Variant { expr, ty: _ } => { buf.push('~'); expr.node.print(buf, indent); } diff --git a/src/to_c.rs b/src/to_c.rs index dcd4cb7e..60a3b2c7 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -2012,7 +2012,7 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut w!(p, "}})"); } - Expr::Variant(expr) => { + Expr::Variant { expr, ty: _ } => { // Variants are represented as their underlying type w!(p, "((Variant*)"); expr_to_c(&expr.node, &expr.loc, locals, cg, p); From 106164bbe7ffd674af87056133446e37d8d5ab74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Mon, 2 Feb 2026 19:09:04 +0000 Subject: [PATCH 07/19] code ahoy --- src/interpreter.rs | 6 +++++- src/lowering.rs | 7 +++++-- src/lowering/printer.rs | 6 +++++- src/to_c.rs | 25 +++++++++++++++++++++++-- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2d39ef8f..05809a85 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -665,7 +665,11 @@ fn eval( Expr::Do(stmts, _) => exec(w, pgm, heap, locals, stmts, call_stack), - Expr::Variant { expr, ty: _ } => { + Expr::Variant { + expr, + expr_ty: _, + variant_ty: _, + } => { // Note: the interpreter can only deal with variants of boxed types. If `expr` is an // unboxed type things will go wrong. // diff --git a/src/lowering.rs b/src/lowering.rs index 5f00d3b8..3b03e586 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -439,7 +439,8 @@ pub enum Expr { Variant { expr: Box>, - ty: OrdMap, + expr_ty: mono::Type, + variant_ty: OrdMap, }, } @@ -2117,11 +2118,13 @@ fn lower_expr( mono::Expr::Variant(mono::VariantExpr { expr, ty }) => { // Note: Type of the expr in the variant won't be a variant type. Use the // `VariantExpr`'s type. + let expr_ty = expr.node.ty(); let (expr, vars) = lower_bl_expr(expr, closures, indices, scope, mono_pgm); ( Expr::Variant { expr, - ty: ty.clone(), + expr_ty, + variant_ty: ty.clone(), }, vars, ) diff --git a/src/lowering/printer.rs b/src/lowering/printer.rs index 994b6d30..0e4f8fc5 100644 --- a/src/lowering/printer.rs +++ b/src/lowering/printer.rs @@ -387,7 +387,11 @@ impl Expr { } } - Expr::Variant { expr, ty: _ } => { + Expr::Variant { + expr, + expr_ty: _, + variant_ty: _, + } => { buf.push('~'); expr.node.print(buf, indent); } diff --git a/src/to_c.rs b/src/to_c.rs index 68875e12..e80cbafc 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -2014,8 +2014,29 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut w!(p, "}})"); } - Expr::Variant { expr, ty: _ } => { - // Variants are represented as their underlying type + Expr::Variant { + expr, + expr_ty, + variant_ty, + } => { + /* + ~ + + ==> + + ({ temp1 = ; + uint64_t temp2 = get_tag(temp1); + temp3 = { .tag = temp2, .alt._N = temp1 }; + temp3; }) + + where: + + - `get_tag` is type-specific tag getter + - `N` is the index of the named type in the variant type + */ + let variant_struct_name = variant_struct_name(&VariantType { + alts: variant_ty.clone(), + }); w!(p, "((Variant*)"); expr_to_c(&expr.node, &expr.loc, locals, cg, p); w!(p, ")"); From 6ae808356d282b8e20c367c90b1406c222c78270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Mon, 2 Feb 2026 21:01:50 +0000 Subject: [PATCH 08/19] doit no mistakes --- src/to_c.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/to_c.rs b/src/to_c.rs index abb743c6..7a945272 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -2055,6 +2055,7 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { Pat::Con(ConPat { con, fields }) => { let struct_name = heap_obj_struct_name(cg.pgm, *con); let tag_name = heap_obj_tag_name(cg.pgm, *con); + // let get_tag_expr = gen_get_tag(&cg.pgm, scrutinee, ty); TODO let mut cond = format!("(get_tag({}) == {})", scrutinee, tag_name); for (i, field_pat) in fields.iter().enumerate() { let field_expr = format!("(({struct_name}*){scrutinee})->_{i}"); @@ -2128,6 +2129,42 @@ fn generate_main_fn(pgm: &LoweredPgm, main: &str, p: &mut Printer) { wln!(p, "}}"); } +fn gen_get_tag(pgm: &LoweredPgm, expr: &str, ty: &mono::Type) -> String { + // For product types, use the tag macro. + match ty { + mono::Type::Named(mono::NamedType { name, args }) => { + match pgm.type_objs.get(name).unwrap().get(args).unwrap() { + TypeObjs::Product(heap_obj_idx) => heap_obj_tag_name(pgm, *heap_obj_idx), + TypeObjs::Sum(_) => { + // TODO: handle unboxed values + format!("(get_tag({expr}))") + } + } + } + + mono::Type::Record { fields } => { + let heap_obj_idx = *pgm + .record_objs + .get(&RecordType { + fields: fields.clone(), + }) + .unwrap(); + heap_obj_tag_name(pgm, heap_obj_idx) + } + + mono::Type::Variant { alts: _ } => { + // TODO: Currently variants only hold boxed objects, and represented as the boxed object + // directly. + format!("(get_tag({expr}))") + } + + mono::Type::Fn(_) => "CLOSURE_TAG".to_string(), + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Printing utils + #[derive(Debug, Default)] struct Printer { lines: Vec, From b9c94ad3865440dcb1cdc3e8c29553f84782824b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Tue, 3 Feb 2026 12:31:28 +0000 Subject: [PATCH 09/19] brb writing code --- src/lowering.rs | 16 ++++++++++++++-- src/mono_ast.rs | 1 + src/monomorph.rs | 2 ++ src/to_c.rs | 34 +++++++++++++++++++++++++++------- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/lowering.rs b/src/lowering.rs index 3b03e586..15f8aa91 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -57,7 +57,10 @@ pub struct LoweredPgm { #[derive(Debug)] pub enum TypeObjs { Product(HeapObjIdx), - Sum(Vec), + Sum { + con_indices: Vec, + value: bool, + }, } pub const CON_CON_IDX: HeapObjIdx = HeapObjIdx(0); @@ -781,6 +784,9 @@ pub fn lower(mono_pgm: &mut mono::MonoPgm) -> LoweredPgm { match &con_decl.rhs { Some(rhs) => match rhs { mono::TypeDeclRhs::Sum(cons) => { + // For sum types, we generate an index representing the type itself (rather + // than its consturctors). This index is used in dependency anlaysis, and to + // get the type details during code generation. let mut con_indices: Vec = Vec::with_capacity(cons.len()); for mono::ConDecl { name, fields } in cons { let idx = HeapObjIdx(lowered_pgm.heap_objs.len() as u32); @@ -797,7 +803,13 @@ pub fn lower(mono_pgm: &mut mono::MonoPgm) -> LoweredPgm { .type_objs .entry(con_id.clone()) .or_default() - .insert(con_ty_args.clone(), TypeObjs::Sum(con_indices)); + .insert( + con_ty_args.clone(), + TypeObjs::Sum { + con_indices, + value: con_decl.value, + }, + ); assert!(old.is_none()); } diff --git a/src/mono_ast.rs b/src/mono_ast.rs index bb798798..5176f649 100644 --- a/src/mono_ast.rs +++ b/src/mono_ast.rs @@ -23,6 +23,7 @@ pub struct MonoPgm { pub struct TypeDecl { pub name: Id, pub rhs: Option, + pub value: bool, } #[derive(Debug, Clone)] diff --git a/src/monomorph.rs b/src/monomorph.rs index eba1ecc9..3ededd96 100644 --- a/src/monomorph.rs +++ b/src/monomorph.rs @@ -1877,6 +1877,7 @@ fn mono_ty_decl( mono::TypeDecl { name: mono_ty_id.clone(), rhs: None, + value: ty_decl.value, }, ); @@ -1905,6 +1906,7 @@ fn mono_ty_decl( mono::TypeDecl { name: mono_ty_id.clone(), rhs, + value: ty_decl.value, }, ); diff --git a/src/to_c.rs b/src/to_c.rs index 7a945272..002f794e 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -2129,15 +2129,34 @@ fn generate_main_fn(pgm: &LoweredPgm, main: &str, p: &mut Printer) { wln!(p, "}}"); } +/// Generate the C expression to get the tag of the expression `expr`, which should have the type `ty`. +/// +/// For product types: the tag will be the macro that defines the tag. +/// +/// For sum types: the tag will be extracted from the expression and the code will depend on whether +/// the sum type is a value type or not. +/// +/// - For boxed sum types: the generated code will read the tag word of the heap allocated object. +/// - For unboxed sum types: the generated code will read the tag from the struct of the sum type. fn gen_get_tag(pgm: &LoweredPgm, expr: &str, ty: &mono::Type) -> String { // For product types, use the tag macro. match ty { mono::Type::Named(mono::NamedType { name, args }) => { match pgm.type_objs.get(name).unwrap().get(args).unwrap() { TypeObjs::Product(heap_obj_idx) => heap_obj_tag_name(pgm, *heap_obj_idx), - TypeObjs::Sum(_) => { - // TODO: handle unboxed values - format!("(get_tag({expr}))") + + TypeObjs::Sum { + con_indices: _, + value: true, + } => { + format!("((uint32_t)({expr})._tag)") + } + + TypeObjs::Sum { + con_indices: _, + value: false, + } => { + format!("((uint32_t)*(uint64_t*)({expr}))") } } } @@ -2153,9 +2172,7 @@ fn gen_get_tag(pgm: &LoweredPgm, expr: &str, ty: &mono::Type) -> String { } mono::Type::Variant { alts: _ } => { - // TODO: Currently variants only hold boxed objects, and represented as the boxed object - // directly. - format!("(get_tag({expr}))") + format!("((uint32_t)({expr})._tag)") } mono::Type::Fn(_) => "CLOSURE_TAG".to_string(), @@ -2465,6 +2482,9 @@ fn named_type_heap_obj_deps( TypeObjs::Product(idx) => { deps.insert(*idx); } - TypeObjs::Sum(idxs) => deps.extend(idxs.iter().cloned()), + TypeObjs::Sum { + con_indices, + value: _, + } => deps.extend(con_indices.iter().cloned()), } } From f2e56823a35fe29a619f014e1c258d6ef6fb44d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Tue, 3 Feb 2026 12:54:42 +0000 Subject: [PATCH 10/19] Add things --- src/interpreter.rs | 4 ++-- src/lowering.rs | 16 ++++++++-------- src/lowering/printer.rs | 4 ++-- src/to_c.rs | 11 ++++++++++- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 05809a85..13822170 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -780,10 +780,10 @@ fn try_bind_pat(pgm: &Pgm, heap: &mut Heap, pat: &L, locals: &mut [u64], va true } - Pat::Variant(p) => { + Pat::Variant { pat, variant_ty: _ } => { // `p` needs to match a boxed type, but we can't check this here (e.g. in an `assert`). // See the documentation in `Expr::Variant` evaluator. - try_bind_pat(pgm, heap, p, locals, value) + try_bind_pat(pgm, heap, pat, locals, value) } } } diff --git a/src/lowering.rs b/src/lowering.rs index 15f8aa91..ebfa07c1 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -507,7 +507,10 @@ pub enum Pat { Str(String), Char(char), Or(Box>, Box>), - Variant(Box>), + Variant { + pat: Box>, + variant_ty: OrdMap, + }, } #[derive(Debug, Clone)] @@ -2352,13 +2355,10 @@ fn lower_pat( lower_bl_pat(p2, indices, scope, mono_pgm, mapped_binders), ), - mono::Pat::Variant(mono::VariantPat { pat, ty: _ }) => Pat::Variant(Box::new(lower_l_pat( - pat, - indices, - scope, - mono_pgm, - mapped_binders, - ))), + mono::Pat::Variant(mono::VariantPat { pat, ty }) => Pat::Variant { + pat: Box::new(lower_l_pat(pat, indices, scope, mono_pgm, mapped_binders)), + variant_ty: ty.clone(), + }, } } diff --git a/src/lowering/printer.rs b/src/lowering/printer.rs index 0e4f8fc5..d8676ce4 100644 --- a/src/lowering/printer.rs +++ b/src/lowering/printer.rs @@ -437,9 +437,9 @@ impl Pat { p2.node.print(buf); } - Pat::Variant(p) => { + Pat::Variant { pat, variant_ty: _ } => { buf.push('~'); - p.node.print(buf); + pat.node.print(buf); } } } diff --git a/src/to_c.rs b/src/to_c.rs index 002f794e..2deae1c0 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -2101,7 +2101,16 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { format!("({} || {})", c1, c2) } - Pat::Variant(inner) => pat_to_cond(&inner.node, scrutinee, cg), + Pat::Variant { pat, variant_ty } => { + /* + ~ + + ==> + + ??? TODO + */ + pat_to_cond(&pat.node, scrutinee, cg) + } } } From e8fe57b2a3bde81247e843a156016698dd85b947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 18:46:59 +0000 Subject: [PATCH 11/19] Annotate var binders with refinements --- src/ast.rs | 4 ++ src/ast/printer.rs | 8 +-- src/monomorph.rs | 2 +- src/parser.lalrpop | 2 +- src/parser.rs | 3 +- src/type_checker/expr.rs | 81 +++++++++++++++++++++++++++---- src/type_checker/normalization.rs | 9 +++- src/type_checker/pat.rs | 6 ++- src/type_checker/pat_coverage.rs | 12 ++++- src/type_checker/stmt.rs | 1 + 10 files changed, 109 insertions(+), 19 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 2d093070..bd9c0179 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -396,6 +396,10 @@ pub struct VarPat { /// Inferred type of the binder. Filled in by the type checker. pub ty: Option, + + /// Only after type checking: when the binder type is refined by pattern matching, this holds + /// the refined type. + pub refined: Option, } #[derive(Debug, Clone)] diff --git a/src/ast/printer.rs b/src/ast/printer.rs index e63f0cd2..8f13d9c1 100644 --- a/src/ast/printer.rs +++ b/src/ast/printer.rs @@ -814,11 +814,13 @@ impl Expr { impl Pat { pub fn print(&self, buf: &mut String) { match self { - Pat::Var(VarPat { var, ty }) => { + Pat::Var(VarPat { var, ty, refined }) => { buf.push_str(var); if let Some(ty) = ty { - buf.push_str(": "); - write!(buf, "{ty}").unwrap(); + write!(buf, ": {ty}").unwrap(); + } + if let Some(refined) = refined { + write!(buf, " ~> {refined}").unwrap(); } } diff --git a/src/monomorph.rs b/src/monomorph.rs index 3ededd96..c2f93415 100644 --- a/src/monomorph.rs +++ b/src/monomorph.rs @@ -1263,7 +1263,7 @@ fn mono_pat( loc: &ast::Loc, ) -> mono::Pat { match pat { - ast::Pat::Var(ast::VarPat { var, ty }) => { + ast::Pat::Var(ast::VarPat { var, ty, refined }) => { let mono_ty = mono_tc_ty(ty.as_ref().unwrap(), ty_map, poly_pgm, mono_pgm); locals.insert(var.clone()); mono::Pat::Var(mono::VarPat { diff --git a/src/parser.lalrpop b/src/parser.lalrpop index dda25d03..59b411ce 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -951,7 +951,7 @@ LPat: L = { Pat: Pat = { #[precedence(level = "0")] - => Pat::Var(VarPat { var: id.smol_str(), ty: None }), + => Pat::Var(VarPat { var: id.smol_str(), ty: None, refined: None }), => Pat::Con(con), diff --git a/src/parser.rs b/src/parser.rs index 2c4c032d..4fb004c7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,5 +1,5 @@ // auto-generated: "lalrpop 0.22.2" -// sha3: f7a8829affc1d86e00b24542123a0e5870ee952878606b525e1e96baeb8430d1 +// sha3: 52383218fe8655612aba1731ca84f6a2562106485c38d26a48f5cecea992b73e #![allow(clippy::all)] use crate::ast::*; use crate::interpolation::{copy_update_escapes, str_parts}; @@ -48799,6 +48799,7 @@ fn __action147<'a>(module: &'a Rc, (_, id, _): (Loc, Token, Loc)) -> Pat { Pat::Var(VarPat { var: id.smol_str(), ty: None, + refined: None, }) } diff --git a/src/type_checker/expr.rs b/src/type_checker/expr.rs index c9bd0298..b6e07a80 100644 --- a/src/type_checker/expr.rs +++ b/src/type_checker/expr.rs @@ -858,6 +858,7 @@ pub(super) fn check_expr( node: ast::Pat::Var(ast::VarPat { var: buf_id.clone(), ty: Some(str_buf_ty.clone()), + refined: None, }), }, ty: None, @@ -1777,7 +1778,22 @@ pub(super) fn check_match_expr( for (alt_idx, (ast::Alt { pat, guard, rhs }, mut alt_scope)) in alts.iter_mut().zip(alt_envs.into_iter()).enumerate() { - refine_binders(&mut alt_scope, &info.bound_vars[alt_idx], &pat.loc); + let refined_binders = refine_binders(&info.bound_vars[alt_idx], &pat.loc); + + if cfg!(debug_assertions) { + let scope_vars: HashSet<&Id> = alt_scope.keys().collect(); + let binders_vars: HashSet<&Id> = refined_binders.keys().collect(); + assert_eq!(scope_vars, binders_vars); + } + + add_coercions( + &mut pat.node, + &refined_binders, + tc_state.tys.tys.cons(), + &pat.loc, + ); + + alt_scope.extend(refined_binders); tc_state.env.push_scope(alt_scope); @@ -2149,12 +2165,8 @@ pub(crate) fn make_variant(tc_state: &mut TcFunState, ty: Ty, level: u32, loc: & } } -fn refine_binders(scope: &mut HashMap, binders: &HashMap>, loc: &ast::Loc) { - if cfg!(debug_assertions) { - let scope_vars: HashSet<&Id> = scope.keys().collect(); - let binders_vars: HashSet<&Id> = binders.keys().collect(); - assert_eq!(scope_vars, binders_vars); - } +fn refine_binders(binders: &HashMap>, loc: &ast::Loc) -> HashMap { + let mut refined_binders: HashMap = Default::default(); for (var, tys) in binders.iter() { // println!("{} --> {:?}", var, tys); @@ -2162,7 +2174,8 @@ fn refine_binders(scope: &mut HashMap, binders: &HashMap if tys.len() == 1 { // println!("{} --> {}", var, tys.iter().next().unwrap().clone()); - scope.insert(var.clone(), tys.iter().next().unwrap().clone()); + let old = refined_binders.insert(var.clone(), tys.iter().next().unwrap().clone()); + assert_eq!(old, None); } else { let mut labels: OrdMap = Default::default(); let mut extension: Option> = None; @@ -2208,7 +2221,57 @@ fn refine_binders(scope: &mut HashMap, binders: &HashMap // println!("{} --> {}", var, new_ty); - scope.insert(var.clone(), new_ty); + let old = refined_binders.insert(var.clone(), new_ty); + assert_eq!(old, None); + } + } + + refined_binders +} + +fn add_coercions( + pat: &mut ast::Pat, + refined_binders: &HashMap, + cons: &ScopeMap, + _loc: &ast::Loc, +) { + match pat { + ast::Pat::Var(ast::VarPat { var, ty, refined }) => { + assert_eq!(refined, &mut None); + let refined_ty = refined_binders.get(var).unwrap().deep_normalize(cons); + let ty = ty.as_ref().unwrap().deep_normalize(cons); + if refined_ty != ty { + *refined = Some(refined_ty); + } + } + + ast::Pat::Con(ast::ConPat { + con: _, + fields, + ignore_rest: _, + }) + | ast::Pat::Record(ast::RecordPat { + fields, + ignore_rest: _, + inferred_ty: _, + }) => { + for field in fields { + add_coercions(&mut field.node.node, refined_binders, cons, &field.node.loc); + } + } + + ast::Pat::Ignore | ast::Pat::Str(_) | ast::Pat::Char(_) => {} + + ast::Pat::Or(p1, p2) => { + add_coercions(&mut p1.node, refined_binders, cons, &p1.loc); + add_coercions(&mut p2.node, refined_binders, cons, &p2.loc); + } + + ast::Pat::Variant(ast::VariantPat { + pat, + inferred_ty: _, + }) => { + add_coercions(&mut pat.node, refined_binders, cons, &pat.loc); } } } diff --git a/src/type_checker/normalization.rs b/src/type_checker/normalization.rs index 4c13028e..a00d9838 100644 --- a/src/type_checker/normalization.rs +++ b/src/type_checker/normalization.rs @@ -228,8 +228,15 @@ fn normalize_expr(expr: &mut ast::Expr, loc: &ast::Loc, cons: &ScopeMap) { match pat { - ast::Pat::Var(ast::VarPat { var: _, ty }) => { + ast::Pat::Var(ast::VarPat { + var: _, + ty, + refined, + }) => { *ty = Some(ty.as_ref().unwrap().deep_normalize(cons)); + if let Some(ty) = refined { + *ty = ty.deep_normalize(cons); + } } ast::Pat::Ignore | ast::Pat::Str(_) | ast::Pat::Char(_) => {} diff --git a/src/type_checker/pat.rs b/src/type_checker/pat.rs index bcd2c930..3d653ba2 100644 --- a/src/type_checker/pat.rs +++ b/src/type_checker/pat.rs @@ -10,7 +10,11 @@ use crate::type_checker::{TcFunState, loc_display}; /// `pat` is `mut` to be able to add types of variables and type arguments of constructors. pub(super) fn check_pat(tc_state: &mut TcFunState, pat: &mut ast::L, level: u32) -> Ty { match &mut pat.node { - ast::Pat::Var(ast::VarPat { var, ty }) => { + ast::Pat::Var(ast::VarPat { + var, + ty, + refined: _, + }) => { assert!(ty.is_none()); let fresh_ty = Ty::UVar(tc_state.var_gen.new_var(level, Kind::Star, pat.loc.clone())); *ty = Some(fresh_ty.clone()); diff --git a/src/type_checker/pat_coverage.rs b/src/type_checker/pat_coverage.rs index 9dcd72bd..9d241294 100644 --- a/src/type_checker/pat_coverage.rs +++ b/src/type_checker/pat_coverage.rs @@ -42,8 +42,12 @@ struct PatMatrix { struct Row { /// `match` arm index the row is generated from. arm_index: ArmIndex, + pats: Vec>, + + /// Maps variables in the row to types they're bound. bound_vars: HashMap>, + guarded: bool, } @@ -52,7 +56,7 @@ pub(crate) struct CoverageInfo { /// Maps arm indices to variables bound in the arms. pub(crate) bound_vars: Vec>>, - /// Maps arm indices to whether its useful. + /// Maps arm indices to whether they're useful. pub(crate) usefulness: Vec, } @@ -502,7 +506,11 @@ impl PatMatrix { // pat should be variable with the same name as the // field. (type checker checks this) match &named_field.node.node { - ast::Pat::Var(ast::VarPat { var, ty: _ }) => var, + ast::Pat::Var(ast::VarPat { + var, + ty: _, + refined: _, + }) => var, _ => panic!(), } } diff --git a/src/type_checker/stmt.rs b/src/type_checker/stmt.rs index cc2ecffd..0ecebf54 100644 --- a/src/type_checker/stmt.rs +++ b/src/type_checker/stmt.rs @@ -392,6 +392,7 @@ fn check_stmt( node: ast::Pat::Var(ast::VarPat { var: expr_local.clone(), ty: Some(iter_ty.clone()), + refined: None, }), }, ty: None, From 65498f6c0400169cce750ddc839e580324d6bff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 18:51:37 +0000 Subject: [PATCH 12/19] Pass refined tys to mono --- src/lowering.rs | 2 +- src/mono_ast.rs | 1 + src/mono_ast/printer.rs | 2 +- src/monomorph.rs | 5 +++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lowering.rs b/src/lowering.rs index ebfa07c1..6f4867b3 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -2196,7 +2196,7 @@ fn lower_pat( mapped_binders: &mut HashMap, ) -> Pat { match pat { - mono::Pat::Var(mono::VarPat { var, ty }) => match mapped_binders.get(var) { + mono::Pat::Var(mono::VarPat { var, ty, refined }) => match mapped_binders.get(var) { Some(idx) => Pat::Var(*idx), None => { let var_idx = LocalIdx(scope.locals.len() as u32); diff --git a/src/mono_ast.rs b/src/mono_ast.rs index 5176f649..c5358347 100644 --- a/src/mono_ast.rs +++ b/src/mono_ast.rs @@ -234,6 +234,7 @@ pub enum Pat { pub struct VarPat { pub var: Id, pub ty: Type, + pub refined: Option, } #[derive(Debug, Clone)] diff --git a/src/mono_ast/printer.rs b/src/mono_ast/printer.rs index a4376a35..31c179de 100644 --- a/src/mono_ast/printer.rs +++ b/src/mono_ast/printer.rs @@ -576,7 +576,7 @@ impl Expr { impl Pat { pub fn print(&self, buf: &mut String) { match self { - Pat::Var(VarPat { var, ty }) => { + Pat::Var(VarPat { var, ty, refined }) => { buf.push_str(var); buf.push_str(": "); ty.print(buf); diff --git a/src/monomorph.rs b/src/monomorph.rs index c2f93415..89646e52 100644 --- a/src/monomorph.rs +++ b/src/monomorph.rs @@ -575,6 +575,7 @@ fn mono_expr( node: mono::Pat::Var(mono::VarPat { var: SmolStr::new_static("$receiver$"), ty: mono_object_ty, + refined: None, }), }, rhs: *mono_object, @@ -1265,10 +1266,14 @@ fn mono_pat( match pat { ast::Pat::Var(ast::VarPat { var, ty, refined }) => { let mono_ty = mono_tc_ty(ty.as_ref().unwrap(), ty_map, poly_pgm, mono_pgm); + let refined = refined + .as_ref() + .map(|refined| mono_tc_ty(refined, ty_map, poly_pgm, mono_pgm)); locals.insert(var.clone()); mono::Pat::Var(mono::VarPat { var: var.clone(), ty: mono_ty, + refined, }) } From 9ac5ddfe3ad1ec40df521b239493f06254269e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 19:07:16 +0000 Subject: [PATCH 13/19] Pass coercions to lower AST --- src/interpreter.rs | 7 +++++-- src/lowering.rs | 29 +++++++++++++++++++++++------ src/lowering/printer.rs | 5 ++++- src/to_c.rs | 5 ++++- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 13822170..80452c4e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -734,8 +734,11 @@ fn assign( /// compiled version `StrView`s will be allocated on stack. fn try_bind_pat(pgm: &Pgm, heap: &mut Heap, pat: &L, locals: &mut [u64], value: u64) -> bool { match &pat.node { - Pat::Var(var) => { - locals[var.as_usize()] = value; + Pat::Var(VarPat { + idx, + original_ty: _, + }) => { + locals[idx.as_usize()] = value; true } diff --git a/src/lowering.rs b/src/lowering.rs index 6f4867b3..baeb69bc 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -501,7 +501,7 @@ pub struct IsExpr { #[derive(Debug, Clone)] pub enum Pat { - Var(LocalIdx), + Var(VarPat), Con(ConPat), Ignore, Str(String), @@ -513,6 +513,17 @@ pub enum Pat { }, } +#[derive(Debug, Clone)] +pub struct VarPat { + pub idx: LocalIdx, + + /// When the binder was refined by pattern matching, the local in `SourceFunDecl` will have the + /// refined type, and this will be the original type. + /// + /// When pattern matching, we should convert the original type to the local's type. + pub original_ty: mono::Type, +} + #[derive(Debug, Clone)] pub struct ConPat { pub con: HeapObjIdx, @@ -2191,22 +2202,28 @@ fn lower_pat( // This map is to map binders in alternatives of or patterns to the same local. // - // Only in or pattern alternatives we allow same binders, so if we see a binder for the second - // time, we must be checking another alternative of an or pattern. + // Only in or-pattern alternatives we allow same binders, so if we see a binder for the second + // time, we must be checking another alternative of an or-pattern. mapped_binders: &mut HashMap, ) -> Pat { match pat { mono::Pat::Var(mono::VarPat { var, ty, refined }) => match mapped_binders.get(var) { - Some(idx) => Pat::Var(*idx), + Some(idx) => Pat::Var(VarPat { + idx: *idx, + original_ty: ty.clone(), + }), None => { let var_idx = LocalIdx(scope.locals.len() as u32); scope.locals.push(LocalInfo { name: var.clone(), - ty: ty.clone(), + ty: refined.as_ref().unwrap_or(ty).clone(), }); scope.bounds.insert(var.clone(), var_idx); mapped_binders.insert(var.clone(), var_idx); - Pat::Var(var_idx) + Pat::Var(VarPat { + idx: var_idx, + original_ty: ty.clone(), + }) } }, diff --git a/src/lowering/printer.rs b/src/lowering/printer.rs index d8676ce4..9239177a 100644 --- a/src/lowering/printer.rs +++ b/src/lowering/printer.rs @@ -402,7 +402,10 @@ impl Expr { impl Pat { pub fn print(&self, buf: &mut String) { match self { - Pat::Var(idx) => write!(buf, "local{}", idx.0).unwrap(), + Pat::Var(VarPat { + idx, + original_ty: _, + }) => write!(buf, "local{}", idx.0).unwrap(), Pat::Con(ConPat { con, fields }) => { write!(buf, "con{}(", con.0).unwrap(); diff --git a/src/to_c.rs b/src/to_c.rs index 2deae1c0..69dcd195 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -2048,7 +2048,10 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { match pat { Pat::Ignore => "1".to_string(), - Pat::Var(idx) => { + Pat::Var(VarPat { + idx, + original_ty: _, + }) => { format!("({{ _{} = {}; 1; }})", idx.as_usize(), scrutinee) } From 59cac34deb0bb83f2a33aca1b02c1d02ed7320a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 19:57:20 +0000 Subject: [PATCH 14/19] brb crafting code --- src/to_c.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/src/to_c.rs b/src/to_c.rs index 69dcd195..13251bc5 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -696,9 +696,7 @@ fn variant_decl_to_c(variant: &VariantType, tag: u32, p: &mut Printer) { p.indent(); for (i, alt) in variant.alts.values().enumerate() { p.nl(); - let mut ty = String::new(); - named_ty_to_c(alt, &mut ty); - w!(p, "{ty} _{i};"); + w!(p, "{} _{i};", c_ty(&mono::Type::Named(alt.clone()))); } p.dedent(); p.nl(); @@ -743,7 +741,9 @@ fn c_ty(ty: &mono::Type) -> String { } let mut s = String::new(); ty_to_c(ty, &mut s); - s.push('*'); // make pointer + if !matches!(ty, mono::Type::Variant { .. }) { + s.push('*'); // make pointer + } s } @@ -2033,12 +2033,55 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut - `get_tag` is type-specific tag getter - `N` is the index of the named type in the variant type */ + + w!(p, "({{"); + p.indent(); + p.nl(); + + // TODO: Check that variant exprs are named types in an earlier pass. + let expr_named_ty = match expr_ty { + mono::Type::Named(named_ty) => named_ty, + _ => panic!(), + }; + + let alt_idx = variant_ty + .iter() + .enumerate() + .find_map(|(idx, (_, alt_ty))| { + if alt_ty == expr_named_ty { + Some(idx) + } else { + None + } + }) + .unwrap(); + let variant_struct_name = variant_struct_name(&VariantType { alts: variant_ty.clone(), }); - w!(p, "((Variant*)"); + + let expr_temp = cg.fresh_temp(); + w!(p, "{} {expr_temp} = ", c_ty(expr_ty)); expr_to_c(&expr.node, &expr.loc, locals, cg, p); - w!(p, ")"); + wln!(p, "; // {}", loc_display(&expr.loc)); + + let expr_tag_temp = cg.fresh_temp(); + wln!( + p, + "uint32_t {expr_tag_temp} = {};", + gen_get_tag(cg.pgm, &expr_temp, expr_ty) + ); + + let variant_temp = cg.fresh_temp(); + wln!( + p, + "{variant_struct_name} {variant_temp} = {{ ._tag = {expr_tag_temp}, ._alt._{alt_idx} = {expr_temp} }};" + ); + w!(p, "{variant_temp};"); + + p.dedent(); + p.nl(); + w!(p, "}})"); } } } From c051717015c1a015d2e77d051d52b2ef16da91c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 20:41:55 +0000 Subject: [PATCH 15/19] write gud code no mistakes --- src/ast.rs | 1 + src/ast/printer.rs | 6 ++++- src/interpreter.rs | 6 ++++- src/lowering.rs | 10 ++++++-- src/lowering/printer.rs | 6 ++++- src/mono_ast.rs | 3 ++- src/mono_ast/printer.rs | 6 ++++- src/monomorph.rs | 26 +++++++++++++------- src/parser.lalrpop | 2 +- src/parser.rs | 3 ++- src/to_c.rs | 41 ++++++++++++++++++++++++------- src/type_checker/expr.rs | 1 + src/type_checker/normalization.rs | 7 +++++- src/type_checker/pat.rs | 11 ++++++++- src/type_checker/pat_coverage.rs | 1 + src/type_collector.rs | 14 ++++++++--- 16 files changed, 112 insertions(+), 32 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index bd9c0179..f8d7ca42 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -423,6 +423,7 @@ pub struct RecordPat { pub struct VariantPat { pub pat: Box>, pub inferred_ty: Option, + pub inferred_pat_ty: Option, } #[derive(Debug, Clone)] diff --git a/src/ast/printer.rs b/src/ast/printer.rs index f67915bd..d65fc204 100644 --- a/src/ast/printer.rs +++ b/src/ast/printer.rs @@ -904,7 +904,11 @@ impl Pat { buf.push(')'); } - Pat::Variant(VariantPat { pat, inferred_ty }) => { + Pat::Variant(VariantPat { + pat, + inferred_ty, + inferred_pat_ty: _, + }) => { buf.push('~'); pat.node.print(buf); if let Some(ty) = inferred_ty { diff --git a/src/interpreter.rs b/src/interpreter.rs index 80452c4e..a8c5bb87 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -783,7 +783,11 @@ fn try_bind_pat(pgm: &Pgm, heap: &mut Heap, pat: &L, locals: &mut [u64], va true } - Pat::Variant { pat, variant_ty: _ } => { + Pat::Variant { + pat, + variant_ty: _, + pat_ty: _, + } => { // `p` needs to match a boxed type, but we can't check this here (e.g. in an `assert`). // See the documentation in `Expr::Variant` evaluator. try_bind_pat(pgm, heap, pat, locals, value) diff --git a/src/lowering.rs b/src/lowering.rs index baeb69bc..2be01f60 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -510,6 +510,7 @@ pub enum Pat { Variant { pat: Box>, variant_ty: OrdMap, + pat_ty: mono::Type, }, } @@ -2372,9 +2373,14 @@ fn lower_pat( lower_bl_pat(p2, indices, scope, mono_pgm, mapped_binders), ), - mono::Pat::Variant(mono::VariantPat { pat, ty }) => Pat::Variant { + mono::Pat::Variant(mono::VariantPat { + pat, + variant_ty, + pat_ty, + }) => Pat::Variant { pat: Box::new(lower_l_pat(pat, indices, scope, mono_pgm, mapped_binders)), - variant_ty: ty.clone(), + variant_ty: variant_ty.clone(), + pat_ty: pat_ty.clone(), }, } } diff --git a/src/lowering/printer.rs b/src/lowering/printer.rs index 9239177a..0359f7b0 100644 --- a/src/lowering/printer.rs +++ b/src/lowering/printer.rs @@ -440,7 +440,11 @@ impl Pat { p2.node.print(buf); } - Pat::Variant { pat, variant_ty: _ } => { + Pat::Variant { + pat, + variant_ty: _, + pat_ty: _, + } => { buf.push('~'); pat.node.print(buf); } diff --git a/src/mono_ast.rs b/src/mono_ast.rs index c5358347..b8825ac5 100644 --- a/src/mono_ast.rs +++ b/src/mono_ast.rs @@ -268,7 +268,8 @@ pub struct RecordPat { #[derive(Debug, Clone)] pub struct VariantPat { pub pat: Box>, - pub ty: OrdMap, // the variant type + pub variant_ty: OrdMap, + pub pat_ty: Type, } #[derive(Debug, Clone)] diff --git a/src/mono_ast/printer.rs b/src/mono_ast/printer.rs index 31c179de..65c77985 100644 --- a/src/mono_ast/printer.rs +++ b/src/mono_ast/printer.rs @@ -640,7 +640,11 @@ impl Pat { buf.push(')'); } - Pat::Variant(VariantPat { pat, ty: _ }) => { + Pat::Variant(VariantPat { + pat, + variant_ty: _, + pat_ty: _, + }) => { buf.push('~'); pat.node.print(buf); } diff --git a/src/monomorph.rs b/src/monomorph.rs index 89646e52..81430a9d 100644 --- a/src/monomorph.rs +++ b/src/monomorph.rs @@ -1347,15 +1347,23 @@ fn mono_pat( ), }), - ast::Pat::Variant(ast::VariantPat { pat, inferred_ty }) => { - mono::Pat::Variant(mono::VariantPat { - pat: mono_bl_pat(pat, ty_map, poly_pgm, mono_pgm, locals), - ty: get_variant_ty( - mono_tc_ty(inferred_ty.as_ref().unwrap(), ty_map, poly_pgm, mono_pgm), - loc, - ), - }) - } + ast::Pat::Variant(ast::VariantPat { + pat, + inferred_ty, + inferred_pat_ty, + }) => mono::Pat::Variant(mono::VariantPat { + pat: mono_bl_pat(pat, ty_map, poly_pgm, mono_pgm, locals), + variant_ty: get_variant_ty( + mono_tc_ty(inferred_ty.as_ref().unwrap(), ty_map, poly_pgm, mono_pgm), + loc, + ), + pat_ty: mono_tc_ty( + inferred_pat_ty.as_ref().unwrap(), + ty_map, + poly_pgm, + mono_pgm, + ), + }), } } diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 59b411ce..ab282478 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -974,7 +974,7 @@ Pat: Pat = { => Pat::Char(parse_char_lit(&char.text)), "~" => - Pat::Variant(VariantPat { pat: Box::new(L::new(module, l, r, pat)), inferred_ty: None }), + Pat::Variant(VariantPat { pat: Box::new(L::new(module, l, r, pat)), inferred_ty: None, inferred_pat_ty: None }), #[precedence(level = "1")] #[assoc(side = "right")] diff --git a/src/parser.rs b/src/parser.rs index 4fb004c7..20469a29 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,5 +1,5 @@ // auto-generated: "lalrpop 0.22.2" -// sha3: 52383218fe8655612aba1731ca84f6a2562106485c38d26a48f5cecea992b73e +// sha3: 7cce679c0a9108b8107aba89173064c3c44ce886f9d92d415bbe0b1ddb93c5c7 #![allow(clippy::all)] use crate::ast::*; use crate::interpolation::{copy_update_escapes, str_parts}; @@ -48887,6 +48887,7 @@ fn __action153<'a>( Pat::Variant(VariantPat { pat: Box::new(L::new(module, l, r, pat)), inferred_ty: None, + inferred_pat_ty: None, }) } diff --git a/src/to_c.rs b/src/to_c.rs index 13251bc5..60dd8f12 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -2086,6 +2086,27 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut } } +/// Given a pattern type inside a variant pattern, find which alternative in the variant it matches. +/// Returns the index of the alternative. +fn find_variant_alt_index(pat_ty: &mono::Type, variant_ty: &OrdMap) -> usize { + let type_name = match pat_ty { + mono::Type::Named(named_ty) => named_ty.name.as_str(), + _ => panic!("Non-named type in variant pattern: {:?}", pat_ty), + }; + + variant_ty + .iter() + .enumerate() + .find_map(|(idx, (name, _))| { + if name.as_str() == type_name { + Some(idx) + } else { + None + } + }) + .unwrap_or_else(|| panic!("Type {type_name} not found in variant alternatives")) +} + /// Generate a C condition expression for pattern matching. fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { match pat { @@ -2147,15 +2168,17 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { format!("({} || {})", c1, c2) } - Pat::Variant { pat, variant_ty } => { - /* - ~ - - ==> - - ??? TODO - */ - pat_to_cond(&pat.node, scrutinee, cg) + Pat::Variant { + pat, + variant_ty, + pat_ty, + } => { + let alt_idx = find_variant_alt_index(pat_ty, variant_ty); + let inner_expr = format!("({scrutinee})._alt._{alt_idx}"); + let expected_tag = gen_get_tag(cg.pgm, &inner_expr, pat_ty); + let tag_check = format!("(({scrutinee})._tag == {expected_tag})"); + let inner_cond = pat_to_cond(&pat.node, &inner_expr, cg); + format!("({tag_check} && {inner_cond})") } } } diff --git a/src/type_checker/expr.rs b/src/type_checker/expr.rs index b6e07a80..0d67f197 100644 --- a/src/type_checker/expr.rs +++ b/src/type_checker/expr.rs @@ -2270,6 +2270,7 @@ fn add_coercions( ast::Pat::Variant(ast::VariantPat { pat, inferred_ty: _, + inferred_pat_ty: _, }) => { add_coercions(&mut pat.node, refined_binders, cons, &pat.loc); } diff --git a/src/type_checker/normalization.rs b/src/type_checker/normalization.rs index a00d9838..5fb52ad3 100644 --- a/src/type_checker/normalization.rs +++ b/src/type_checker/normalization.rs @@ -270,8 +270,13 @@ fn normalize_pat(pat: &mut ast::Pat, cons: &ScopeMap) { .for_each(|ast::Named { name: _, node }| normalize_pat(&mut node.node, cons)); } - ast::Pat::Variant(ast::VariantPat { pat, inferred_ty }) => { + ast::Pat::Variant(ast::VariantPat { + pat, + inferred_ty, + inferred_pat_ty, + }) => { *inferred_ty = Some(inferred_ty.as_mut().unwrap().deep_normalize(cons)); + *inferred_pat_ty = Some(inferred_pat_ty.as_mut().unwrap().deep_normalize(cons)); normalize_pat(&mut pat.node, cons); } } diff --git a/src/type_checker/pat.rs b/src/type_checker/pat.rs index 3d653ba2..944eb953 100644 --- a/src/type_checker/pat.rs +++ b/src/type_checker/pat.rs @@ -256,12 +256,21 @@ pub(super) fn check_pat(tc_state: &mut TcFunState, pat: &mut ast::L, l ty } - ast::Pat::Variant(ast::VariantPat { pat, inferred_ty }) => { + ast::Pat::Variant(ast::VariantPat { + pat, + inferred_ty, + inferred_pat_ty, + }) => { assert!(inferred_ty.is_none()); + assert!(inferred_pat_ty.is_none()); + let pat_ty = check_pat(tc_state, pat, level); + *inferred_pat_ty = Some(pat_ty.clone()); + let variant_ty = crate::type_checker::expr::make_variant(tc_state, pat_ty, level, &pat.loc); *inferred_ty = Some(variant_ty.clone()); + variant_ty } } diff --git a/src/type_checker/pat_coverage.rs b/src/type_checker/pat_coverage.rs index 9d241294..d29ba08c 100644 --- a/src/type_checker/pat_coverage.rs +++ b/src/type_checker/pat_coverage.rs @@ -610,6 +610,7 @@ impl PatMatrix { ast::Pat::Variant(ast::VariantPat { pat, inferred_ty: _, + inferred_pat_ty: _, }) => { work.push((*pat).clone()); } diff --git a/src/type_collector.rs b/src/type_collector.rs index 47cf4b56..069d7174 100644 --- a/src/type_collector.rs +++ b/src/type_collector.rs @@ -239,10 +239,18 @@ fn visit_pat( } } - mono::Pat::Variant(mono::VariantPat { pat, ty }) => { - ty.values() + mono::Pat::Variant(mono::VariantPat { + pat, + variant_ty, + pat_ty, + }) => { + variant_ty + .values() .for_each(|ty| visit_named_ty(ty, records, variants)); - variants.insert(VariantType { alts: ty.clone() }); + visit_ty(pat_ty, records, variants); + variants.insert(VariantType { + alts: variant_ty.clone(), + }); visit_pat(&pat.node, records, variants); } } From 46d7edec20bea5de2af5fe7f6541d5bb4332bc70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 20:49:18 +0000 Subject: [PATCH 16/19] software crafting --- src/to_c.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/to_c.rs b/src/to_c.rs index 60dd8f12..4d252e39 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -153,12 +153,12 @@ pub(crate) fn to_c(pgm: &LoweredPgm, main: &str) -> String { typedef struct ExnHandler {{ jmp_buf buf; struct ExnHandler* prev; - uint64_t exn_value; + void* exn_value; // pointer to boxed exception value }} ExnHandler; static ExnHandler* current_exn_handler = NULL; - static void throw_exn(uint64_t exn) {{ + static void throw_exn(void* exn) {{ if (current_exn_handler == NULL) {{ fprintf(stderr, \"Uncaught exception\\n\"); exit(1); @@ -1026,16 +1026,13 @@ fn builtin_fun_to_c( BuiltinFunDecl::I32Neg => wln!(p, "static I32 _fun_{idx}(I32 a) {{ return -a; }}"), BuiltinFunDecl::ThrowUnchecked => { - w!( - p, - "static {} _fun_{}({} exn) {{", - c_ty(ret), - idx, - c_ty(¶ms[0]) - ); + let exn_ty = c_ty(¶ms[0]); + w!(p, "static {} _fun_{}({} exn) {{", c_ty(ret), idx, exn_ty); p.indent(); p.nl(); - wln!(p, "throw_exn((uint64_t)exn);"); + wln!(p, "{exn_ty}* boxed = malloc(sizeof({exn_ty}));"); + wln!(p, "*boxed = exn;"); + wln!(p, "throw_exn(boxed);"); w!(p, "__builtin_unreachable();"); p.dedent(); p.nl(); @@ -1090,7 +1087,8 @@ fn builtin_fun_to_c( "{err_struct_name}* err = malloc(sizeof({err_struct_name}));" ); wln!(p, "err->_tag = {};", err_tag_name); - wln!(p, "err->_0 = ({})handler.exn_value;", c_ty(&ty_args[1])); + let exn_ty = c_ty(&ty_args[1]); + wln!(p, "err->_0 = *({exn_ty}*)handler.exn_value;"); w!(p, "return ({})err;", c_ty(ret)); p.dedent(); p.nl(); From 0a1beb897cdbd5159dd51e5e8c7fa554579d7654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 21:01:28 +0000 Subject: [PATCH 17/19] code = crafted --- src/mono_ast/printer.rs | 4 ++ src/to_c.rs | 113 +++++++++++++++++++++++++++++++++++----- 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/src/mono_ast/printer.rs b/src/mono_ast/printer.rs index 65c77985..eb2359b6 100644 --- a/src/mono_ast/printer.rs +++ b/src/mono_ast/printer.rs @@ -580,6 +580,10 @@ impl Pat { buf.push_str(var); buf.push_str(": "); ty.print(buf); + if let Some(refined) = refined { + buf.push_str(" ~> "); + refined.print(buf); + } } Pat::Con(ConPat { con, fields }) => { diff --git a/src/to_c.rs b/src/to_c.rs index 4d252e39..4bf11de4 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -1452,7 +1452,7 @@ fn stmt_to_c( w!(p, "{} {} = ", c_ty(rhs_ty), rhs_temp); expr_to_c(&rhs.node, &rhs.loc, locals, cg, p); wln!(p, "; // {}", loc_display(&rhs.loc)); - wln!(p, "{};", pat_to_cond(&lhs.node, &rhs_temp, cg)); + wln!(p, "{};", pat_to_cond(&lhs.node, &rhs_temp, locals, cg)); if let Some(result_var) = result_var { wln!( p, @@ -1795,7 +1795,7 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut w!(p, " else "); } // Generate pattern match condition - let cond = pat_to_cond(&alt.pat.node, &scrut_temp, cg); + let cond = pat_to_cond(&alt.pat.node, &scrut_temp, locals, cg); w!(p, "if ({}", cond); // Add guard if present @@ -1971,7 +1971,11 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut expr_to_c(&expr.node, &expr.loc, locals, cg, p); wln!(p, "; // {}", loc_display(&expr.loc)); wln!(p, "Bool* _is_result;"); - w!(p, "if ({}) {{", pat_to_cond(&pat.node, &expr_temp, cg)); + w!( + p, + "if ({}) {{", + pat_to_cond(&pat.node, &expr_temp, locals, cg) + ); p.indent(); p.nl(); w!( @@ -2105,16 +2109,99 @@ fn find_variant_alt_index(pat_ty: &mono::Type, variant_ty: &OrdMap bool { + match (from_ty, to_ty) { + (mono::Type::Variant { alts: from_alts }, mono::Type::Variant { alts: to_alts }) => { + from_alts != to_alts + } + _ => false, + } +} + +/// Generate code to convert a value from one variant type to another: unpack the value from the +/// source variant and repack it into the target variant. +fn gen_variant_conversion( + scrutinee: &str, + from_ty: &mono::Type, + to_ty: &mono::Type, + cg: &mut Cg, +) -> String { + let (from_alts, to_alts) = match (from_ty, to_ty) { + (mono::Type::Variant { alts: from }, mono::Type::Variant { alts: to }) => (from, to), + _ => panic!("gen_variant_conversion called with non-variant types"), + }; + + let to_variant_ty = VariantType { + alts: to_alts.clone(), + }; + let to_struct_name = variant_struct_name(&to_variant_ty); + + // Handle empty target variant - this is an unreachable case at runtime, + // but we still need to generate valid C code. Just copy the tag. + if to_alts.is_empty() { + let temp = cg.fresh_temp(); + return format!( + "({{ {to_struct_name} {temp}; {temp}._tag = ({scrutinee})._tag; {temp}; }})" + ); + } + + // Find the mapping from source alternative index to target alternative index. + // The value's tag tells us which alternative is active in the source. + // We need to find the corresponding alternative in the target and repack. + + // Generate a compound expression that: + // 1. Reads the tag from the source + // 2. Based on the tag, copies the value to the appropriate field in the target + + let temp = cg.fresh_temp(); + let mut cases = String::new(); + + for (to_idx, (type_name, named_ty)) in to_alts.iter().enumerate() { + // Find this type in the source variant + let (from_idx, _) = from_alts + .iter() + .enumerate() + .find(|(_, (name, _))| *name == type_name) + .unwrap_or_else(|| { + panic!( + "Type {} not found in source variant during conversion", + type_name + ) + }); + + let alt_ty = mono::Type::Named(named_ty.clone()); + let expected_tag = gen_get_tag(cg.pgm, &format!("({scrutinee})._alt._{from_idx}"), &alt_ty); + + if !cases.is_empty() { + cases.push_str(" else "); + } + cases.push_str(&format!( + "if (({scrutinee})._tag == {expected_tag}) {{ {temp}._tag = {expected_tag}; {temp}._alt._{to_idx} = ({scrutinee})._alt._{from_idx}; }}" + )); + } + + // Add a fallback case (should never happen if types are correct) + cases.push_str(" else { fprintf(stderr, \"Invalid variant conversion\\n\"); exit(1); }"); + + format!("({{ {to_struct_name} {temp}; {cases} {temp}; }})") +} + /// Generate a C condition expression for pattern matching. -fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { +fn pat_to_cond(pat: &Pat, scrutinee: &str, locals: &[LocalInfo], cg: &mut Cg) -> String { match pat { Pat::Ignore => "1".to_string(), - Pat::Var(VarPat { - idx, - original_ty: _, - }) => { - format!("({{ _{} = {}; 1; }})", idx.as_usize(), scrutinee) + Pat::Var(VarPat { idx, original_ty }) => { + let refined_ty = &locals[idx.as_usize()].ty; + + if needs_variant_conversion(original_ty, refined_ty) { + let conversion = gen_variant_conversion(scrutinee, original_ty, refined_ty, cg); + format!("({{ _{} = {}; 1; }})", idx.as_usize(), conversion) + } else { + format!("({{ _{} = {}; 1; }})", idx.as_usize(), scrutinee) + } } Pat::Con(ConPat { con, fields }) => { @@ -2124,7 +2211,7 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { let mut cond = format!("(get_tag({}) == {})", scrutinee, tag_name); for (i, field_pat) in fields.iter().enumerate() { let field_expr = format!("(({struct_name}*){scrutinee})->_{i}"); - let field_cond = pat_to_cond(&field_pat.node, &field_expr, cg); + let field_cond = pat_to_cond(&field_pat.node, &field_expr, locals, cg); cond = format!("({} && {})", cond, field_cond); } cond @@ -2161,8 +2248,8 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { } Pat::Or(p1, p2) => { - let c1 = pat_to_cond(&p1.node, scrutinee, cg); - let c2 = pat_to_cond(&p2.node, scrutinee, cg); + let c1 = pat_to_cond(&p1.node, scrutinee, locals, cg); + let c2 = pat_to_cond(&p2.node, scrutinee, locals, cg); format!("({} || {})", c1, c2) } @@ -2175,7 +2262,7 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, cg: &mut Cg) -> String { let inner_expr = format!("({scrutinee})._alt._{alt_idx}"); let expected_tag = gen_get_tag(cg.pgm, &inner_expr, pat_ty); let tag_check = format!("(({scrutinee})._tag == {expected_tag})"); - let inner_cond = pat_to_cond(&pat.node, &inner_expr, cg); + let inner_cond = pat_to_cond(&pat.node, &inner_expr, locals, cg); format!("({tag_check} && {inner_cond})") } } From 8b89ebd0a687bef8ff225f5607086eea9860b363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 21:22:07 +0000 Subject: [PATCH 18/19] no mistakes --- src/to_c.rs | 70 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/to_c.rs b/src/to_c.rs index 4bf11de4..7358b49c 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -1452,7 +1452,11 @@ fn stmt_to_c( w!(p, "{} {} = ", c_ty(rhs_ty), rhs_temp); expr_to_c(&rhs.node, &rhs.loc, locals, cg, p); wln!(p, "; // {}", loc_display(&rhs.loc)); - wln!(p, "{};", pat_to_cond(&lhs.node, &rhs_temp, locals, cg)); + wln!( + p, + "{};", + pat_to_cond(&lhs.node, &rhs_temp, None, locals, cg) + ); if let Some(result_var) = result_var { wln!( p, @@ -1795,7 +1799,7 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut w!(p, " else "); } // Generate pattern match condition - let cond = pat_to_cond(&alt.pat.node, &scrut_temp, locals, cg); + let cond = pat_to_cond(&alt.pat.node, &scrut_temp, None, locals, cg); w!(p, "if ({}", cond); // Add guard if present @@ -1974,7 +1978,7 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut w!( p, "if ({}) {{", - pat_to_cond(&pat.node, &expr_temp, locals, cg) + pat_to_cond(&pat.node, &expr_temp, None, locals, cg) ); p.indent(); p.nl(); @@ -2189,13 +2193,24 @@ fn gen_variant_conversion( } /// Generate a C condition expression for pattern matching. -fn pat_to_cond(pat: &Pat, scrutinee: &str, locals: &[LocalInfo], cg: &mut Cg) -> String { +/// +/// - `scrutinee` is the expression being matched against. +/// +/// - `tag_expr` is an optional override for how to get the tag. When `Some`, use that expression +/// directly (e.g., for variant patterns where we check the variant's `_tag` field). When `None`, +/// derive the tag from the scrutinee using `get_tag(scrutinee)`. +fn pat_to_cond( + pat: &Pat, + scrutinee: &str, + tag_expr: Option<&str>, + locals: &[LocalInfo], + cg: &mut Cg, +) -> String { match pat { Pat::Ignore => "1".to_string(), Pat::Var(VarPat { idx, original_ty }) => { let refined_ty = &locals[idx.as_usize()].ty; - if needs_variant_conversion(original_ty, refined_ty) { let conversion = gen_variant_conversion(scrutinee, original_ty, refined_ty, cg); format!("({{ _{} = {}; 1; }})", idx.as_usize(), conversion) @@ -2207,12 +2222,15 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, locals: &[LocalInfo], cg: &mut Cg) -> Pat::Con(ConPat { con, fields }) => { let struct_name = heap_obj_struct_name(cg.pgm, *con); let tag_name = heap_obj_tag_name(cg.pgm, *con); - // let get_tag_expr = gen_get_tag(&cg.pgm, scrutinee, ty); TODO - let mut cond = format!("(get_tag({}) == {})", scrutinee, tag_name); + let tag_check = match tag_expr { + Some(expr) => format!("({expr} == {tag_name})"), + None => format!("(get_tag({scrutinee}) == {tag_name})"), + }; + let mut cond = tag_check; for (i, field_pat) in fields.iter().enumerate() { let field_expr = format!("(({struct_name}*){scrutinee})->_{i}"); - let field_cond = pat_to_cond(&field_pat.node, &field_expr, locals, cg); - cond = format!("({} && {})", cond, field_cond); + let field_cond = pat_to_cond(&field_pat.node, &field_expr, None, locals, cg); + cond = format!("({cond} && {field_cond})"); } cond } @@ -2221,36 +2239,34 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, locals: &[LocalInfo], cg: &mut Cg) -> let mut escaped = String::new(); for byte in s.bytes() { if byte == b'"' || byte == b'\\' || !(32..=126).contains(&byte) { - // Same as `Expr::Str`, use octal escape here instead of hex. escaped.push_str(&format!("\\{:03o}", byte)); } else { escaped.push(byte as char); } } - // Note: the type cast below is to handle strings in variants. Variants are currently - // `Variant*` so they need to be cast. + let tag_check = match tag_expr { + Some(expr) => format!("({expr} == {})", cg.pgm.str_con_idx.0), + None => format!("(get_tag({scrutinee}) == {})", cg.pgm.str_con_idx.0), + }; format!( - "(get_tag({}) == {} && str_eq((Str*){}, \"{}\", {}))", - scrutinee, - cg.pgm.str_con_idx.0, - scrutinee, - escaped, + "({tag_check} && str_eq((Str*){scrutinee}, \"{escaped}\", {}))", s.len() ) } Pat::Char(c) => { let tag_name = heap_obj_tag_name(cg.pgm, cg.pgm.char_con_idx); - format!( - "(get_tag({}) == {} && ((Char*){})->_0 == {})", - scrutinee, tag_name, scrutinee, *c as u32 - ) + let tag_check = match tag_expr { + Some(expr) => format!("({expr} == {tag_name})"), + None => format!("(get_tag({scrutinee}) == {tag_name})"), + }; + format!("({tag_check} && ((Char*){scrutinee})->_0 == {})", *c as u32) } Pat::Or(p1, p2) => { - let c1 = pat_to_cond(&p1.node, scrutinee, locals, cg); - let c2 = pat_to_cond(&p2.node, scrutinee, locals, cg); - format!("({} || {})", c1, c2) + let c1 = pat_to_cond(&p1.node, scrutinee, tag_expr, locals, cg); + let c2 = pat_to_cond(&p2.node, scrutinee, tag_expr, locals, cg); + format!("({c1} || {c2})") } Pat::Variant { @@ -2260,10 +2276,8 @@ fn pat_to_cond(pat: &Pat, scrutinee: &str, locals: &[LocalInfo], cg: &mut Cg) -> } => { let alt_idx = find_variant_alt_index(pat_ty, variant_ty); let inner_expr = format!("({scrutinee})._alt._{alt_idx}"); - let expected_tag = gen_get_tag(cg.pgm, &inner_expr, pat_ty); - let tag_check = format!("(({scrutinee})._tag == {expected_tag})"); - let inner_cond = pat_to_cond(&pat.node, &inner_expr, locals, cg); - format!("({tag_check} && {inner_cond})") + let variant_tag_expr = format!("({scrutinee})._tag"); + pat_to_cond(&pat.node, &inner_expr, Some(&variant_tag_expr), locals, cg) } } } From f933b16792056a1cf37aadd64b4d4dee8f105b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 5 Feb 2026 21:27:43 +0000 Subject: [PATCH 19/19] use gen_get_tag --- src/to_c.rs | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/src/to_c.rs b/src/to_c.rs index 7358b49c..d9671d3a 100644 --- a/src/to_c.rs +++ b/src/to_c.rs @@ -1455,7 +1455,7 @@ fn stmt_to_c( wln!( p, "{};", - pat_to_cond(&lhs.node, &rhs_temp, None, locals, cg) + pat_to_cond(&lhs.node, &rhs_temp, rhs_ty, None, locals, cg) ); if let Some(result_var) = result_var { wln!( @@ -1799,7 +1799,7 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut w!(p, " else "); } // Generate pattern match condition - let cond = pat_to_cond(&alt.pat.node, &scrut_temp, None, locals, cg); + let cond = pat_to_cond(&alt.pat.node, &scrut_temp, scrut_ty, None, locals, cg); w!(p, "if ({}", cond); // Add guard if present @@ -1978,7 +1978,7 @@ fn expr_to_c(expr: &Expr, loc: &Loc, locals: &[LocalInfo], cg: &mut Cg, p: &mut w!( p, "if ({}) {{", - pat_to_cond(&pat.node, &expr_temp, None, locals, cg) + pat_to_cond(&pat.node, &expr_temp, expr_ty, None, locals, cg) ); p.indent(); p.nl(); @@ -2196,12 +2196,15 @@ fn gen_variant_conversion( /// /// - `scrutinee` is the expression being matched against. /// +/// - `scrutinee_ty` is the type of the scrutinee (used for generating tag checks with `gen_get_tag`). +/// /// - `tag_expr` is an optional override for how to get the tag. When `Some`, use that expression /// directly (e.g., for variant patterns where we check the variant's `_tag` field). When `None`, -/// derive the tag from the scrutinee using `get_tag(scrutinee)`. +/// derive the tag from the scrutinee using `gen_get_tag`. fn pat_to_cond( pat: &Pat, scrutinee: &str, + scrutinee_ty: &mono::Type, tag_expr: Option<&str>, locals: &[LocalInfo], cg: &mut Cg, @@ -2224,12 +2227,23 @@ fn pat_to_cond( let tag_name = heap_obj_tag_name(cg.pgm, *con); let tag_check = match tag_expr { Some(expr) => format!("({expr} == {tag_name})"), - None => format!("(get_tag({scrutinee}) == {tag_name})"), + None => { + let get_tag = gen_get_tag(cg.pgm, scrutinee, scrutinee_ty); + format!("({get_tag} == {tag_name})") + } + }; + let field_tys: Vec = match &cg.pgm.heap_objs[con.as_usize()] { + HeapObj::Source(source_con) => source_con.fields.clone(), + HeapObj::Record(record) => record.fields.values().cloned().collect(), + HeapObj::Builtin(_) => panic!("Builtin constructor {:?} in Pat::Con", con), + HeapObj::Variant(_) => panic!("Variant in Pat::Con"), }; let mut cond = tag_check; for (i, field_pat) in fields.iter().enumerate() { let field_expr = format!("(({struct_name}*){scrutinee})->_{i}"); - let field_cond = pat_to_cond(&field_pat.node, &field_expr, None, locals, cg); + let field_ty = &field_tys[i]; + let field_cond = + pat_to_cond(&field_pat.node, &field_expr, field_ty, None, locals, cg); cond = format!("({cond} && {field_cond})"); } cond @@ -2246,7 +2260,10 @@ fn pat_to_cond( } let tag_check = match tag_expr { Some(expr) => format!("({expr} == {})", cg.pgm.str_con_idx.0), - None => format!("(get_tag({scrutinee}) == {})", cg.pgm.str_con_idx.0), + None => { + let get_tag = gen_get_tag(cg.pgm, scrutinee, scrutinee_ty); + format!("({get_tag} == {})", cg.pgm.str_con_idx.0) + } }; format!( "({tag_check} && str_eq((Str*){scrutinee}, \"{escaped}\", {}))", @@ -2258,14 +2275,17 @@ fn pat_to_cond( let tag_name = heap_obj_tag_name(cg.pgm, cg.pgm.char_con_idx); let tag_check = match tag_expr { Some(expr) => format!("({expr} == {tag_name})"), - None => format!("(get_tag({scrutinee}) == {tag_name})"), + None => { + let get_tag = gen_get_tag(cg.pgm, scrutinee, scrutinee_ty); + format!("({get_tag} == {tag_name})") + } }; format!("({tag_check} && ((Char*){scrutinee})->_0 == {})", *c as u32) } Pat::Or(p1, p2) => { - let c1 = pat_to_cond(&p1.node, scrutinee, tag_expr, locals, cg); - let c2 = pat_to_cond(&p2.node, scrutinee, tag_expr, locals, cg); + let c1 = pat_to_cond(&p1.node, scrutinee, scrutinee_ty, tag_expr, locals, cg); + let c2 = pat_to_cond(&p2.node, scrutinee, scrutinee_ty, tag_expr, locals, cg); format!("({c1} || {c2})") } @@ -2277,7 +2297,14 @@ fn pat_to_cond( let alt_idx = find_variant_alt_index(pat_ty, variant_ty); let inner_expr = format!("({scrutinee})._alt._{alt_idx}"); let variant_tag_expr = format!("({scrutinee})._tag"); - pat_to_cond(&pat.node, &inner_expr, Some(&variant_tag_expr), locals, cg) + pat_to_cond( + &pat.node, + &inner_expr, + pat_ty, + Some(&variant_tag_expr), + locals, + cg, + ) } } }