1use super::constraint::Constraint;
10use super::types::{InferType, TypeVarAllocator, TypeVarId};
11use crate::Type;
12use crate::intern_pool::TypeInternPool;
13use crate::scope::ScopedContext;
14use crate::types::{
15 EnumId, PtrMutability, StructId, parse_array_type_syntax, parse_pointer_type_syntax,
16};
17use gruel_rir::{InstData, InstRef, Rir};
18use gruel_span::Span;
19use lasso::{Spur, ThreadedRodeo};
20use std::collections::HashMap;
21
22#[derive(Debug, Clone)]
24pub struct LocalVarInfo {
25 pub ty: InferType,
27 pub is_mut: bool,
29 pub span: Span,
31}
32
33#[derive(Debug, Clone)]
35pub struct ParamVarInfo {
36 pub ty: InferType,
38}
39
40#[derive(Debug, Clone)]
46pub struct FunctionSig {
47 pub param_types: Vec<InferType>,
49 pub return_type: InferType,
51 pub is_generic: bool,
55 pub param_modes: Vec<gruel_rir::RirParamMode>,
57 pub param_comptime: Vec<bool>,
61 pub param_names: Vec<lasso::Spur>,
63 pub return_type_sym: lasso::Spur,
65}
66
67#[derive(Debug, Clone)]
71pub struct MethodSig {
72 pub struct_type: Type,
74 pub has_self: bool,
76 pub param_types: Vec<InferType>,
78 pub return_type: InferType,
80}
81
82pub struct ConstraintContext<'a> {
84 pub locals: HashMap<Spur, LocalVarInfo>,
86 pub params: &'a HashMap<Spur, ParamVarInfo>,
88 pub return_type: Type,
90 pub loop_depth: u32,
92 scope_stack: Vec<Vec<(Spur, Option<LocalVarInfo>)>>,
94}
95
96impl<'a> ConstraintContext<'a> {
97 pub fn new(params: &'a HashMap<Spur, ParamVarInfo>, return_type: Type) -> Self {
99 Self {
100 locals: HashMap::new(),
101 params,
102 return_type,
103 loop_depth: 0,
104 scope_stack: Vec::new(),
105 }
106 }
107}
108
109impl ScopedContext for ConstraintContext<'_> {
110 type VarInfo = LocalVarInfo;
111
112 fn locals_mut(&mut self) -> &mut HashMap<Spur, Self::VarInfo> {
113 &mut self.locals
114 }
115
116 fn scope_stack_mut(&mut self) -> &mut Vec<Vec<(Spur, Option<Self::VarInfo>)>> {
117 &mut self.scope_stack
118 }
119}
120
121#[derive(Debug, Clone)]
123pub struct ExprInfo {
124 pub ty: InferType,
126 pub span: Span,
128}
129
130impl ExprInfo {
131 pub fn new(ty: InferType, span: Span) -> Self {
133 Self { ty, span }
134 }
135}
136
137pub struct ConstraintGenerator<'a> {
142 rir: &'a Rir,
144 interner: &'a ThreadedRodeo,
146 type_vars: TypeVarAllocator,
148 constraints: Vec<Constraint>,
150 expr_types: HashMap<InstRef, InferType>,
152 functions: &'a HashMap<Spur, FunctionSig>,
154 structs: &'a HashMap<Spur, Type>,
156 enums: &'a HashMap<Spur, Type>,
158 methods: &'a HashMap<(StructId, Spur), MethodSig>,
160 enum_methods: &'a HashMap<(EnumId, Spur), MethodSig>,
162 int_literal_vars: Vec<TypeVarId>,
165 float_literal_vars: Vec<TypeVarId>,
168 type_subst: Option<&'a HashMap<Spur, Type>>,
171 type_pool: &'a TypeInternPool,
173}
174
175impl<'a> ConstraintGenerator<'a> {
176 #[allow(clippy::too_many_arguments)]
178 pub fn new(
179 rir: &'a Rir,
180 interner: &'a ThreadedRodeo,
181 functions: &'a HashMap<Spur, FunctionSig>,
182 structs: &'a HashMap<Spur, Type>,
183 enums: &'a HashMap<Spur, Type>,
184 methods: &'a HashMap<(StructId, Spur), MethodSig>,
185 enum_methods: &'a HashMap<(EnumId, Spur), MethodSig>,
186 type_pool: &'a TypeInternPool,
187 ) -> Self {
188 Self {
189 rir,
190 interner,
191 type_vars: TypeVarAllocator::new(),
192 constraints: Vec::new(),
193 expr_types: HashMap::new(),
194 functions,
195 structs,
196 enums,
197 methods,
198 enum_methods,
199 int_literal_vars: Vec::new(),
200 float_literal_vars: Vec::new(),
201 type_subst: None,
202 type_pool,
203 }
204 }
205
206 pub fn with_type_subst(mut self, type_subst: Option<&'a HashMap<Spur, Type>>) -> Self {
213 self.type_subst = type_subst;
214 self
215 }
216
217 pub fn int_literal_vars(&self) -> &[TypeVarId] {
219 &self.int_literal_vars
220 }
221
222 pub fn fresh_var(&mut self) -> TypeVarId {
224 self.type_vars.fresh()
225 }
226
227 pub fn add_constraint(&mut self, constraint: Constraint) {
229 self.constraints.push(constraint);
230 }
231
232 pub fn record_type(&mut self, inst_ref: InstRef, ty: InferType) {
234 self.expr_types.insert(inst_ref, ty);
235 }
236
237 pub fn get_type(&self, inst_ref: InstRef) -> Option<&InferType> {
239 self.expr_types.get(&inst_ref)
240 }
241
242 pub fn constraints(&self) -> &[Constraint] {
244 &self.constraints
245 }
246
247 pub fn take_constraints(self) -> Vec<Constraint> {
249 self.constraints
250 }
251
252 pub fn expr_types(&self) -> &HashMap<InstRef, InferType> {
254 &self.expr_types
255 }
256
257 pub fn into_parts(
262 self,
263 ) -> (
264 Vec<Constraint>,
265 Vec<TypeVarId>,
266 Vec<TypeVarId>,
267 HashMap<InstRef, InferType>,
268 u32,
269 ) {
270 (
271 self.constraints,
272 self.int_literal_vars,
273 self.float_literal_vars,
274 self.expr_types,
275 self.type_vars.count(),
276 )
277 }
278
279 pub fn generate(&mut self, inst_ref: InstRef, ctx: &mut ConstraintContext) -> ExprInfo {
284 let inst = self.rir.get(inst_ref);
285 let span = inst.span;
286
287 let ty = match &inst.data {
288 InstData::IntConst(_) => {
289 let var = self.fresh_var();
301 self.int_literal_vars.push(var);
302 InferType::Var(var)
303 }
304
305 InstData::FloatConst(_) => {
306 let var = self.fresh_var();
308 self.float_literal_vars.push(var);
309 InferType::Var(var)
310 }
311
312 InstData::BoolConst(_) => InferType::Concrete(Type::BOOL),
313
314 InstData::StringConst(_) => {
316 if let Some(string_spur) = self.interner.get("String") {
318 if let Some(&string_ty) = self.structs.get(&string_spur) {
319 InferType::Concrete(string_ty)
320 } else {
321 InferType::Concrete(Type::ERROR)
323 }
324 } else {
325 InferType::Concrete(Type::ERROR)
326 }
327 }
328
329 InstData::UnitConst => InferType::Concrete(Type::UNIT),
330
331 InstData::Add { lhs, rhs }
333 | InstData::Sub { lhs, rhs }
334 | InstData::Mul { lhs, rhs }
335 | InstData::Div { lhs, rhs }
336 | InstData::Mod { lhs, rhs } => self.generate_binary_arith(*lhs, *rhs, ctx),
337
338 InstData::BitAnd { lhs, rhs }
340 | InstData::BitOr { lhs, rhs }
341 | InstData::BitXor { lhs, rhs }
342 | InstData::Shl { lhs, rhs }
343 | InstData::Shr { lhs, rhs } => self.generate_binary_bitwise(*lhs, *rhs, ctx),
344
345 InstData::Eq { lhs, rhs }
347 | InstData::Ne { lhs, rhs }
348 | InstData::Lt { lhs, rhs }
349 | InstData::Gt { lhs, rhs }
350 | InstData::Le { lhs, rhs }
351 | InstData::Ge { lhs, rhs } => {
352 let lhs_info = self.generate(*lhs, ctx);
353 let rhs_info = self.generate(*rhs, ctx);
354 self.add_constraint(Constraint::equal(lhs_info.ty, rhs_info.ty, span));
356 InferType::Concrete(Type::BOOL)
357 }
358
359 InstData::And { lhs, rhs } | InstData::Or { lhs, rhs } => {
361 let lhs_info = self.generate(*lhs, ctx);
362 let rhs_info = self.generate(*rhs, ctx);
363 self.add_constraint(Constraint::equal(
364 lhs_info.ty,
365 InferType::Concrete(Type::BOOL),
366 lhs_info.span,
367 ));
368 self.add_constraint(Constraint::equal(
369 rhs_info.ty,
370 InferType::Concrete(Type::BOOL),
371 rhs_info.span,
372 ));
373 InferType::Concrete(Type::BOOL)
374 }
375
376 InstData::Neg { operand } => {
378 let operand_info = self.generate(*operand, ctx);
379 let result_ty = operand_info.ty.clone();
381 self.add_constraint(Constraint::is_signed(result_ty.clone(), span));
383 result_ty
384 }
385
386 InstData::Not { operand } => {
388 let operand_info = self.generate(*operand, ctx);
389 self.add_constraint(Constraint::equal(
390 operand_info.ty,
391 InferType::Concrete(Type::BOOL),
392 operand_info.span,
393 ));
394 InferType::Concrete(Type::BOOL)
395 }
396
397 InstData::BitNot { operand } => {
399 let operand_info = self.generate(*operand, ctx);
400 let result_ty = operand_info.ty.clone();
401 self.add_constraint(Constraint::is_integer(result_ty.clone(), span));
403 result_ty
404 }
405
406 InstData::VarRef { name } => {
408 if let Some(local) = ctx.locals.get(name) {
409 local.ty.clone()
410 } else if let Some(param) = ctx.params.get(name) {
411 param.ty.clone()
412 } else {
413 InferType::Concrete(Type::ERROR)
415 }
416 }
417
418 InstData::ParamRef { name, .. } => {
420 if let Some(param) = ctx.params.get(name) {
421 param.ty.clone()
422 } else {
423 InferType::Concrete(Type::ERROR)
424 }
425 }
426
427 InstData::Alloc {
429 directives_start: _,
430 directives_len: _,
431 name,
432 is_mut,
433 ty: type_annotation,
434 init,
435 } => {
436 let init_info = self.generate(*init, ctx);
437
438 let var_ty = if let Some(ty_sym) = type_annotation {
439 let ty_name = self.interner.resolve(ty_sym);
441 if let Some(annotated_ty) = self.resolve_type_name(ty_name) {
442 self.add_constraint(Constraint::equal(
443 init_info.ty,
444 annotated_ty.clone(),
445 span,
446 ));
447 annotated_ty
448 } else {
449 init_info.ty
453 }
454 } else {
455 init_info.ty
457 };
458
459 if let Some(var_name) = name {
461 ctx.insert_local(
462 *var_name,
463 LocalVarInfo {
464 ty: var_ty.clone(),
465 is_mut: *is_mut,
466 span,
467 },
468 );
469 }
470
471 InferType::Concrete(Type::UNIT)
473 }
474
475 InstData::StructDestructure {
477 type_name,
478 fields_start,
479 fields_len,
480 init,
481 } => {
482 self.generate(*init, ctx);
483
484 if let Some(&struct_ty) = self.structs.get(type_name)
486 && let Some(struct_id) = struct_ty.as_struct()
487 {
488 let struct_def = self.type_pool.struct_def(struct_id);
489 let rir_fields = self.rir.get_destructure_fields(*fields_start, *fields_len);
490 for field in &rir_fields {
491 if field.is_wildcard {
492 continue;
493 }
494 let field_name = self.interner.resolve(&field.field_name);
495 if let Some((_, struct_field)) = struct_def.find_field(field_name) {
496 let binding_name = field.binding_name.unwrap_or(field.field_name);
497 ctx.insert_local(
498 binding_name,
499 LocalVarInfo {
500 ty: InferType::Concrete(struct_field.ty),
501 is_mut: field.is_mut,
502 span,
503 },
504 );
505 }
506 }
507 }
508
509 InferType::Concrete(Type::UNIT)
510 }
511
512 InstData::Assign { name, value } => {
514 let value_info = self.generate(*value, ctx);
515 if let Some(local) = ctx.locals.get(name) {
516 self.add_constraint(Constraint::equal(value_info.ty, local.ty.clone(), span));
518 }
519 InferType::Concrete(Type::UNIT)
521 }
522
523 InstData::Ret(value) => {
525 if let Some(val_ref) = value {
526 let value_info = self.generate(*val_ref, ctx);
527 self.add_constraint(Constraint::equal(
529 value_info.ty,
530 InferType::Concrete(ctx.return_type),
531 span,
532 ));
533 } else {
534 self.add_constraint(Constraint::equal(
536 InferType::Concrete(Type::UNIT),
537 InferType::Concrete(ctx.return_type),
538 span,
539 ));
540 }
541 InferType::Concrete(Type::NEVER)
543 }
544
545 InstData::Call {
547 name,
548 args_start,
549 args_len,
550 } => {
551 let args = self.rir.get_call_args(*args_start, *args_len);
552 if let Some(func) = self.functions.get(name) {
553 if func.is_generic {
557 let mut type_subst: std::collections::HashMap<lasso::Spur, Type> =
559 std::collections::HashMap::new();
560
561 for (i, arg) in args.iter().enumerate() {
562 let arg_info = self.generate(arg.value, ctx);
563
564 if i < func.param_comptime.len() && func.param_comptime[i] {
566 if let InferType::Concrete(Type::COMPTIME_TYPE) = &arg_info.ty {
568 let arg_inst = self.rir.get(arg.value);
570 if let gruel_rir::InstData::TypeConst { type_name } =
571 &arg_inst.data
572 {
573 let type_name_str = self.interner.resolve(type_name);
575 let concrete_ty = match type_name_str {
576 "i8" => Type::I8,
577 "i16" => Type::I16,
578 "i32" => Type::I32,
579 "i64" => Type::I64,
580 "u8" => Type::U8,
581 "u16" => Type::U16,
582 "u32" => Type::U32,
583 "u64" => Type::U64,
584 "bool" => Type::BOOL,
585 "()" => Type::UNIT,
586 _ => Type::ERROR, };
588 if i < func.param_names.len() {
589 type_subst.insert(func.param_names[i], concrete_ty);
590 }
591 }
592 }
593 }
594 }
595
596 if func.return_type == InferType::Concrete(Type::COMPTIME_TYPE) {
599 if let Some(&concrete_ty) = type_subst.get(&func.return_type_sym) {
601 InferType::Concrete(concrete_ty)
602 } else {
603 func.return_type.clone()
604 }
605 } else {
606 func.return_type.clone()
607 }
608 } else if args.len() != func.param_types.len() {
609 for arg in args.iter() {
614 self.generate(arg.value, ctx);
615 }
616 func.return_type.clone()
618 } else {
619 for (arg, param_ty) in args.iter().zip(func.param_types.iter()) {
621 let arg_info = self.generate(arg.value, ctx);
622 self.add_constraint(Constraint::equal(
623 arg_info.ty,
624 param_ty.clone(),
625 arg_info.span,
626 ));
627 }
628 func.return_type.clone()
629 }
630 } else {
631 for arg in args.iter() {
633 self.generate(arg.value, ctx);
634 }
635 InferType::Concrete(Type::ERROR)
636 }
637 }
638
639 InstData::Intrinsic {
641 name,
642 args_start,
643 args_len,
644 } => {
645 let intrinsic_name = self.interner.resolve(name);
646 let args = self.rir.get_inst_refs(*args_start, *args_len);
647
648 if intrinsic_name == "intCast" || intrinsic_name == "cast" {
649 if !args.is_empty() {
652 let arg_info = self.generate(args[0], ctx);
653 let _ = arg_info;
654 }
655 let result_var = self.fresh_var();
657 InferType::Var(result_var)
658 } else if intrinsic_name == "read_line" {
659 if let Some(string_spur) = self.interner.get("String") {
661 if let Some(&string_ty) = self.structs.get(&string_spur) {
662 InferType::Concrete(string_ty)
663 } else {
664 InferType::Concrete(Type::ERROR)
666 }
667 } else {
668 InferType::Concrete(Type::ERROR)
669 }
670 } else if intrinsic_name == "parse_i32" {
671 for arg_ref in args.iter() {
673 self.generate(*arg_ref, ctx);
674 }
675 InferType::Concrete(Type::I32)
676 } else if intrinsic_name == "parse_i64" {
677 for arg_ref in args.iter() {
679 self.generate(*arg_ref, ctx);
680 }
681 InferType::Concrete(Type::I64)
682 } else if intrinsic_name == "parse_u32" {
683 for arg_ref in args.iter() {
685 self.generate(*arg_ref, ctx);
686 }
687 InferType::Concrete(Type::U32)
688 } else if intrinsic_name == "parse_u64" {
689 for arg_ref in args.iter() {
691 self.generate(*arg_ref, ctx);
692 }
693 InferType::Concrete(Type::U64)
694 } else if intrinsic_name == "random_u32" {
695 InferType::Concrete(Type::U32)
697 } else if intrinsic_name == "random_u64" {
698 InferType::Concrete(Type::U64)
700 } else if intrinsic_name == "syscall" {
701 for arg_ref in args.iter() {
703 self.generate(*arg_ref, ctx);
704 }
705 InferType::Concrete(Type::I64)
706 } else if intrinsic_name == "ptr_to_int" {
707 for arg_ref in args.iter() {
709 self.generate(*arg_ref, ctx);
710 }
711 InferType::Concrete(Type::U64)
712 } else if intrinsic_name == "ptr_write" {
713 for arg_ref in args.iter() {
715 self.generate(*arg_ref, ctx);
716 }
717 InferType::Concrete(Type::UNIT)
718 } else if intrinsic_name == "is_null" {
719 for arg_ref in args.iter() {
721 self.generate(*arg_ref, ctx);
722 }
723 InferType::Concrete(Type::BOOL)
724 } else if intrinsic_name == "ptr_read" {
725 for arg_ref in args.iter() {
730 self.generate(*arg_ref, ctx);
731 }
732 let result_var = self.fresh_var();
733 InferType::Var(result_var)
734 } else if intrinsic_name == "ptr_offset" {
735 for arg_ref in args.iter() {
739 self.generate(*arg_ref, ctx);
740 }
741 let result_var = self.fresh_var();
742 InferType::Var(result_var)
743 } else if intrinsic_name == "raw" || intrinsic_name == "raw_mut" {
744 for arg_ref in args.iter() {
748 self.generate(*arg_ref, ctx);
749 }
750 let result_var = self.fresh_var();
751 InferType::Var(result_var)
752 } else if intrinsic_name == "int_to_ptr" || intrinsic_name == "null_ptr" {
753 for arg_ref in args.iter() {
755 self.generate(*arg_ref, ctx);
756 }
757 let result_var = self.fresh_var();
758 InferType::Var(result_var)
759 } else if intrinsic_name == "ptr_copy" {
760 for arg_ref in args.iter() {
762 self.generate(*arg_ref, ctx);
763 }
764 InferType::Concrete(Type::UNIT)
765 } else if intrinsic_name == "target_arch" {
766 if let Some(arch_spur) = self.interner.get("Arch") {
768 if let Some(&arch_ty) = self.enums.get(&arch_spur) {
769 InferType::Concrete(arch_ty)
770 } else {
771 InferType::Concrete(Type::ERROR)
772 }
773 } else {
774 InferType::Concrete(Type::ERROR)
775 }
776 } else if intrinsic_name == "target_os" {
777 if let Some(os_spur) = self.interner.get("Os") {
779 if let Some(&os_ty) = self.enums.get(&os_spur) {
780 InferType::Concrete(os_ty)
781 } else {
782 InferType::Concrete(Type::ERROR)
783 }
784 } else {
785 InferType::Concrete(Type::ERROR)
786 }
787 } else if intrinsic_name == "range" {
788 if !args.is_empty() {
791 let first = self.generate(args[0], ctx);
792 for arg_ref in args.iter().skip(1) {
793 let arg_info = self.generate(*arg_ref, ctx);
794 self.add_constraint(Constraint::equal(
795 first.ty.clone(),
796 arg_info.ty,
797 span,
798 ));
799 }
800 first.ty
801 } else {
802 InferType::Concrete(Type::ERROR)
803 }
804 } else if intrinsic_name == "field" {
805 for arg_ref in args.iter() {
810 self.generate(*arg_ref, ctx);
811 }
812 let result_var = self.fresh_var();
813 InferType::Var(result_var)
814 } else {
815 for arg_ref in args.iter() {
817 self.generate(*arg_ref, ctx);
818 }
819 InferType::Concrete(Type::UNIT)
821 }
822 }
823
824 InstData::TypeIntrinsic { name, type_arg: _ } => {
826 let intrinsic_name = self.interner.resolve(name);
827 match intrinsic_name {
828 "typeName" => InferType::Concrete(Type::COMPTIME_STR),
829 "typeInfo" => {
830 InferType::Var(self.fresh_var())
833 }
834 _ => {
835 InferType::Concrete(Type::I32)
837 }
838 }
839 }
840
841 InstData::Block { extra_start, len } => {
843 ctx.push_scope();
844 let mut last_ty = InferType::Concrete(Type::UNIT);
845 let block_insts = self.rir.get_extra(*extra_start, *len);
846 for &inst_raw in block_insts {
847 let block_inst_ref = InstRef::from_raw(inst_raw);
848 let info = self.generate(block_inst_ref, ctx);
849 last_ty = info.ty;
850 }
851 ctx.pop_scope();
852 last_ty
853 }
854
855 InstData::Branch {
857 cond,
858 then_block,
859 else_block,
860 } => {
861 let cond_info = self.generate(*cond, ctx);
862 self.add_constraint(Constraint::equal(
863 cond_info.ty,
864 InferType::Concrete(Type::BOOL),
865 cond_info.span,
866 ));
867
868 let then_info = self.generate(*then_block, ctx);
869
870 if let Some(else_ref) = else_block {
871 let else_info = self.generate(*else_ref, ctx);
872
873 let then_is_never = matches!(&then_info.ty, InferType::Concrete(Type::NEVER));
878 let else_is_never = matches!(&else_info.ty, InferType::Concrete(Type::NEVER));
879
880 match (then_is_never, else_is_never) {
881 (true, true) => {
882 InferType::Concrete(Type::NEVER)
884 }
885 (true, false) => {
886 else_info.ty
888 }
889 (false, true) => {
890 then_info.ty
892 }
893 (false, false) => {
894 let result_var = self.fresh_var();
896 let result_ty = InferType::Var(result_var);
897 self.add_constraint(Constraint::equal(
898 then_info.ty,
899 result_ty.clone(),
900 then_info.span,
901 ));
902 self.add_constraint(Constraint::equal(
903 else_info.ty,
904 result_ty.clone(),
905 else_info.span,
906 ));
907 result_ty
908 }
909 }
910 } else {
911 InferType::Concrete(Type::UNIT)
914 }
915 }
916
917 InstData::Loop { cond, body } => {
919 let cond_info = self.generate(*cond, ctx);
920 self.add_constraint(Constraint::equal(
921 cond_info.ty,
922 InferType::Concrete(Type::BOOL),
923 cond_info.span,
924 ));
925
926 ctx.loop_depth += 1;
927 self.generate(*body, ctx);
928 ctx.loop_depth -= 1;
929
930 InferType::Concrete(Type::UNIT)
932 }
933
934 InstData::For {
936 binding,
937 is_mut,
938 iterable,
939 body,
940 } => {
941 let iterable_info = self.generate(*iterable, ctx);
943
944 let binding_ty = match &iterable_info.ty {
948 InferType::Array { element, .. } => *element.clone(),
949 other => other.clone(),
950 };
951
952 ctx.insert_local(
954 *binding,
955 LocalVarInfo {
956 ty: binding_ty,
957 is_mut: *is_mut,
958 span,
959 },
960 );
961
962 ctx.loop_depth += 1;
963 self.generate(*body, ctx);
964 ctx.loop_depth -= 1;
965
966 InferType::Concrete(Type::UNIT)
968 }
969
970 InstData::InfiniteLoop { body } => {
972 ctx.loop_depth += 1;
973 self.generate(*body, ctx);
974 ctx.loop_depth -= 1;
975
976 InferType::Concrete(Type::NEVER)
978 }
979
980 InstData::Break | InstData::Continue => InferType::Concrete(Type::NEVER),
982
983 InstData::Match {
985 scrutinee,
986 arms_start,
987 arms_len,
988 } => {
989 let scrutinee_info = self.generate(*scrutinee, ctx);
990 let arms = self.rir.get_match_arms(*arms_start, *arms_len);
991
992 let mut arm_types: Vec<ExprInfo> = Vec::new();
994 for (pattern, body) in arms.iter() {
995 let pattern_ty = self.pattern_type(pattern);
997 self.add_constraint(Constraint::equal(
998 scrutinee_info.ty.clone(),
999 pattern_ty,
1000 pattern.span(),
1001 ));
1002
1003 let bindings_to_remove = match pattern {
1006 gruel_rir::RirPattern::DataVariant {
1007 type_name,
1008 variant,
1009 bindings,
1010 ..
1011 } => {
1012 let mut added_bindings = Vec::new();
1013 if let Some(&enum_ty) = self.enums.get(type_name)
1014 && let Some(enum_id) = enum_ty.as_enum()
1015 {
1016 let enum_def = self.type_pool.enum_def(enum_id);
1017 let variant_name = self.interner.resolve(variant);
1018 if let Some(variant_idx) = enum_def.find_variant(variant_name) {
1019 let field_types = &enum_def.variants[variant_idx].fields;
1020 for (i, binding) in bindings.iter().enumerate() {
1021 if !binding.is_wildcard
1022 && let Some(name) = binding.name
1023 {
1024 let field_ty = if i < field_types.len() {
1025 InferType::Concrete(field_types[i])
1026 } else {
1027 InferType::Concrete(Type::ERROR)
1028 };
1029 let old = ctx.locals.insert(
1030 name,
1031 LocalVarInfo {
1032 ty: field_ty,
1033 is_mut: binding.is_mut,
1034 span: pattern.span(),
1035 },
1036 );
1037 added_bindings.push((name, old));
1038 }
1039 }
1040 }
1041 } else {
1042 for binding in bindings.iter() {
1046 if !binding.is_wildcard
1047 && let Some(name) = binding.name
1048 {
1049 let var = self.fresh_var();
1050 let old = ctx.locals.insert(
1051 name,
1052 LocalVarInfo {
1053 ty: InferType::Var(var),
1054 is_mut: binding.is_mut,
1055 span: pattern.span(),
1056 },
1057 );
1058 added_bindings.push((name, old));
1059 }
1060 }
1061 }
1062 added_bindings
1063 }
1064 gruel_rir::RirPattern::StructVariant {
1065 type_name,
1066 variant,
1067 field_bindings,
1068 ..
1069 } => {
1070 let mut added_bindings = Vec::new();
1071 if let Some(&enum_ty) = self.enums.get(type_name)
1072 && let Some(enum_id) = enum_ty.as_enum()
1073 {
1074 let enum_def = self.type_pool.enum_def(enum_id);
1075 let variant_name = self.interner.resolve(variant);
1076 if let Some(variant_idx) = enum_def.find_variant(variant_name) {
1077 let variant_def = &enum_def.variants[variant_idx];
1078 for fb in field_bindings {
1079 if !fb.binding.is_wildcard
1080 && let Some(name) = fb.binding.name
1081 {
1082 let field_name = self.interner.resolve(&fb.field_name);
1083 let field_ty = if let Some(idx) =
1084 variant_def.find_field(field_name)
1085 {
1086 InferType::Concrete(variant_def.fields[idx])
1087 } else {
1088 InferType::Concrete(Type::ERROR)
1089 };
1090 let old = ctx.locals.insert(
1091 name,
1092 LocalVarInfo {
1093 ty: field_ty,
1094 is_mut: fb.binding.is_mut,
1095 span: pattern.span(),
1096 },
1097 );
1098 added_bindings.push((name, old));
1099 }
1100 }
1101 }
1102 } else {
1103 for fb in field_bindings {
1106 if !fb.binding.is_wildcard
1107 && let Some(name) = fb.binding.name
1108 {
1109 let var = self.fresh_var();
1110 let old = ctx.locals.insert(
1111 name,
1112 LocalVarInfo {
1113 ty: InferType::Var(var),
1114 is_mut: fb.binding.is_mut,
1115 span: pattern.span(),
1116 },
1117 );
1118 added_bindings.push((name, old));
1119 }
1120 }
1121 }
1122 added_bindings
1123 }
1124 _ => Vec::new(),
1125 };
1126
1127 let body_info = self.generate(*body, ctx);
1129 arm_types.push(body_info);
1130
1131 for (name, old_val) in bindings_to_remove {
1133 match old_val {
1134 Some(prev) => {
1135 ctx.locals.insert(name, prev);
1136 }
1137 None => {
1138 ctx.locals.remove(&name);
1139 }
1140 }
1141 }
1142 }
1143
1144 let non_never_arms: Vec<_> = arm_types
1147 .iter()
1148 .filter(|info| !matches!(&info.ty, InferType::Concrete(Type::NEVER)))
1149 .collect();
1150
1151 if non_never_arms.is_empty() {
1152 InferType::Concrete(Type::NEVER)
1154 } else {
1155 let result_var = self.fresh_var();
1157 let result_ty = InferType::Var(result_var);
1158 for arm_info in non_never_arms {
1159 self.add_constraint(Constraint::equal(
1160 arm_info.ty.clone(),
1161 result_ty.clone(),
1162 arm_info.span,
1163 ));
1164 }
1165 result_ty
1166 }
1167 }
1168
1169 InstData::StructInit {
1171 type_name,
1172 fields_start,
1173 fields_len,
1174 ..
1175 } => {
1176 let struct_ty = self
1178 .type_subst
1179 .and_then(|subst| subst.get(type_name).copied())
1180 .or_else(|| self.structs.get(type_name).copied());
1181
1182 if let Some(struct_ty) = struct_ty {
1183 let fields = self.rir.get_field_inits(*fields_start, *fields_len);
1184 for (_, value_ref) in fields.iter() {
1186 self.generate(*value_ref, ctx);
1187 }
1188 InferType::Concrete(struct_ty)
1189 } else {
1190 InferType::Concrete(Type::ERROR)
1191 }
1192 }
1193
1194 InstData::FieldGet { base, field: _ } => {
1196 let _base_info = self.generate(*base, ctx);
1198 let result_var = self.fresh_var();
1202 InferType::Var(result_var)
1203 }
1204
1205 InstData::FieldSet {
1207 base,
1208 field: _,
1209 value,
1210 } => {
1211 self.generate(*base, ctx);
1212 self.generate(*value, ctx);
1213 InferType::Concrete(Type::UNIT)
1214 }
1215
1216 InstData::EnumVariant { type_name, .. } => {
1218 if let Some(&enum_ty) = self.enums.get(type_name) {
1219 InferType::Concrete(enum_ty)
1220 } else {
1221 let var = self.fresh_var();
1223 InferType::Var(var)
1224 }
1225 }
1226
1227 InstData::EnumStructVariant {
1229 type_name,
1230 fields_start,
1231 fields_len,
1232 ..
1233 } => {
1234 let fields = self.rir.get_field_inits(*fields_start, *fields_len);
1236 for (_, value_ref) in fields.iter() {
1237 self.generate(*value_ref, ctx);
1238 }
1239 if let Some(&enum_ty) = self.enums.get(type_name) {
1240 InferType::Concrete(enum_ty)
1241 } else {
1242 let var = self.fresh_var();
1244 InferType::Var(var)
1245 }
1246 }
1247
1248 InstData::ArrayInit {
1250 elems_start,
1251 elems_len,
1252 } => {
1253 let elements = self.rir.get_inst_refs(*elems_start, *elems_len);
1254 if elements.is_empty() {
1255 let elem_var = self.fresh_var();
1258 InferType::Array {
1259 element: Box::new(InferType::Var(elem_var)),
1260 length: 0,
1261 }
1262 } else {
1263 let first_info = self.generate(elements[0], ctx);
1265 for elem_ref in elements.iter().skip(1) {
1266 let elem_info = self.generate(*elem_ref, ctx);
1267 self.add_constraint(Constraint::equal(
1268 elem_info.ty,
1269 first_info.ty.clone(),
1270 elem_info.span,
1271 ));
1272 }
1273 InferType::Array {
1275 element: Box::new(first_info.ty),
1276 length: elements.len() as u64,
1277 }
1278 }
1279 }
1280
1281 InstData::IndexGet { base, index } => {
1283 let base_info = self.generate(*base, ctx);
1284 let index_info = self.generate(*index, ctx);
1285 self.add_constraint(Constraint::is_unsigned(index_info.ty, index_info.span));
1287
1288 match &base_info.ty {
1292 InferType::Array { element, .. } => (**element).clone(),
1293 _ => {
1294 let result_var = self.fresh_var();
1297 InferType::Var(result_var)
1298 }
1299 }
1300 }
1301
1302 InstData::IndexSet { base, index, value } => {
1304 let base_info = self.generate(*base, ctx);
1305 let index_info = self.generate(*index, ctx);
1306 self.add_constraint(Constraint::is_unsigned(index_info.ty, index_info.span));
1308
1309 let value_info = self.generate(*value, ctx);
1310
1311 if let InferType::Array { element, .. } = &base_info.ty {
1313 self.add_constraint(Constraint::equal(
1314 value_info.ty,
1315 (**element).clone(),
1316 value_info.span,
1317 ));
1318 }
1319
1320 InferType::Concrete(Type::UNIT)
1321 }
1322
1323 InstData::FnDecl { .. }
1325 | InstData::StructDecl { .. }
1326 | InstData::EnumDecl { .. }
1327 | InstData::DropFnDecl { .. }
1328 | InstData::ConstDecl { .. } => InferType::Concrete(Type::UNIT),
1329
1330 InstData::MethodCall {
1332 receiver,
1333 method,
1334 args_start,
1335 args_len,
1336 } => {
1337 let receiver_info = self.generate(*receiver, ctx);
1339 let args = self.rir.get_call_args(*args_start, *args_len);
1340
1341 if let InferType::Concrete(ty) = &receiver_info.ty {
1346 if let Some(struct_id) = ty.as_struct() {
1347 let method_key = (struct_id, *method);
1349 if let Some(method_sig) = self.methods.get(&method_key) {
1350 for (arg, param_type) in args.iter().zip(method_sig.param_types.iter())
1352 {
1353 let arg_info = self.generate(arg.value, ctx);
1354 self.add_constraint(Constraint::equal(
1355 arg_info.ty,
1356 param_type.clone(),
1357 arg_info.span,
1358 ));
1359 }
1360 method_sig.return_type.clone()
1361 } else {
1362 for arg in args.iter() {
1367 self.generate(arg.value, ctx);
1368 }
1369 InferType::Var(self.fresh_var())
1370 }
1371 } else if let Some(enum_id) = ty.as_enum() {
1372 let method_key = (enum_id, *method);
1374 if let Some(method_sig) = self.enum_methods.get(&method_key) {
1375 for (arg, param_type) in args.iter().zip(method_sig.param_types.iter())
1376 {
1377 let arg_info = self.generate(arg.value, ctx);
1378 self.add_constraint(Constraint::equal(
1379 arg_info.ty,
1380 param_type.clone(),
1381 arg_info.span,
1382 ));
1383 }
1384 method_sig.return_type.clone()
1385 } else {
1386 for arg in args.iter() {
1388 self.generate(arg.value, ctx);
1389 }
1390 InferType::Var(self.fresh_var())
1391 }
1392 } else {
1393 for arg in args.iter() {
1395 self.generate(arg.value, ctx);
1396 }
1397 InferType::Concrete(Type::ERROR)
1398 }
1399 } else {
1400 for arg in args.iter() {
1402 self.generate(arg.value, ctx);
1403 }
1404 InferType::Var(self.fresh_var())
1405 }
1406 }
1407
1408 InstData::AssocFnCall {
1410 type_name,
1411 function,
1412 args_start,
1413 args_len,
1414 } => {
1415 let args = self.rir.get_call_args(*args_start, *args_len);
1416 let struct_id = self.structs.get(type_name).and_then(|ty| ty.as_struct());
1418
1419 if let Some(struct_id) = struct_id {
1420 let method_key = (struct_id, *function);
1421 if let Some(method_sig) = self.methods.get(&method_key) {
1422 for (arg, param_type) in args.iter().zip(method_sig.param_types.iter()) {
1424 let arg_info = self.generate(arg.value, ctx);
1425 self.add_constraint(Constraint::equal(
1426 arg_info.ty,
1427 param_type.clone(),
1428 arg_info.span,
1429 ));
1430 }
1431 method_sig.return_type.clone()
1432 } else {
1433 for arg in args.iter() {
1436 self.generate(arg.value, ctx);
1437 }
1438 InferType::Var(self.fresh_var())
1439 }
1440 } else {
1441 for arg in args.iter() {
1444 self.generate(arg.value, ctx);
1445 }
1446 InferType::Var(self.fresh_var())
1447 }
1448 }
1449
1450 InstData::Comptime { expr: _ } => {
1455 let var = self.fresh_var();
1462 self.int_literal_vars.push(var);
1463 InferType::Var(var)
1464 }
1465
1466 InstData::ComptimeUnrollFor {
1473 binding,
1474 iterable,
1475 body,
1476 } => {
1477 self.generate(*iterable, ctx);
1479
1480 let binding_ty = {
1484 let var = self.fresh_var();
1485 InferType::Var(var)
1486 };
1487 ctx.insert_local(
1488 *binding,
1489 LocalVarInfo {
1490 ty: binding_ty,
1491 is_mut: false,
1492 span,
1493 },
1494 );
1495
1496 self.generate(*body, ctx);
1498
1499 InferType::Concrete(Type::UNIT)
1500 }
1501
1502 InstData::Checked { expr } => {
1505 let inner_info = self.generate(*expr, ctx);
1507 inner_info.ty
1508 }
1509
1510 InstData::TypeConst { .. } => InferType::Concrete(Type::COMPTIME_TYPE),
1513
1514 InstData::AnonStructType { .. } => InferType::Concrete(Type::COMPTIME_TYPE),
1517
1518 InstData::AnonEnumType { .. } => InferType::Concrete(Type::COMPTIME_TYPE),
1521 };
1522
1523 self.record_type(inst_ref, ty.clone());
1525 ExprInfo::new(ty, span)
1526 }
1527
1528 fn generate_binary_arith(
1532 &mut self,
1533 lhs: InstRef,
1534 rhs: InstRef,
1535 ctx: &mut ConstraintContext,
1536 ) -> InferType {
1537 let lhs_info = self.generate(lhs, ctx);
1538 let rhs_info = self.generate(rhs, ctx);
1539
1540 let result_var = self.fresh_var();
1541 let result_ty = InferType::Var(result_var);
1542
1543 self.add_constraint(Constraint::equal(
1544 lhs_info.ty,
1545 result_ty.clone(),
1546 lhs_info.span,
1547 ));
1548 self.add_constraint(Constraint::equal(
1549 rhs_info.ty,
1550 result_ty.clone(),
1551 rhs_info.span,
1552 ));
1553
1554 self.add_constraint(Constraint::is_numeric(result_ty.clone(), lhs_info.span));
1556
1557 result_ty
1558 }
1559
1560 fn generate_binary_bitwise(
1564 &mut self,
1565 lhs: InstRef,
1566 rhs: InstRef,
1567 ctx: &mut ConstraintContext,
1568 ) -> InferType {
1569 let lhs_info = self.generate(lhs, ctx);
1570 let rhs_info = self.generate(rhs, ctx);
1571
1572 let result_var = self.fresh_var();
1573 let result_ty = InferType::Var(result_var);
1574
1575 self.add_constraint(Constraint::equal(
1576 lhs_info.ty,
1577 result_ty.clone(),
1578 lhs_info.span,
1579 ));
1580 self.add_constraint(Constraint::equal(
1581 rhs_info.ty,
1582 result_ty.clone(),
1583 rhs_info.span,
1584 ));
1585
1586 self.add_constraint(Constraint::is_integer(result_ty.clone(), lhs_info.span));
1588
1589 result_ty
1590 }
1591
1592 fn pattern_type(&mut self, pattern: &gruel_rir::RirPattern) -> InferType {
1594 match pattern {
1595 gruel_rir::RirPattern::Wildcard(_) => {
1596 let var = self.fresh_var();
1598 InferType::Var(var)
1599 }
1600 gruel_rir::RirPattern::Int(_, _) => InferType::IntLiteral,
1601 gruel_rir::RirPattern::Bool(_, _) => InferType::Concrete(Type::BOOL),
1602 gruel_rir::RirPattern::Path { type_name, .. }
1603 | gruel_rir::RirPattern::DataVariant { type_name, .. }
1604 | gruel_rir::RirPattern::StructVariant { type_name, .. } => {
1605 if let Some(&enum_ty) = self.enums.get(type_name) {
1606 InferType::Concrete(enum_ty)
1607 } else {
1608 let var = self.fresh_var();
1612 InferType::Var(var)
1613 }
1614 }
1615 }
1616 }
1617
1618 fn resolve_type_name(&self, name: &str) -> Option<InferType> {
1623 if let Some((element_type_str, length)) = parse_array_type_syntax(name) {
1625 let element_ty = self.resolve_type_name(&element_type_str)?;
1627 return Some(InferType::Array {
1628 element: Box::new(element_ty),
1629 length,
1630 });
1631 }
1632
1633 if let Some((pointee_type_str, mutability)) = parse_pointer_type_syntax(name) {
1635 let pointee_infer_ty = self.resolve_type_name(&pointee_type_str)?;
1637
1638 let pointee_ty = match pointee_infer_ty {
1640 InferType::Concrete(ty) => ty,
1641 _ => return None,
1643 };
1644
1645 let ptr_ty = match mutability {
1647 PtrMutability::Mut => {
1648 let ptr_id = self.type_pool.intern_ptr_mut_from_type(pointee_ty);
1649 Type::new_ptr_mut(ptr_id)
1650 }
1651 PtrMutability::Const => {
1652 let ptr_id = self.type_pool.intern_ptr_const_from_type(pointee_ty);
1653 Type::new_ptr_const(ptr_id)
1654 }
1655 };
1656 return Some(InferType::Concrete(ptr_ty));
1657 }
1658
1659 let ty = match name {
1661 "i8" => Type::I8,
1662 "i16" => Type::I16,
1663 "i32" => Type::I32,
1664 "i64" => Type::I64,
1665 "u8" => Type::U8,
1666 "u16" => Type::U16,
1667 "u32" => Type::U32,
1668 "u64" => Type::U64,
1669 "isize" => Type::ISIZE,
1670 "usize" => Type::USIZE,
1671 "f16" => Type::F16,
1672 "f32" => Type::F32,
1673 "f64" => Type::F64,
1674 "bool" => Type::BOOL,
1675 "()" => Type::UNIT,
1676 _ => {
1677 if let Some(name_spur) = self.interner.get(name) {
1679 if let Some(&struct_ty) = self.structs.get(&name_spur) {
1680 return Some(InferType::Concrete(struct_ty));
1681 }
1682 if let Some(&enum_ty) = self.enums.get(&name_spur) {
1683 return Some(InferType::Concrete(enum_ty));
1684 }
1685 }
1686 return None;
1687 }
1688 };
1689 Some(InferType::Concrete(ty))
1690 }
1691}
1692
1693#[cfg(test)]
1694mod tests {
1695 use super::*;
1696 use lasso::ThreadedRodeo;
1697
1698 fn make_test_rir_interner_and_type_pool() -> (Rir, ThreadedRodeo, TypeInternPool) {
1700 let rir = Rir::new();
1701 let interner = ThreadedRodeo::new();
1702 let type_pool = TypeInternPool::new();
1703 (rir, interner, type_pool)
1704 }
1705
1706 #[test]
1707 fn test_constraint_generator_int_literal() {
1708 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
1709 let functions = HashMap::new();
1710 let structs = HashMap::new();
1711 let enums = HashMap::new();
1712 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
1713 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
1714
1715 let inst_ref = rir.add_inst(gruel_rir::Inst {
1717 data: InstData::IntConst(42),
1718 span: Span::new(0, 2),
1719 });
1720
1721 let mut cgen = ConstraintGenerator::new(
1722 &rir,
1723 &interner,
1724 &functions,
1725 &structs,
1726 &enums,
1727 &methods,
1728 &enum_methods,
1729 &type_pool,
1730 );
1731 let params = HashMap::new();
1732 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
1733
1734 let info = cgen.generate(inst_ref, &mut ctx);
1735
1736 assert!(matches!(info.ty, InferType::Var(_)));
1738 assert_eq!(cgen.int_literal_vars().len(), 1);
1740 assert_eq!(cgen.constraints().len(), 0);
1742 }
1743
1744 #[test]
1745 fn test_constraint_generator_bool_literal() {
1746 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
1747 let functions = HashMap::new();
1748 let structs = HashMap::new();
1749 let enums = HashMap::new();
1750 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
1751 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
1752
1753 let inst_ref = rir.add_inst(gruel_rir::Inst {
1754 data: InstData::BoolConst(true),
1755 span: Span::new(0, 4),
1756 });
1757
1758 let mut cgen = ConstraintGenerator::new(
1759 &rir,
1760 &interner,
1761 &functions,
1762 &structs,
1763 &enums,
1764 &methods,
1765 &enum_methods,
1766 &type_pool,
1767 );
1768 let params = HashMap::new();
1769 let mut ctx = ConstraintContext::new(¶ms, Type::BOOL);
1770
1771 let info = cgen.generate(inst_ref, &mut ctx);
1772
1773 assert_eq!(info.ty, InferType::Concrete(Type::BOOL));
1774 assert_eq!(cgen.constraints().len(), 0);
1775 }
1776
1777 #[test]
1778 fn test_constraint_generator_binary_add() {
1779 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
1780 let functions = HashMap::new();
1781 let structs = HashMap::new();
1782 let enums = HashMap::new();
1783 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
1784 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
1785
1786 let lhs = rir.add_inst(gruel_rir::Inst {
1788 data: InstData::IntConst(1),
1789 span: Span::new(0, 1),
1790 });
1791 let rhs = rir.add_inst(gruel_rir::Inst {
1792 data: InstData::IntConst(2),
1793 span: Span::new(4, 5),
1794 });
1795 let add = rir.add_inst(gruel_rir::Inst {
1796 data: InstData::Add { lhs, rhs },
1797 span: Span::new(0, 5),
1798 });
1799
1800 let mut cgen = ConstraintGenerator::new(
1801 &rir,
1802 &interner,
1803 &functions,
1804 &structs,
1805 &enums,
1806 &methods,
1807 &enum_methods,
1808 &type_pool,
1809 );
1810 let params = HashMap::new();
1811 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
1812
1813 let info = cgen.generate(add, &mut ctx);
1814
1815 assert!(info.ty.is_var());
1817 assert_eq!(cgen.constraints().len(), 3);
1819 match &cgen.constraints()[2] {
1821 Constraint::IsNumeric(_, _) => {}
1822 _ => panic!("Expected IsNumeric constraint for arithmetic result"),
1823 }
1824 }
1825
1826 #[test]
1827 fn test_constraint_generator_comparison() {
1828 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
1829 let functions = HashMap::new();
1830 let structs = HashMap::new();
1831 let enums = HashMap::new();
1832 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
1833 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
1834
1835 let lhs = rir.add_inst(gruel_rir::Inst {
1837 data: InstData::IntConst(1),
1838 span: Span::new(0, 1),
1839 });
1840 let rhs = rir.add_inst(gruel_rir::Inst {
1841 data: InstData::IntConst(2),
1842 span: Span::new(4, 5),
1843 });
1844 let lt = rir.add_inst(gruel_rir::Inst {
1845 data: InstData::Lt { lhs, rhs },
1846 span: Span::new(0, 5),
1847 });
1848
1849 let mut cgen = ConstraintGenerator::new(
1850 &rir,
1851 &interner,
1852 &functions,
1853 &structs,
1854 &enums,
1855 &methods,
1856 &enum_methods,
1857 &type_pool,
1858 );
1859 let params = HashMap::new();
1860 let mut ctx = ConstraintContext::new(¶ms, Type::BOOL);
1861
1862 let info = cgen.generate(lt, &mut ctx);
1863
1864 assert_eq!(info.ty, InferType::Concrete(Type::BOOL));
1866 assert_eq!(cgen.constraints().len(), 1);
1868 }
1869
1870 #[test]
1871 fn test_constraint_generator_logical_and() {
1872 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
1873 let functions = HashMap::new();
1874 let structs = HashMap::new();
1875 let enums = HashMap::new();
1876 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
1877 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
1878
1879 let lhs = rir.add_inst(gruel_rir::Inst {
1881 data: InstData::BoolConst(true),
1882 span: Span::new(0, 4),
1883 });
1884 let rhs = rir.add_inst(gruel_rir::Inst {
1885 data: InstData::BoolConst(false),
1886 span: Span::new(8, 13),
1887 });
1888 let and = rir.add_inst(gruel_rir::Inst {
1889 data: InstData::And { lhs, rhs },
1890 span: Span::new(0, 13),
1891 });
1892
1893 let mut cgen = ConstraintGenerator::new(
1894 &rir,
1895 &interner,
1896 &functions,
1897 &structs,
1898 &enums,
1899 &methods,
1900 &enum_methods,
1901 &type_pool,
1902 );
1903 let params = HashMap::new();
1904 let mut ctx = ConstraintContext::new(¶ms, Type::BOOL);
1905
1906 let info = cgen.generate(and, &mut ctx);
1907
1908 assert_eq!(info.ty, InferType::Concrete(Type::BOOL));
1910 assert_eq!(cgen.constraints().len(), 2);
1912 }
1913
1914 #[test]
1915 fn test_constraint_generator_negation() {
1916 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
1917 let functions = HashMap::new();
1918 let structs = HashMap::new();
1919 let enums = HashMap::new();
1920 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
1921 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
1922
1923 let operand = rir.add_inst(gruel_rir::Inst {
1925 data: InstData::IntConst(42),
1926 span: Span::new(1, 3),
1927 });
1928 let neg = rir.add_inst(gruel_rir::Inst {
1929 data: InstData::Neg { operand },
1930 span: Span::new(0, 3),
1931 });
1932
1933 let mut cgen = ConstraintGenerator::new(
1934 &rir,
1935 &interner,
1936 &functions,
1937 &structs,
1938 &enums,
1939 &methods,
1940 &enum_methods,
1941 &type_pool,
1942 );
1943 let params = HashMap::new();
1944 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
1945
1946 let info = cgen.generate(neg, &mut ctx);
1947
1948 assert!(matches!(info.ty, InferType::Var(_)));
1950 assert_eq!(cgen.constraints().len(), 1);
1952 match &cgen.constraints()[0] {
1954 Constraint::IsSigned(_, _) => {}
1955 _ => panic!("Expected IsSigned constraint"),
1956 }
1957 }
1958
1959 #[test]
1960 fn test_constraint_generator_return() {
1961 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
1962 let functions = HashMap::new();
1963 let structs = HashMap::new();
1964 let enums = HashMap::new();
1965 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
1966 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
1967
1968 let value = rir.add_inst(gruel_rir::Inst {
1970 data: InstData::IntConst(42),
1971 span: Span::new(7, 9),
1972 });
1973 let ret = rir.add_inst(gruel_rir::Inst {
1974 data: InstData::Ret(Some(value)),
1975 span: Span::new(0, 9),
1976 });
1977
1978 let mut cgen = ConstraintGenerator::new(
1979 &rir,
1980 &interner,
1981 &functions,
1982 &structs,
1983 &enums,
1984 &methods,
1985 &enum_methods,
1986 &type_pool,
1987 );
1988 let params = HashMap::new();
1989 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
1990
1991 let info = cgen.generate(ret, &mut ctx);
1992
1993 assert_eq!(info.ty, InferType::Concrete(Type::NEVER));
1995 assert_eq!(cgen.constraints().len(), 1);
1997 }
1998
1999 #[test]
2000 fn test_constraint_generator_if_else() {
2001 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2002 let functions = HashMap::new();
2003 let structs = HashMap::new();
2004 let enums = HashMap::new();
2005 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2006 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2007
2008 let cond = rir.add_inst(gruel_rir::Inst {
2010 data: InstData::BoolConst(true),
2011 span: Span::new(3, 7),
2012 });
2013 let then_val = rir.add_inst(gruel_rir::Inst {
2014 data: InstData::IntConst(1),
2015 span: Span::new(10, 11),
2016 });
2017 let else_val = rir.add_inst(gruel_rir::Inst {
2018 data: InstData::IntConst(2),
2019 span: Span::new(20, 21),
2020 });
2021 let branch = rir.add_inst(gruel_rir::Inst {
2022 data: InstData::Branch {
2023 cond,
2024 then_block: then_val,
2025 else_block: Some(else_val),
2026 },
2027 span: Span::new(0, 25),
2028 });
2029
2030 let mut cgen = ConstraintGenerator::new(
2031 &rir,
2032 &interner,
2033 &functions,
2034 &structs,
2035 &enums,
2036 &methods,
2037 &enum_methods,
2038 &type_pool,
2039 );
2040 let params = HashMap::new();
2041 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
2042
2043 let info = cgen.generate(branch, &mut ctx);
2044
2045 assert!(info.ty.is_var());
2047 assert_eq!(cgen.constraints().len(), 3);
2049 }
2050
2051 #[test]
2052 fn test_constraint_generator_while_loop() {
2053 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2054 let functions = HashMap::new();
2055 let structs = HashMap::new();
2056 let enums = HashMap::new();
2057 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2058 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2059
2060 let cond = rir.add_inst(gruel_rir::Inst {
2062 data: InstData::BoolConst(true),
2063 span: Span::new(6, 10),
2064 });
2065 let body = rir.add_inst(gruel_rir::Inst {
2066 data: InstData::IntConst(0),
2067 span: Span::new(13, 14),
2068 });
2069 let loop_inst = rir.add_inst(gruel_rir::Inst {
2070 data: InstData::Loop { cond, body },
2071 span: Span::new(0, 15),
2072 });
2073
2074 let mut cgen = ConstraintGenerator::new(
2075 &rir,
2076 &interner,
2077 &functions,
2078 &structs,
2079 &enums,
2080 &methods,
2081 &enum_methods,
2082 &type_pool,
2083 );
2084 let params = HashMap::new();
2085 let mut ctx = ConstraintContext::new(¶ms, Type::UNIT);
2086
2087 let info = cgen.generate(loop_inst, &mut ctx);
2088
2089 assert_eq!(info.ty, InferType::Concrete(Type::UNIT));
2091 assert_eq!(cgen.constraints().len(), 1);
2093 }
2094
2095 #[test]
2096 fn test_constraint_context_scope() {
2097 let params = HashMap::new();
2098 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
2099
2100 let interner = ThreadedRodeo::new();
2102 let sym = interner.get_or_intern("x");
2103 ctx.insert_local(
2104 sym,
2105 LocalVarInfo {
2106 ty: InferType::Concrete(Type::I32),
2107 is_mut: false,
2108 span: Span::new(0, 1),
2109 },
2110 );
2111
2112 assert!(ctx.locals.contains_key(&sym));
2113
2114 ctx.push_scope();
2116 ctx.insert_local(
2117 sym,
2118 LocalVarInfo {
2119 ty: InferType::Concrete(Type::I64),
2120 is_mut: true,
2121 span: Span::new(10, 15),
2122 },
2123 );
2124
2125 let local = ctx.locals.get(&sym).unwrap();
2127 assert_eq!(local.ty, InferType::Concrete(Type::I64));
2128 assert!(local.is_mut);
2129
2130 ctx.pop_scope();
2132 let local = ctx.locals.get(&sym).unwrap();
2133 assert_eq!(local.ty, InferType::Concrete(Type::I32));
2134 assert!(!local.is_mut);
2135 }
2136
2137 #[test]
2138 fn test_expr_info_creation() {
2139 let info = ExprInfo::new(InferType::IntLiteral, Span::new(5, 10));
2140 assert!(info.ty.is_int_literal());
2141 assert_eq!(info.span, Span::new(5, 10));
2142 }
2143
2144 fn make_test_func_sig(param_types: Vec<InferType>, return_type: InferType) -> FunctionSig {
2146 let num_params = param_types.len();
2147 FunctionSig {
2148 param_types,
2149 return_type,
2150 is_generic: false,
2151 param_modes: vec![gruel_rir::RirParamMode::Normal; num_params],
2152 param_comptime: vec![false; num_params],
2153 param_names: vec![],
2154 return_type_sym: lasso::Spur::default(),
2155 }
2156 }
2157
2158 #[test]
2159 fn test_function_sig() {
2160 let sig = make_test_func_sig(
2161 vec![
2162 InferType::Concrete(Type::I32),
2163 InferType::Concrete(Type::BOOL),
2164 ],
2165 InferType::Concrete(Type::I64),
2166 );
2167 assert_eq!(sig.param_types.len(), 2);
2168 assert_eq!(sig.return_type, InferType::Concrete(Type::I64));
2169 }
2170
2171 #[test]
2172 fn test_constraint_generator_infinite_loop() {
2173 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2174 let functions = HashMap::new();
2175 let structs = HashMap::new();
2176 let enums = HashMap::new();
2177 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2178 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2179
2180 let body = rir.add_inst(gruel_rir::Inst {
2182 data: InstData::IntConst(0),
2183 span: Span::new(6, 7),
2184 });
2185 let loop_inst = rir.add_inst(gruel_rir::Inst {
2186 data: InstData::InfiniteLoop { body },
2187 span: Span::new(0, 10),
2188 });
2189
2190 let mut cgen = ConstraintGenerator::new(
2191 &rir,
2192 &interner,
2193 &functions,
2194 &structs,
2195 &enums,
2196 &methods,
2197 &enum_methods,
2198 &type_pool,
2199 );
2200 let params = HashMap::new();
2201 let mut ctx = ConstraintContext::new(¶ms, Type::UNIT);
2202
2203 let info = cgen.generate(loop_inst, &mut ctx);
2204
2205 assert_eq!(info.ty, InferType::Concrete(Type::NEVER));
2207 assert_eq!(cgen.constraints().len(), 0);
2209 }
2210
2211 #[test]
2212 fn test_constraint_generator_break_continue() {
2213 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2214 let functions = HashMap::new();
2215 let structs = HashMap::new();
2216 let enums = HashMap::new();
2217 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2218 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2219
2220 let break_inst = rir.add_inst(gruel_rir::Inst {
2221 data: InstData::Break,
2222 span: Span::new(0, 5),
2223 });
2224
2225 let mut cgen = ConstraintGenerator::new(
2226 &rir,
2227 &interner,
2228 &functions,
2229 &structs,
2230 &enums,
2231 &methods,
2232 &enum_methods,
2233 &type_pool,
2234 );
2235 let params = HashMap::new();
2236 let mut ctx = ConstraintContext::new(¶ms, Type::UNIT);
2237
2238 let info = cgen.generate(break_inst, &mut ctx);
2239
2240 assert_eq!(info.ty, InferType::Concrete(Type::NEVER));
2242 assert_eq!(cgen.constraints().len(), 0);
2243 }
2244
2245 #[test]
2246 fn test_constraint_generator_index_get() {
2247 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2248 let functions = HashMap::new();
2249 let structs = HashMap::new();
2250 let enums = HashMap::new();
2251 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2252 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2253
2254 let base = rir.add_inst(gruel_rir::Inst {
2256 data: InstData::IntConst(0), span: Span::new(0, 3),
2258 });
2259 let index = rir.add_inst(gruel_rir::Inst {
2260 data: InstData::IntConst(0),
2261 span: Span::new(4, 5),
2262 });
2263 let index_get = rir.add_inst(gruel_rir::Inst {
2264 data: InstData::IndexGet { base, index },
2265 span: Span::new(0, 6),
2266 });
2267
2268 let mut cgen = ConstraintGenerator::new(
2269 &rir,
2270 &interner,
2271 &functions,
2272 &structs,
2273 &enums,
2274 &methods,
2275 &enum_methods,
2276 &type_pool,
2277 );
2278 let params = HashMap::new();
2279 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
2280
2281 let info = cgen.generate(index_get, &mut ctx);
2282
2283 assert!(info.ty.is_var());
2285 assert_eq!(cgen.constraints().len(), 1);
2287 match &cgen.constraints()[0] {
2288 Constraint::IsUnsigned(_, _) => {}
2289 _ => panic!("Expected IsUnsigned constraint for index"),
2290 }
2291 }
2292
2293 #[test]
2294 fn test_constraint_generator_index_set() {
2295 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2296 let functions = HashMap::new();
2297 let structs = HashMap::new();
2298 let enums = HashMap::new();
2299 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2300 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2301
2302 let base = rir.add_inst(gruel_rir::Inst {
2304 data: InstData::IntConst(0), span: Span::new(0, 3),
2306 });
2307 let index = rir.add_inst(gruel_rir::Inst {
2308 data: InstData::IntConst(0),
2309 span: Span::new(4, 5),
2310 });
2311 let value = rir.add_inst(gruel_rir::Inst {
2312 data: InstData::IntConst(42),
2313 span: Span::new(9, 11),
2314 });
2315 let index_set = rir.add_inst(gruel_rir::Inst {
2316 data: InstData::IndexSet { base, index, value },
2317 span: Span::new(0, 11),
2318 });
2319
2320 let mut cgen = ConstraintGenerator::new(
2321 &rir,
2322 &interner,
2323 &functions,
2324 &structs,
2325 &enums,
2326 &methods,
2327 &enum_methods,
2328 &type_pool,
2329 );
2330 let params = HashMap::new();
2331 let mut ctx = ConstraintContext::new(¶ms, Type::UNIT);
2332
2333 let info = cgen.generate(index_set, &mut ctx);
2334
2335 assert_eq!(info.ty, InferType::Concrete(Type::UNIT));
2337 assert_eq!(cgen.constraints().len(), 1);
2339 match &cgen.constraints()[0] {
2340 Constraint::IsUnsigned(_, _) => {}
2341 _ => panic!("Expected IsUnsigned constraint for index"),
2342 }
2343 }
2344
2345 #[test]
2346 fn test_constraint_generator_empty_block() {
2347 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2348 let functions = HashMap::new();
2349 let structs = HashMap::new();
2350 let enums = HashMap::new();
2351 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2352 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2353
2354 let block = rir.add_inst(gruel_rir::Inst {
2356 data: InstData::Block {
2357 extra_start: 0,
2358 len: 0,
2359 },
2360 span: Span::new(0, 2),
2361 });
2362
2363 let mut cgen = ConstraintGenerator::new(
2364 &rir,
2365 &interner,
2366 &functions,
2367 &structs,
2368 &enums,
2369 &methods,
2370 &enum_methods,
2371 &type_pool,
2372 );
2373 let params = HashMap::new();
2374 let mut ctx = ConstraintContext::new(¶ms, Type::UNIT);
2375
2376 let info = cgen.generate(block, &mut ctx);
2377
2378 assert_eq!(info.ty, InferType::Concrete(Type::UNIT));
2380 assert_eq!(cgen.constraints().len(), 0);
2381 }
2382
2383 #[test]
2384 fn test_constraint_generator_bitwise_not() {
2385 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2386 let functions = HashMap::new();
2387 let structs = HashMap::new();
2388 let enums = HashMap::new();
2389 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2390 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2391
2392 let operand = rir.add_inst(gruel_rir::Inst {
2394 data: InstData::IntConst(42),
2395 span: Span::new(1, 3),
2396 });
2397 let bitnot = rir.add_inst(gruel_rir::Inst {
2398 data: InstData::BitNot { operand },
2399 span: Span::new(0, 3),
2400 });
2401
2402 let mut cgen = ConstraintGenerator::new(
2403 &rir,
2404 &interner,
2405 &functions,
2406 &structs,
2407 &enums,
2408 &methods,
2409 &enum_methods,
2410 &type_pool,
2411 );
2412 let params = HashMap::new();
2413 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
2414
2415 let info = cgen.generate(bitnot, &mut ctx);
2416
2417 assert!(matches!(info.ty, InferType::Var(_)));
2419 assert_eq!(cgen.constraints().len(), 1);
2421 match &cgen.constraints()[0] {
2422 Constraint::IsInteger(_, _) => {}
2423 _ => panic!("Expected IsInteger constraint"),
2424 }
2425 }
2426
2427 #[test]
2428 fn test_constraint_generator_function_call_arg_count_mismatch() {
2429 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2430 let mut functions = HashMap::new();
2431 let structs = HashMap::new();
2432 let enums = HashMap::new();
2433 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2434 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2435
2436 let func_name = interner.get_or_intern("foo");
2438 functions.insert(
2439 func_name,
2440 make_test_func_sig(
2441 vec![
2442 InferType::Concrete(Type::I32),
2443 InferType::Concrete(Type::I32),
2444 ],
2445 InferType::Concrete(Type::BOOL),
2446 ),
2447 );
2448
2449 let arg = rir.add_inst(gruel_rir::Inst {
2451 data: InstData::IntConst(42),
2452 span: Span::new(4, 6),
2453 });
2454 let (args_start, args_len) = rir.add_call_args(&[gruel_rir::RirCallArg {
2455 value: arg,
2456 mode: gruel_rir::RirArgMode::Normal,
2457 }]);
2458 let call = rir.add_inst(gruel_rir::Inst {
2459 data: InstData::Call {
2460 name: func_name,
2461 args_start,
2462 args_len,
2463 },
2464 span: Span::new(0, 7),
2465 });
2466
2467 let mut cgen = ConstraintGenerator::new(
2468 &rir,
2469 &interner,
2470 &functions,
2471 &structs,
2472 &enums,
2473 &methods,
2474 &enum_methods,
2475 &type_pool,
2476 );
2477 let params = HashMap::new();
2478 let mut ctx = ConstraintContext::new(¶ms, Type::BOOL);
2479
2480 let info = cgen.generate(call, &mut ctx);
2481
2482 assert_eq!(info.ty, InferType::Concrete(Type::BOOL));
2484 assert_eq!(cgen.constraints().len(), 0);
2486 }
2487
2488 #[test]
2489 fn test_constraint_generator_unknown_function() {
2490 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2491 let functions = HashMap::new(); let structs = HashMap::new();
2493 let enums = HashMap::new();
2494 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2495 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2496
2497 let unknown_func = interner.get_or_intern("unknown");
2499 let arg = rir.add_inst(gruel_rir::Inst {
2500 data: InstData::IntConst(42),
2501 span: Span::new(8, 10),
2502 });
2503 let (args_start, args_len) = rir.add_call_args(&[gruel_rir::RirCallArg {
2504 value: arg,
2505 mode: gruel_rir::RirArgMode::Normal,
2506 }]);
2507 let call = rir.add_inst(gruel_rir::Inst {
2508 data: InstData::Call {
2509 name: unknown_func,
2510 args_start,
2511 args_len,
2512 },
2513 span: Span::new(0, 11),
2514 });
2515
2516 let mut cgen = ConstraintGenerator::new(
2517 &rir,
2518 &interner,
2519 &functions,
2520 &structs,
2521 &enums,
2522 &methods,
2523 &enum_methods,
2524 &type_pool,
2525 );
2526 let params = HashMap::new();
2527 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
2528
2529 let info = cgen.generate(call, &mut ctx);
2530
2531 assert_eq!(info.ty, InferType::Concrete(Type::ERROR));
2533 assert_eq!(cgen.constraints().len(), 0);
2535 }
2536
2537 #[test]
2538 fn test_constraint_generator_match_multiple_arms() {
2539 let (mut rir, interner, type_pool) = make_test_rir_interner_and_type_pool();
2540 let functions = HashMap::new();
2541 let structs = HashMap::new();
2542 let enums = HashMap::new();
2543 let methods: HashMap<(StructId, Spur), MethodSig> = HashMap::new();
2544 let enum_methods: HashMap<(EnumId, Spur), MethodSig> = HashMap::new();
2545
2546 let scrutinee = rir.add_inst(gruel_rir::Inst {
2548 data: InstData::IntConst(5),
2549 span: Span::new(6, 7),
2550 });
2551
2552 let body1 = rir.add_inst(gruel_rir::Inst {
2554 data: InstData::IntConst(10),
2555 span: Span::new(15, 17),
2556 });
2557 let pattern1 = gruel_rir::RirPattern::Int(1, Span::new(10, 11));
2558
2559 let body2 = rir.add_inst(gruel_rir::Inst {
2561 data: InstData::IntConst(20),
2562 span: Span::new(25, 27),
2563 });
2564 let pattern2 = gruel_rir::RirPattern::Int(2, Span::new(20, 21));
2565
2566 let body3 = rir.add_inst(gruel_rir::Inst {
2568 data: InstData::IntConst(30),
2569 span: Span::new(35, 37),
2570 });
2571 let pattern3 = gruel_rir::RirPattern::Wildcard(Span::new(30, 31));
2572
2573 let arms = vec![(pattern1, body1), (pattern2, body2), (pattern3, body3)];
2574 let (arms_start, arms_len) = rir.add_match_arms(&arms);
2575 let match_inst = rir.add_inst(gruel_rir::Inst {
2576 data: InstData::Match {
2577 scrutinee,
2578 arms_start,
2579 arms_len,
2580 },
2581 span: Span::new(0, 40),
2582 });
2583
2584 let mut cgen = ConstraintGenerator::new(
2585 &rir,
2586 &interner,
2587 &functions,
2588 &structs,
2589 &enums,
2590 &methods,
2591 &enum_methods,
2592 &type_pool,
2593 );
2594 let params = HashMap::new();
2595 let mut ctx = ConstraintContext::new(¶ms, Type::I32);
2596
2597 let info = cgen.generate(match_inst, &mut ctx);
2598
2599 assert!(info.ty.is_var());
2601
2602 assert_eq!(cgen.constraints().len(), 6);
2606
2607 for constraint in cgen.constraints() {
2609 match constraint {
2610 Constraint::Equal(_, _, _) => {}
2611 _ => panic!("Expected Equal constraint in match"),
2612 }
2613 }
2614 }
2615}