1use std::collections::{HashMap, HashSet};
18
19use gruel_builtins::BuiltinTypeDef;
20use gruel_error::{
21 CompileError, CompileErrors, CompileResult, CompileWarning, ErrorKind,
22 IntrinsicTypeMismatchError, MultiErrorResult, OptionExt, PreviewFeature, WarningKind,
23};
24use gruel_rir::{
25 InstData, InstRef, RirArgMode, RirCallArg, RirDirective, RirParamMode, RirPattern,
26};
27use gruel_span::Span;
28use gruel_target::{Arch, Os};
29use lasso::Spur;
30use tracing::info_span;
31
32use super::context::{
33 AnalysisContext, AnalysisResult, BuiltinMethodContext, ComptimeHeapItem, ConstValue, ParamInfo,
34 ReceiverInfo, StringReceiverStorage,
35};
36use super::{AnalyzedFunction, InferenceContext, MethodInfo, Sema, SemaOutput};
37use crate::inference::{
38 Constraint, ConstraintContext, ConstraintGenerator, ParamVarInfo, Unifier, UnifyResult,
39};
40use crate::inst::{
41 Air, AirArgMode, AirCallArg, AirInst, AirInstData, AirPlaceBase, AirProjection, AirRef,
42};
43use crate::types::{EnumId, EnumVariantDef, StructField, StructId, Type, TypeKind};
44
45struct MethodBodySpec<'a> {
47 return_type: Spur,
48 params: &'a [gruel_rir::RirParam],
49 body: InstRef,
50 self_type: Option<Type>,
52}
53
54type AnalyzedFnResult = CompileResult<(
57 AnalyzedFunction,
58 Vec<CompileWarning>,
59 Vec<String>,
60 HashSet<Spur>,
61 HashSet<(StructId, Spur)>,
62)>;
63
64type RawFnAnalysis = CompileResult<(
67 Air,
68 u32,
69 u32,
70 Vec<bool>,
71 Vec<Type>,
72 Vec<CompileWarning>,
73 Vec<String>,
74 HashSet<Spur>,
75 HashSet<(StructId, Spur)>,
76)>;
77
78struct AnonStructSpec {
80 struct_id: StructId,
81 struct_type: crate::types::Type,
82 methods_start: u32,
83 methods_len: u32,
84}
85
86pub(crate) fn analyze_all_function_bodies(mut sema: Sema<'_>) -> MultiErrorResult<SemaOutput> {
101 if sema.has_imports() {
104 analyze_function_bodies_lazy(&mut sema)
105 } else {
106 analyze_all_function_bodies_sequential(&mut sema)
108 }
109}
110
111fn analyze_all_function_bodies_sequential(sema: &mut Sema<'_>) -> MultiErrorResult<SemaOutput> {
113 let infer_ctx = sema.build_inference_context();
115
116 let mut functions_with_strings: Vec<(AnalyzedFunction, Vec<String>)> = Vec::new();
118 let mut errors = CompileErrors::new();
119 let mut all_warnings = Vec::new();
120
121 let mut method_refs: HashSet<InstRef> = HashSet::new();
123 for (_, inst) in sema.rir.iter() {
124 match &inst.data {
125 InstData::StructDecl {
126 methods_start,
127 methods_len,
128 ..
129 } => {
130 let methods = sema.rir.get_inst_refs(*methods_start, *methods_len);
131 for method_ref in methods {
132 method_refs.insert(method_ref);
133 }
134 }
135 InstData::AnonStructType {
137 methods_start,
138 methods_len,
139 ..
140 }
141 | InstData::AnonEnumType {
142 methods_start,
143 methods_len,
144 ..
145 } => {
146 if *methods_len > 0 {
147 let methods = sema.rir.get_inst_refs(*methods_start, *methods_len);
148 for method_ref in methods {
149 method_refs.insert(method_ref);
150 }
151 }
152 }
153 _ => {}
154 }
155 }
156
157 for (inst_ref, inst) in sema.rir.iter() {
159 if let InstData::FnDecl {
160 name,
161 params_start,
162 params_len,
163 return_type,
164 body,
165 has_self,
166 ..
167 } = &inst.data
168 {
169 if method_refs.contains(&inst_ref) {
170 continue;
171 }
172
173 if *has_self {
177 continue;
178 }
179
180 if !sema.functions.contains_key(name) {
183 continue;
184 }
185
186 if let Some(fn_info) = sema.functions.get(name)
188 && fn_info.is_generic
189 {
190 continue;
191 }
192
193 let fn_name = sema.interner.resolve(name).to_string();
194 let params = sema.rir.get_params(*params_start, *params_len);
195
196 match sema.analyze_single_function(
197 &infer_ctx,
198 &fn_name,
199 *return_type,
200 ¶ms,
201 *body,
202 inst.span,
203 ) {
204 Ok((analyzed, warnings, local_strings, _ref_fns, _ref_meths)) => {
205 functions_with_strings.push((analyzed, local_strings));
206 all_warnings.extend(warnings);
207 }
208 Err(e) => errors.push(e),
209 }
210 }
211 }
212
213 for (_, inst) in sema.rir.iter() {
215 if let InstData::StructDecl {
216 name: type_name,
217 methods_start,
218 methods_len,
219 ..
220 } = &inst.data
221 {
222 let type_name_str = sema.interner.resolve(type_name).to_string();
223 let struct_id = match sema.structs.get(type_name) {
224 Some(id) => *id,
225 None => {
226 errors.push(CompileError::new(
227 ErrorKind::InternalError(format!(
228 "struct '{}' not found in struct map during method analysis",
229 type_name_str
230 )),
231 inst.span,
232 ));
233 continue;
234 }
235 };
236 let struct_type = Type::new_struct(struct_id);
237
238 let methods = sema.rir.get_inst_refs(*methods_start, *methods_len);
239 for method_ref in methods {
240 let method_inst = sema.rir.get(method_ref);
241 if let InstData::FnDecl {
242 name: method_name,
243 params_start,
244 params_len,
245 return_type,
246 body,
247 has_self,
248 ..
249 } = &method_inst.data
250 {
251 let method_name_str = sema.interner.resolve(method_name).to_string();
252 let params = sema.rir.get_params(*params_start, *params_len);
253
254 let full_name = if *has_self {
255 format!("{}.{}", type_name_str, method_name_str)
256 } else {
257 format!("{}::{}", type_name_str, method_name_str)
258 };
259
260 match sema.analyze_method_function(
261 &infer_ctx,
262 &full_name,
263 MethodBodySpec {
264 return_type: *return_type,
265 params: ¶ms,
266 body: *body,
267 self_type: has_self.then_some(struct_type),
268 },
269 method_inst.span,
270 ) {
271 Ok((analyzed, warnings, local_strings, _ref_fns, _ref_meths)) => {
272 functions_with_strings.push((analyzed, local_strings));
273 all_warnings.extend(warnings);
274 }
275 Err(e) => errors.push(e),
276 }
277 }
278 }
279 }
280 }
281
282 for (_, inst) in sema.rir.iter() {
284 if let InstData::DropFnDecl { type_name, body } = &inst.data {
285 let type_name_str = sema.interner.resolve(type_name).to_string();
286 let struct_id = match sema.structs.get(type_name) {
287 Some(id) => *id,
288 None => {
289 errors.push(CompileError::new(
290 ErrorKind::InternalError(format!(
291 "destructor for undefined type '{}' survived validation",
292 type_name_str
293 )),
294 inst.span,
295 ));
296 continue;
297 }
298 };
299 let struct_type = Type::new_struct(struct_id);
300 let full_name = format!("{}.__drop", type_name_str);
301
302 match sema.analyze_destructor_function(
303 &infer_ctx,
304 &full_name,
305 *body,
306 inst.span,
307 struct_type,
308 ) {
309 Ok((analyzed, warnings, local_strings, _ref_fns, _ref_meths)) => {
310 functions_with_strings.push((analyzed, local_strings));
311 all_warnings.extend(warnings);
312 }
313 Err(e) => errors.push(e),
314 }
315 }
316 }
317
318 let mut analyzed_anon_methods: HashSet<(StructId, Spur)> = HashSet::new();
323 let mut analyzed_anon_enum_methods: HashSet<(EnumId, Spur)> = HashSet::new();
324 loop {
325 let pending_anon_methods: Vec<(StructId, Spur, MethodInfo)> = sema
327 .methods
328 .iter()
329 .filter_map(|((struct_id, method_name), method_info)| {
330 let struct_def = sema.type_pool.struct_def(*struct_id);
332 if struct_def.name.starts_with("__anon_struct_")
333 && !analyzed_anon_methods.contains(&(*struct_id, *method_name))
334 {
335 Some((*struct_id, *method_name, *method_info))
336 } else {
337 None
338 }
339 })
340 .collect();
341
342 let pending_anon_enum_methods: Vec<(EnumId, Spur, MethodInfo)> = sema
344 .enum_methods
345 .iter()
346 .filter_map(|((enum_id, method_name), method_info)| {
347 let enum_def = sema.type_pool.enum_def(*enum_id);
348 if enum_def.name.starts_with("__anon_enum_")
349 && !analyzed_anon_enum_methods.contains(&(*enum_id, *method_name))
350 {
351 Some((*enum_id, *method_name, *method_info))
352 } else {
353 None
354 }
355 })
356 .collect();
357
358 if pending_anon_methods.is_empty() && pending_anon_enum_methods.is_empty() {
359 break;
360 }
361
362 for (struct_id, method_name, method_info) in pending_anon_methods {
363 analyzed_anon_methods.insert((struct_id, method_name));
364
365 let struct_def = sema.type_pool.struct_def(struct_id);
366 let type_name_str = struct_def.name.clone();
367 let method_name_str = sema.interner.resolve(&method_name).to_string();
368
369 let full_name = if method_info.has_self {
370 format!("{}.{}", type_name_str, method_name_str)
371 } else {
372 format!("{}::{}", type_name_str, method_name_str)
373 };
374
375 let param_names = sema.param_arena.names(method_info.params);
377 let param_types = sema.param_arena.types(method_info.params);
378 let param_modes = sema.param_arena.modes(method_info.params);
379
380 let mut param_info: Vec<(Spur, Type, RirParamMode)> = Vec::new();
381
382 if method_info.has_self {
383 let self_sym = sema.interner.get_or_intern("self");
385 param_info.push((self_sym, method_info.struct_type, RirParamMode::Normal));
386 }
387
388 for i in 0..param_names.len() {
390 param_info.push((param_names[i], param_types[i], param_modes[i]));
391 }
392
393 let struct_id = method_info
396 .struct_type
397 .as_struct()
398 .expect("method must belong to struct");
399 let captured_values = sema
400 .anon_struct_captured_values
401 .get(&struct_id)
402 .cloned()
403 .unwrap_or_else(HashMap::new);
404
405 match sema.analyze_method_body(
406 &infer_ctx,
407 method_info.return_type,
408 ¶m_info,
409 method_info.body,
410 method_info.struct_type,
411 &captured_values,
412 ) {
413 Ok((
414 air,
415 num_locals,
416 num_param_slots,
417 param_modes_result,
418 param_slot_types,
419 warnings,
420 local_strings,
421 _ref_fns,
422 _ref_meths,
423 )) => {
424 let analyzed = AnalyzedFunction {
425 name: full_name,
426 air,
427 num_locals,
428 num_param_slots,
429 param_modes: param_modes_result,
430 param_slot_types,
431 is_destructor: false,
432 };
433 functions_with_strings.push((analyzed, local_strings));
434 all_warnings.extend(warnings);
435 }
436 Err(e) => errors.push(e),
437 }
438 }
439
440 for (enum_id, method_name, method_info) in pending_anon_enum_methods {
442 analyzed_anon_enum_methods.insert((enum_id, method_name));
443
444 let enum_def = sema.type_pool.enum_def(enum_id);
445 let type_name_str = enum_def.name.clone();
446 let method_name_str = sema.interner.resolve(&method_name).to_string();
447
448 let full_name = if method_info.has_self {
449 format!("{}.{}", type_name_str, method_name_str)
450 } else {
451 format!("{}::{}", type_name_str, method_name_str)
452 };
453
454 let param_names = sema.param_arena.names(method_info.params);
455 let param_types = sema.param_arena.types(method_info.params);
456 let param_modes = sema.param_arena.modes(method_info.params);
457
458 let mut param_info: Vec<(Spur, Type, RirParamMode)> = Vec::new();
459
460 if method_info.has_self {
461 let self_sym = sema.interner.get_or_intern("self");
462 param_info.push((self_sym, method_info.struct_type, RirParamMode::Normal));
463 }
464
465 for i in 0..param_names.len() {
466 param_info.push((param_names[i], param_types[i], param_modes[i]));
467 }
468
469 let captured_values = sema
470 .anon_enum_captured_values
471 .get(&enum_id)
472 .cloned()
473 .unwrap_or_else(HashMap::new);
474
475 match sema.analyze_method_body(
476 &infer_ctx,
477 method_info.return_type,
478 ¶m_info,
479 method_info.body,
480 method_info.struct_type,
481 &captured_values,
482 ) {
483 Ok((
484 air,
485 num_locals,
486 num_param_slots,
487 param_modes_result,
488 param_slot_types,
489 warnings,
490 local_strings,
491 _ref_fns,
492 _ref_meths,
493 )) => {
494 let analyzed = AnalyzedFunction {
495 name: full_name,
496 air,
497 num_locals,
498 num_param_slots,
499 param_modes: param_modes_result,
500 param_slot_types,
501 is_destructor: false,
502 };
503 functions_with_strings.push((analyzed, local_strings));
504 all_warnings.extend(warnings);
505 }
506 Err(e) => errors.push(e),
507 }
508 }
509 }
510
511 let mut global_string_table: HashMap<String, u32> = HashMap::new();
513 let mut global_strings: Vec<String> = Vec::new();
514
515 let mut functions: Vec<AnalyzedFunction> = Vec::new();
516 for (mut analyzed, local_strings) in functions_with_strings {
517 if !local_strings.is_empty() {
518 let local_to_global: Vec<u32> = local_strings
519 .into_iter()
520 .map(|s| {
521 *global_string_table.entry(s.clone()).or_insert_with(|| {
522 let id = global_strings.len() as u32;
523 global_strings.push(s);
524 id
525 })
526 })
527 .collect();
528
529 analyzed
530 .air
531 .remap_string_ids(|local_id| local_to_global[local_id as usize]);
532 }
533 functions.push(analyzed);
534 }
535
536 for (msg, span) in std::mem::take(&mut sema.comptime_log_output) {
538 all_warnings.push(CompileWarning::new(
539 WarningKind::ComptimeDbgPresent(msg),
540 span,
541 ));
542 }
543
544 all_warnings.sort_by_key(|w| w.span().map(|s| s.start));
545
546 let mut output = SemaOutput {
547 functions,
548 strings: global_strings,
549 warnings: all_warnings,
550 type_pool: sema.type_pool.clone(),
551 comptime_dbg_output: std::mem::take(&mut sema.comptime_dbg_output),
552 };
553
554 if let Err(e) = crate::specialize::specialize(&mut output, sema, &infer_ctx, sema.interner) {
557 errors.push(e);
558 }
559
560 errors.into_result_with(output)
561}
562
563fn analyze_function_bodies_lazy(sema: &mut Sema<'_>) -> MultiErrorResult<SemaOutput> {
571 let infer_ctx = sema.build_inference_context();
573
574 let main_sym = match sema.interner.get("main") {
576 Some(sym) if sema.functions.contains_key(&sym) => sym,
577 _ => {
578 return Err(CompileErrors::from(CompileError::without_span(
580 ErrorKind::NoMainFunction,
581 )));
582 }
583 };
584
585 let mut pending_functions: Vec<Spur> = vec![main_sym];
588 let mut analyzed_functions: HashSet<Spur> = HashSet::new();
589 let mut pending_methods: Vec<(StructId, Spur)> = Vec::new();
590 let mut analyzed_methods: HashSet<(StructId, Spur)> = HashSet::new();
591
592 let mut functions_with_strings: Vec<(AnalyzedFunction, Vec<String>)> = Vec::new();
594 let mut errors = CompileErrors::new();
595 let mut all_warnings = Vec::new();
596
597 let mut method_refs: HashSet<InstRef> = HashSet::new();
599 for (_, inst) in sema.rir.iter() {
600 if let InstData::StructDecl {
601 methods_start,
602 methods_len,
603 ..
604 } = &inst.data
605 {
606 let methods = sema.rir.get_inst_refs(*methods_start, *methods_len);
607 for method_ref in methods {
608 method_refs.insert(method_ref);
609 }
610 }
611 }
612
613 while !pending_functions.is_empty() || !pending_methods.is_empty() {
615 while let Some(fn_name) = pending_functions.pop() {
617 if analyzed_functions.contains(&fn_name) {
618 continue;
619 }
620 analyzed_functions.insert(fn_name);
621
622 let fn_info = match sema.functions.get(&fn_name) {
624 Some(info) => *info,
625 None => continue, };
627
628 if fn_info.is_generic {
630 continue;
631 }
632
633 let fn_name_str = sema.interner.resolve(&fn_name).to_string();
634
635 let mut found = false;
637 for (inst_ref, inst) in sema.rir.iter() {
638 if let InstData::FnDecl {
639 name,
640 params_start,
641 params_len,
642 return_type,
643 body,
644 ..
645 } = &inst.data
646 && *name == fn_name
647 && !method_refs.contains(&inst_ref)
648 {
649 found = true;
650 let params = sema.rir.get_params(*params_start, *params_len);
651
652 match sema.analyze_single_function(
653 &infer_ctx,
654 &fn_name_str,
655 *return_type,
656 ¶ms,
657 *body,
658 inst.span,
659 ) {
660 Ok((
661 analyzed,
662 warnings,
663 local_strings,
664 referenced_fns,
665 referenced_meths,
666 )) => {
667 functions_with_strings.push((analyzed, local_strings));
668 all_warnings.extend(warnings);
669
670 for ref_fn in referenced_fns {
672 if !analyzed_functions.contains(&ref_fn) {
673 pending_functions.push(ref_fn);
674 }
675 }
676 for ref_meth in referenced_meths {
677 if !analyzed_methods.contains(&ref_meth) {
678 pending_methods.push(ref_meth);
679 }
680 }
681 }
682 Err(e) => errors.push(e),
683 }
684 break;
685 }
686 }
687
688 if !found {
689 }
692 }
693
694 while let Some((struct_id, method_name)) = pending_methods.pop() {
696 if analyzed_methods.contains(&(struct_id, method_name)) {
697 continue;
698 }
699 analyzed_methods.insert((struct_id, method_name));
700
701 let method_info = match sema.methods.get(&(struct_id, method_name)) {
703 Some(info) => *info,
704 None => continue,
705 };
706
707 let struct_def = sema.type_pool.struct_def(struct_id);
709 let type_name_str = struct_def.name.clone();
710 let type_name_sym = sema.interner.get_or_intern(&type_name_str);
711 let method_name_str = sema.interner.resolve(&method_name).to_string();
712
713 if type_name_str.starts_with("__anon_struct_") {
715 let full_name = if method_info.has_self {
716 format!("{}.{}", type_name_str, method_name_str)
717 } else {
718 format!("{}::{}", type_name_str, method_name_str)
719 };
720
721 let param_names = sema.param_arena.names(method_info.params);
723 let param_types = sema.param_arena.types(method_info.params);
724 let param_modes = sema.param_arena.modes(method_info.params);
725
726 let mut param_info: Vec<(Spur, Type, RirParamMode)> = Vec::new();
727
728 if method_info.has_self {
729 let self_sym = sema.interner.get_or_intern("self");
731 param_info.push((self_sym, method_info.struct_type, RirParamMode::Normal));
732 }
733
734 for i in 0..param_names.len() {
736 param_info.push((param_names[i], param_types[i], param_modes[i]));
737 }
738
739 let struct_id = method_info
742 .struct_type
743 .as_struct()
744 .expect("method must belong to struct");
745 let captured_values = sema
746 .anon_struct_captured_values
747 .get(&struct_id)
748 .cloned()
749 .unwrap_or_else(HashMap::new);
750
751 match sema.analyze_method_body(
752 &infer_ctx,
753 method_info.return_type,
754 ¶m_info,
755 method_info.body,
756 method_info.struct_type,
757 &captured_values,
758 ) {
759 Ok((
760 air,
761 num_locals,
762 num_param_slots,
763 param_modes_result,
764 param_slot_types,
765 warnings,
766 local_strings,
767 referenced_fns,
768 referenced_meths,
769 )) => {
770 let analyzed = AnalyzedFunction {
771 name: full_name,
772 air,
773 num_locals,
774 num_param_slots,
775 param_modes: param_modes_result,
776 param_slot_types,
777 is_destructor: false,
778 };
779 functions_with_strings.push((analyzed, local_strings));
780 all_warnings.extend(warnings);
781
782 for ref_fn in referenced_fns {
783 if !analyzed_functions.contains(&ref_fn) {
784 pending_functions.push(ref_fn);
785 }
786 }
787 for ref_meth in referenced_meths {
788 if !analyzed_methods.contains(&ref_meth) {
789 pending_methods.push(ref_meth);
790 }
791 }
792 }
793 Err(e) => errors.push(e),
794 }
795 continue;
796 }
797
798 for (_, inst) in sema.rir.iter() {
800 if let InstData::StructDecl {
801 name: struct_name,
802 methods_start,
803 methods_len,
804 ..
805 } = &inst.data
806 {
807 if *struct_name != type_name_sym {
808 continue;
809 }
810
811 let methods = sema.rir.get_inst_refs(*methods_start, *methods_len);
812 for method_ref in methods {
813 let method_inst = sema.rir.get(method_ref);
814 if let InstData::FnDecl {
815 name: m_name,
816 params_start,
817 params_len,
818 return_type,
819 body,
820 has_self,
821 ..
822 } = &method_inst.data
823 {
824 if *m_name != method_name {
825 continue;
826 }
827
828 let params = sema.rir.get_params(*params_start, *params_len);
829 let full_name = if *has_self {
830 format!("{}.{}", type_name_str, method_name_str)
831 } else {
832 format!("{}::{}", type_name_str, method_name_str)
833 };
834
835 match sema.analyze_method_function(
836 &infer_ctx,
837 &full_name,
838 MethodBodySpec {
839 return_type: *return_type,
840 params: ¶ms,
841 body: *body,
842 self_type: has_self.then_some(method_info.struct_type),
843 },
844 method_inst.span,
845 ) {
846 Ok((
847 analyzed,
848 warnings,
849 local_strings,
850 referenced_fns,
851 referenced_meths,
852 )) => {
853 functions_with_strings.push((analyzed, local_strings));
854 all_warnings.extend(warnings);
855
856 for ref_fn in referenced_fns {
857 if !analyzed_functions.contains(&ref_fn) {
858 pending_functions.push(ref_fn);
859 }
860 }
861 for ref_meth in referenced_meths {
862 if !analyzed_methods.contains(&ref_meth) {
863 pending_methods.push(ref_meth);
864 }
865 }
866 }
867 Err(e) => errors.push(e),
868 }
869 }
870 }
871 }
872 }
873 }
874 }
875
876 let mut analyzed_anon_enum_methods: HashSet<(EnumId, Spur)> = HashSet::new();
880 loop {
881 let pending_anon_enum_methods: Vec<(EnumId, Spur, MethodInfo)> = sema
882 .enum_methods
883 .iter()
884 .filter_map(|((enum_id, method_name), method_info)| {
885 let enum_def = sema.type_pool.enum_def(*enum_id);
886 if enum_def.name.starts_with("__anon_enum_")
887 && !analyzed_anon_enum_methods.contains(&(*enum_id, *method_name))
888 {
889 Some((*enum_id, *method_name, *method_info))
890 } else {
891 None
892 }
893 })
894 .collect();
895
896 if pending_anon_enum_methods.is_empty() {
897 break;
898 }
899
900 for (enum_id, method_name, method_info) in pending_anon_enum_methods {
901 analyzed_anon_enum_methods.insert((enum_id, method_name));
902
903 let enum_def = sema.type_pool.enum_def(enum_id);
904 let type_name_str = enum_def.name.clone();
905 let method_name_str = sema.interner.resolve(&method_name).to_string();
906
907 let full_name = if method_info.has_self {
908 format!("{}.{}", type_name_str, method_name_str)
909 } else {
910 format!("{}::{}", type_name_str, method_name_str)
911 };
912
913 let param_names = sema.param_arena.names(method_info.params);
914 let param_types = sema.param_arena.types(method_info.params);
915 let param_modes = sema.param_arena.modes(method_info.params);
916
917 let mut param_info: Vec<(Spur, Type, RirParamMode)> = Vec::new();
918
919 if method_info.has_self {
920 let self_sym = sema.interner.get_or_intern("self");
921 param_info.push((self_sym, method_info.struct_type, RirParamMode::Normal));
922 }
923
924 for i in 0..param_names.len() {
925 param_info.push((param_names[i], param_types[i], param_modes[i]));
926 }
927
928 let captured_values = sema
929 .anon_enum_captured_values
930 .get(&enum_id)
931 .cloned()
932 .unwrap_or_else(HashMap::new);
933
934 match sema.analyze_method_body(
935 &infer_ctx,
936 method_info.return_type,
937 ¶m_info,
938 method_info.body,
939 method_info.struct_type,
940 &captured_values,
941 ) {
942 Ok((
943 air,
944 num_locals,
945 num_param_slots,
946 param_modes_result,
947 param_slot_types,
948 warnings,
949 local_strings,
950 _ref_fns,
951 _ref_meths,
952 )) => {
953 let analyzed = AnalyzedFunction {
954 name: full_name,
955 air,
956 num_locals,
957 num_param_slots,
958 param_modes: param_modes_result,
959 param_slot_types,
960 is_destructor: false,
961 };
962 functions_with_strings.push((analyzed, local_strings));
963 all_warnings.extend(warnings);
964 }
965 Err(e) => errors.push(e),
966 }
967 }
968 }
969
970 for (_, inst) in sema.rir.iter() {
973 if let InstData::DropFnDecl { type_name, body } = &inst.data {
974 let type_name_str = sema.interner.resolve(type_name).to_string();
975 let struct_id = match sema.structs.get(type_name) {
976 Some(id) => *id,
977 None => continue,
978 };
979 let struct_type = Type::new_struct(struct_id);
980 let full_name = format!("{}.__drop", type_name_str);
981
982 match sema.analyze_destructor_function(
983 &infer_ctx,
984 &full_name,
985 *body,
986 inst.span,
987 struct_type,
988 ) {
989 Ok((analyzed, warnings, local_strings, _, _)) => {
990 functions_with_strings.push((analyzed, local_strings));
991 all_warnings.extend(warnings);
992 }
993 Err(e) => errors.push(e),
994 }
995 }
996 }
997
998 let mut global_string_table: HashMap<String, u32> = HashMap::new();
1000 let mut global_strings: Vec<String> = Vec::new();
1001
1002 let mut functions: Vec<AnalyzedFunction> = Vec::new();
1003 for (mut analyzed, local_strings) in functions_with_strings {
1004 if !local_strings.is_empty() {
1005 let local_to_global: Vec<u32> = local_strings
1006 .into_iter()
1007 .map(|s| {
1008 *global_string_table.entry(s.clone()).or_insert_with(|| {
1009 let id = global_strings.len() as u32;
1010 global_strings.push(s);
1011 id
1012 })
1013 })
1014 .collect();
1015
1016 analyzed
1017 .air
1018 .remap_string_ids(|local_id| local_to_global[local_id as usize]);
1019 }
1020 functions.push(analyzed);
1021 }
1022
1023 for (msg, span) in std::mem::take(&mut sema.comptime_log_output) {
1025 all_warnings.push(CompileWarning::new(
1026 WarningKind::ComptimeDbgPresent(msg),
1027 span,
1028 ));
1029 }
1030
1031 all_warnings.sort_by_key(|w| w.span().map(|s| s.start));
1032
1033 let mut output = SemaOutput {
1034 functions,
1035 strings: global_strings,
1036 warnings: all_warnings,
1037 type_pool: sema.type_pool.clone(),
1038 comptime_dbg_output: std::mem::take(&mut sema.comptime_dbg_output),
1039 };
1040
1041 if let Err(e) = crate::specialize::specialize(&mut output, sema, &infer_ctx, sema.interner) {
1044 errors.push(e);
1045 }
1046
1047 errors.into_result_with(output)
1048}
1049
1050impl<'a> Sema<'a> {
1055 pub(crate) fn require_preview(
1069 &self,
1070 feature: PreviewFeature,
1071 what: &str,
1072 span: Span,
1073 ) -> CompileResult<()> {
1074 if self.preview_features.contains(&feature) {
1075 Ok(())
1076 } else {
1077 Err(CompileError::new(
1078 ErrorKind::PreviewFeatureRequired {
1079 feature,
1080 what: what.to_string(),
1081 },
1082 span,
1083 )
1084 .with_help(format!(
1085 "use `--preview {}` to enable this feature ({})",
1086 feature.name(),
1087 feature.adr()
1088 )))
1089 }
1090 }
1091
1092 fn require_checked_for_intrinsic(
1095 ctx: &AnalysisContext,
1096 intrinsic_name: &str,
1097 span: Span,
1098 ) -> CompileResult<()> {
1099 if ctx.checked_depth > 0 {
1100 Ok(())
1101 } else {
1102 Err(CompileError::new(
1103 ErrorKind::IntrinsicRequiresChecked(intrinsic_name.to_string()),
1104 span,
1105 ))
1106 }
1107 }
1108
1109 fn analyze_single_function(
1110 &mut self,
1111 infer_ctx: &InferenceContext,
1112 fn_name: &str,
1113 return_type: Spur,
1114 params: &[gruel_rir::RirParam],
1115 body: InstRef,
1116 span: Span,
1117 ) -> AnalyzedFnResult {
1118 let ret_type = self.resolve_type(return_type, span)?;
1119
1120 let param_info: Vec<(Spur, Type, RirParamMode)> = params
1122 .iter()
1123 .map(|p| {
1124 let ty = self.resolve_type(p.ty, span)?;
1125 Ok((p.name, ty, p.mode))
1126 })
1127 .collect::<CompileResult<Vec<_>>>()?;
1128
1129 let (
1130 air,
1131 num_locals,
1132 num_param_slots,
1133 param_modes,
1134 param_slot_types,
1135 warnings,
1136 local_strings,
1137 ref_fns,
1138 ref_meths,
1139 ) = self.analyze_function(infer_ctx, ret_type, ¶m_info, body)?;
1140
1141 Ok((
1142 AnalyzedFunction {
1143 name: fn_name.to_string(),
1144 air,
1145 num_locals,
1146 num_param_slots,
1147 param_modes,
1148 param_slot_types,
1149 is_destructor: false,
1150 },
1151 warnings,
1152 local_strings,
1153 ref_fns,
1154 ref_meths,
1155 ))
1156 }
1157
1158 fn analyze_method_function(
1164 &mut self,
1165 infer_ctx: &InferenceContext,
1166 full_name: &str,
1167 spec: MethodBodySpec<'_>,
1168 span: Span,
1169 ) -> AnalyzedFnResult {
1170 let ret_type = self.resolve_type(spec.return_type, span)?;
1171
1172 let mut param_info: Vec<(Spur, Type, RirParamMode)> = Vec::new();
1174
1175 if let Some(struct_type) = spec.self_type {
1176 let self_sym = self.interner.get_or_intern("self");
1178 param_info.push((self_sym, struct_type, RirParamMode::Normal));
1179 }
1180
1181 for p in spec.params.iter() {
1183 let ty = self.resolve_type(p.ty, span)?;
1184 param_info.push((p.name, ty, p.mode));
1185 }
1186
1187 let (
1188 air,
1189 num_locals,
1190 num_param_slots,
1191 param_modes,
1192 param_slot_types,
1193 warnings,
1194 local_strings,
1195 ref_fns,
1196 ref_meths,
1197 ) = self.analyze_function(infer_ctx, ret_type, ¶m_info, spec.body)?;
1198
1199 Ok((
1200 AnalyzedFunction {
1201 name: full_name.to_string(),
1202 air,
1203 num_locals,
1204 num_param_slots,
1205 param_modes,
1206 param_slot_types,
1207 is_destructor: false,
1208 },
1209 warnings,
1210 local_strings,
1211 ref_fns,
1212 ref_meths,
1213 ))
1214 }
1215
1216 fn analyze_destructor_function(
1222 &mut self,
1223 infer_ctx: &InferenceContext,
1224 full_name: &str,
1225 body: InstRef,
1226 _span: Span,
1227 struct_type: Type,
1228 ) -> AnalyzedFnResult {
1229 let self_sym = self.interner.get_or_intern("self");
1231 let param_info: Vec<(Spur, Type, RirParamMode)> =
1232 vec![(self_sym, struct_type, RirParamMode::Normal)];
1233
1234 let (
1235 air,
1236 num_locals,
1237 num_param_slots,
1238 param_modes,
1239 param_slot_types,
1240 warnings,
1241 local_strings,
1242 ref_fns,
1243 ref_meths,
1244 ) = self.analyze_function(infer_ctx, Type::UNIT, ¶m_info, body)?;
1245
1246 Ok((
1247 AnalyzedFunction {
1248 name: full_name.to_string(),
1249 air,
1250 num_locals,
1251 num_param_slots,
1252 param_modes,
1253 param_slot_types,
1254 is_destructor: true,
1255 },
1256 warnings,
1257 local_strings,
1258 ref_fns,
1259 ref_meths,
1260 ))
1261 }
1262 fn analyze_function(
1270 &mut self,
1271 infer_ctx: &InferenceContext,
1272 return_type: Type,
1273 params: &[(Spur, Type, RirParamMode)], body: InstRef,
1275 ) -> RawFnAnalysis {
1276 self.analyze_function_internal(infer_ctx, return_type, params, body, None, None)
1277 }
1278
1279 fn analyze_function_internal(
1285 &mut self,
1286 infer_ctx: &InferenceContext,
1287 return_type: Type,
1288 params: &[(Spur, Type, RirParamMode)],
1289 body: InstRef,
1290 type_subst: Option<&std::collections::HashMap<Spur, Type>>,
1291 value_subst: Option<&std::collections::HashMap<Spur, ConstValue>>,
1292 ) -> RawFnAnalysis {
1293 let mut air = Air::new(return_type);
1294 let mut param_vec: Vec<ParamInfo> = Vec::new();
1295 let mut param_modes: Vec<bool> = Vec::new();
1296 let mut param_slot_types: Vec<Type> = Vec::new();
1297
1298 let mut next_abi_slot: u32 = 0;
1302 for (pname, ptype, mode) in params.iter() {
1303 param_vec.push(ParamInfo {
1304 name: *pname,
1305 abi_slot: next_abi_slot,
1306 ty: *ptype,
1307 mode: *mode,
1308 });
1309 let is_by_ref = *mode == RirParamMode::Inout || *mode == RirParamMode::Borrow;
1313 let slot_count = if is_by_ref {
1314 1
1316 } else {
1317 self.abi_slot_count(*ptype)
1318 };
1319 for _ in 0..slot_count {
1320 param_modes.push(is_by_ref);
1321 param_slot_types.push(*ptype);
1322 }
1323 next_abi_slot += slot_count;
1324 }
1325 let num_param_slots = next_abi_slot;
1326
1327 let resolved_types = self.run_type_inference(
1333 infer_ctx,
1334 return_type,
1335 params,
1336 body,
1337 type_subst,
1338 value_subst,
1339 )?;
1340
1341 let comptime_type_vars = type_subst.cloned().unwrap_or_default();
1345 let comptime_value_vars = value_subst.cloned().unwrap_or_default();
1346 let mut ctx = AnalysisContext {
1347 locals: HashMap::new(),
1348 params: ¶m_vec,
1349 next_slot: 0,
1350 loop_depth: 0,
1351 forbid_break: None,
1352 checked_depth: 0,
1353 used_locals: HashSet::new(),
1354 return_type,
1355 scope_stack: Vec::new(),
1356 resolved_types: &resolved_types,
1357 moved_vars: HashMap::new(),
1358 warnings: Vec::new(),
1359 local_string_table: HashMap::new(),
1360 local_strings: Vec::new(),
1361 comptime_type_vars,
1362 comptime_value_vars,
1363 referenced_functions: HashSet::new(),
1364 referenced_methods: HashSet::new(),
1365 };
1366
1367 let body_result = self.analyze_inst(&mut air, body, &mut ctx)?;
1372
1373 if body_result.ty != Type::NEVER {
1375 air.add_inst(AirInst {
1376 data: AirInstData::Ret(Some(body_result.air_ref)),
1377 ty: return_type,
1378 span: self.rir.get(body).span,
1379 });
1380 }
1381
1382 Ok((
1383 air,
1384 ctx.next_slot,
1385 num_param_slots,
1386 param_modes,
1387 param_slot_types,
1388 ctx.warnings,
1389 ctx.local_strings,
1390 ctx.referenced_functions,
1391 ctx.referenced_methods,
1392 ))
1393 }
1394
1395 pub fn analyze_specialized_function(
1404 &mut self,
1405 infer_ctx: &InferenceContext,
1406 return_type: Type,
1407 params: &[(Spur, Type, RirParamMode)],
1408 body: InstRef,
1409 type_subst: &std::collections::HashMap<Spur, Type>,
1410 ) -> RawFnAnalysis {
1411 self.analyze_function_internal(infer_ctx, return_type, params, body, Some(type_subst), None)
1415 }
1416
1417 fn analyze_method_body(
1423 &mut self,
1424 infer_ctx: &InferenceContext,
1425 return_type: Type,
1426 params: &[(Spur, Type, RirParamMode)],
1427 body: InstRef,
1428 self_type: Type,
1429 captured_comptime_values: &std::collections::HashMap<Spur, ConstValue>,
1430 ) -> RawFnAnalysis {
1431 let self_sym = self.interner.get_or_intern("Self");
1433 let mut type_subst = HashMap::new();
1434 type_subst.insert(self_sym, self_type);
1435
1436 self.analyze_function_internal(
1437 infer_ctx,
1438 return_type,
1439 params,
1440 body,
1441 Some(&type_subst),
1442 Some(captured_comptime_values),
1443 )
1444 }
1445
1446 fn run_type_inference(
1458 &mut self,
1459 infer_ctx: &InferenceContext,
1460 return_type: Type,
1461 params: &[(Spur, Type, RirParamMode)],
1462 body: InstRef,
1463 type_subst: Option<&HashMap<Spur, Type>>,
1464 value_subst: Option<&HashMap<Spur, ConstValue>>,
1465 ) -> CompileResult<HashMap<InstRef, Type>> {
1466 let mut cgen = ConstraintGenerator::new(
1468 self.rir,
1469 self.interner,
1470 &infer_ctx.func_sigs,
1471 &infer_ctx.struct_types,
1472 &infer_ctx.enum_types,
1473 &infer_ctx.method_sigs,
1474 &infer_ctx.enum_method_sigs,
1475 &self.type_pool,
1476 )
1477 .with_type_subst(type_subst);
1478
1479 let mut param_vars: HashMap<Spur, ParamVarInfo> = params
1482 .iter()
1483 .map(|(name, ty, _mode)| {
1484 (
1485 *name,
1486 ParamVarInfo {
1487 ty: self.type_to_infer_type(*ty),
1488 },
1489 )
1490 })
1491 .collect();
1492
1493 if let Some(values) = value_subst {
1496 for (name, const_val) in values {
1497 let ty = match const_val {
1498 ConstValue::Integer(_) => Type::COMPTIME_INT,
1499 ConstValue::Bool(_) => Type::BOOL,
1500 ConstValue::Type(t) => *t,
1501 ConstValue::Unit => Type::UNIT,
1502 ConstValue::ComptimeStr(_) => Type::COMPTIME_STR,
1503 ConstValue::Struct(_)
1504 | ConstValue::Array(_)
1505 | ConstValue::EnumVariant { .. }
1506 | ConstValue::EnumData { .. }
1507 | ConstValue::EnumStruct { .. }
1508 | ConstValue::BreakSignal
1509 | ConstValue::ContinueSignal
1510 | ConstValue::ReturnSignal => {
1511 unreachable!(
1512 "control-flow signal or composite value in comptime_value_vars"
1513 )
1514 }
1515 };
1516 param_vars.insert(
1517 *name,
1518 ParamVarInfo {
1519 ty: self.type_to_infer_type(ty),
1520 },
1521 );
1522 }
1523 }
1524
1525 let mut cgen_ctx = ConstraintContext::new(¶m_vars, return_type);
1527
1528 let body_info = cgen.generate(body, &mut cgen_ctx);
1530
1531 cgen.add_constraint(Constraint::equal(
1535 body_info.ty,
1536 self.type_to_infer_type(return_type),
1537 body_info.span,
1538 ));
1539
1540 let (constraints, int_literal_vars, float_literal_vars, expr_types, type_var_count) =
1542 cgen.into_parts();
1543
1544 let mut unifier = Unifier::with_capacity(type_var_count);
1547 let errors = unifier.solve_constraints(&constraints);
1548
1549 if let Some(err) = errors.first() {
1553 let error_kind = match &err.kind {
1555 UnifyResult::Ok => unreachable!("UnificationError should never contain Ok"),
1556 UnifyResult::TypeMismatch { expected, found } => ErrorKind::TypeMismatch {
1557 expected: expected.to_string(),
1558 found: found.to_string(),
1559 },
1560 UnifyResult::IntLiteralNonInteger { found } => ErrorKind::TypeMismatch {
1561 expected: "integer type".to_string(),
1562 found: found.name().to_string(),
1563 },
1564 UnifyResult::OccursCheck { var, ty } => ErrorKind::TypeMismatch {
1565 expected: "non-recursive type".to_string(),
1566 found: format!("{var} = {ty} (infinite type)"),
1567 },
1568 UnifyResult::NotSigned { ty } => {
1569 ErrorKind::CannotNegateUnsigned(ty.name().to_string())
1570 }
1571 UnifyResult::NotInteger { ty } => ErrorKind::TypeMismatch {
1572 expected: "integer type".to_string(),
1573 found: ty.name().to_string(),
1574 },
1575 UnifyResult::NotUnsigned { ty } => ErrorKind::TypeMismatch {
1576 expected: "unsigned integer type".to_string(),
1577 found: ty.name().to_string(),
1578 },
1579 UnifyResult::NotNumeric { ty } => ErrorKind::TypeMismatch {
1580 expected: "numeric type".to_string(),
1581 found: ty.name().to_string(),
1582 },
1583 UnifyResult::ArrayLengthMismatch { expected, found } => {
1584 ErrorKind::ArrayLengthMismatch {
1585 expected: *expected,
1586 found: *found,
1587 }
1588 }
1589 };
1590
1591 let mut compile_error = CompileError::new(error_kind, err.span);
1592
1593 if matches!(err.kind, UnifyResult::NotSigned { .. }) {
1595 compile_error = compile_error.with_note("unsigned values cannot be negated");
1596 }
1597
1598 return Err(compile_error);
1599 }
1600
1601 unifier.default_int_literal_vars(&int_literal_vars);
1603 unifier.default_float_literal_vars(&float_literal_vars);
1604
1605 for infer_ty in expr_types.values() {
1610 let resolved = unifier.resolve_infer_type(infer_ty);
1611 self.pre_create_array_types_from_infer_type(&resolved);
1612 }
1613
1614 let mut resolved_types = HashMap::new();
1618 for (inst_ref, infer_ty) in &expr_types {
1619 let resolved = unifier.resolve_infer_type(infer_ty);
1620 let concrete_ty = self.infer_type_to_type(&resolved);
1621 resolved_types.insert(*inst_ref, concrete_ty);
1622 }
1623
1624 Ok(resolved_types)
1625 }
1626 pub(crate) fn analyze_inst_for_projection(
1633 &mut self,
1634 air: &mut Air,
1635 inst_ref: InstRef,
1636 ctx: &mut AnalysisContext,
1637 ) -> CompileResult<AnalysisResult> {
1638 let inst = self.rir.get(inst_ref);
1639
1640 if let InstData::VarRef { name } = &inst.data {
1642 if let Some(param_info) = ctx.params.iter().find(|p| p.name == *name) {
1644 let ty = param_info.ty;
1645
1646 if let Some(move_state) = ctx.moved_vars.get(name)
1649 && let Some(moved_span) = move_state.full_move
1650 {
1651 let name_str = self.interner.resolve(name);
1652 return Err(CompileError::new(
1653 ErrorKind::UseAfterMove(name_str.to_string()),
1654 inst.span,
1655 )
1656 .with_label("value moved here", moved_span));
1657 }
1658
1659 let air_ref = air.add_inst(AirInst {
1662 data: AirInstData::Param {
1663 index: param_info.abi_slot,
1664 },
1665 ty,
1666 span: inst.span,
1667 });
1668 return Ok(AnalysisResult::new(air_ref, ty));
1669 }
1670
1671 if let Some(local) = ctx.locals.get(name) {
1673 let ty = local.ty;
1674 let slot = local.slot;
1675
1676 if let Some(move_state) = ctx.moved_vars.get(name)
1679 && let Some(moved_span) = move_state.full_move
1680 {
1681 let name_str = self.interner.resolve(name);
1682 return Err(CompileError::new(
1683 ErrorKind::UseAfterMove(name_str.to_string()),
1684 inst.span,
1685 )
1686 .with_label("value moved here", moved_span));
1687 }
1688
1689 ctx.used_locals.insert(*name);
1693
1694 let air_ref = air.add_inst(AirInst {
1696 data: AirInstData::Load { slot },
1697 ty,
1698 span: inst.span,
1699 });
1700 return Ok(AnalysisResult::new(air_ref, ty));
1701 }
1702
1703 if let Some(&ty) = ctx.comptime_type_vars.get(name) {
1705 let air_ref = air.add_inst(AirInst {
1706 data: AirInstData::TypeConst(ty),
1707 ty: Type::COMPTIME_TYPE,
1708 span: inst.span,
1709 });
1710 return Ok(AnalysisResult::new(air_ref, Type::COMPTIME_TYPE));
1711 }
1712
1713 if let Some(const_value) = ctx.comptime_value_vars.get(name) {
1715 match const_value {
1716 ConstValue::Integer(val) => {
1717 let ty = Self::get_resolved_type(
1718 ctx,
1719 inst_ref,
1720 inst.span,
1721 "comptime integer value",
1722 )?;
1723 let air_ref = air.add_inst(AirInst {
1724 data: AirInstData::Const(*val as u64),
1725 ty,
1726 span: inst.span,
1727 });
1728 return Ok(AnalysisResult::new(air_ref, ty));
1729 }
1730 ConstValue::Bool(val) => {
1731 let air_ref = air.add_inst(AirInst {
1732 data: AirInstData::Const(*val as u64),
1733 ty: Type::BOOL,
1734 span: inst.span,
1735 });
1736 return Ok(AnalysisResult::new(air_ref, Type::BOOL));
1737 }
1738 ConstValue::Type(ty) => {
1739 let air_ref = air.add_inst(AirInst {
1740 data: AirInstData::TypeConst(*ty),
1741 ty: Type::COMPTIME_TYPE,
1742 span: inst.span,
1743 });
1744 return Ok(AnalysisResult::new(air_ref, Type::COMPTIME_TYPE));
1745 }
1746 ConstValue::ComptimeStr(_)
1747 | ConstValue::Struct(_)
1748 | ConstValue::Array(_)
1749 | ConstValue::EnumVariant { .. }
1750 | ConstValue::EnumData { .. }
1751 | ConstValue::EnumStruct { .. } => {
1752 return Err(CompileError::new(
1753 ErrorKind::ComptimeEvaluationFailed {
1754 reason: "comptime composite values cannot be used in runtime expressions; use @field to access fields".to_string(),
1755 },
1756 inst.span,
1757 ));
1758 }
1759 ConstValue::Unit => {
1760 return Err(CompileError::new(
1761 ErrorKind::ComptimeEvaluationFailed {
1762 reason:
1763 "comptime unit values cannot be used in runtime expressions"
1764 .to_string(),
1765 },
1766 inst.span,
1767 ));
1768 }
1769 ConstValue::BreakSignal
1770 | ConstValue::ContinueSignal
1771 | ConstValue::ReturnSignal => {
1772 unreachable!("control-flow signal in comptime_value_vars")
1773 }
1774 }
1775 }
1776
1777 let name_str = self.interner.resolve(name);
1779 return Err(CompileError::new(
1780 ErrorKind::UndefinedVariable(name_str.to_string()),
1781 inst.span,
1782 ));
1783 }
1784
1785 if let InstData::FieldGet { base, field } = &inst.data {
1787 let base_result = self.analyze_inst_for_projection(air, *base, ctx)?;
1788 let base_type = base_result.ty;
1789
1790 let struct_id = match base_type.kind() {
1791 TypeKind::Struct(id) => id,
1792 _ => {
1793 return Err(CompileError::new(
1794 ErrorKind::FieldAccessOnNonStruct {
1795 found: base_type.name().to_string(),
1796 },
1797 inst.span,
1798 ));
1799 }
1800 };
1801
1802 let struct_def = self.type_pool.struct_def(struct_id);
1803 let field_name_str = self.interner.resolve(field).to_string();
1804
1805 let (field_index, struct_field) =
1806 struct_def.find_field(&field_name_str).ok_or_compile_error(
1807 ErrorKind::UnknownField {
1808 struct_name: struct_def.name.clone(),
1809 field_name: field_name_str.clone(),
1810 },
1811 inst.span,
1812 )?;
1813
1814 let field_type = struct_field.ty;
1815
1816 let air_ref = air.add_inst(AirInst {
1817 data: AirInstData::FieldGet {
1818 base: base_result.air_ref,
1819 struct_id,
1820 field_index: field_index as u32,
1821 },
1822 ty: field_type,
1823 span: inst.span,
1824 });
1825 return Ok(AnalysisResult::new(air_ref, field_type));
1826 }
1827
1828 if let InstData::IndexGet { base, index } = &inst.data {
1832 let base_result = self.analyze_inst_for_projection(air, *base, ctx)?;
1834 let base_type = base_result.ty;
1835
1836 let array_type_id = match base_type.kind() {
1837 TypeKind::Array(id) => id,
1838 _ => {
1839 return Err(CompileError::new(
1840 ErrorKind::IndexOnNonArray {
1841 found: base_type.name().to_string(),
1842 },
1843 inst.span,
1844 ));
1845 }
1846 };
1847
1848 let (element_type, length) = self.type_pool.array_def(array_type_id);
1849
1850 let index_result = self.analyze_inst(air, *index, ctx)?;
1852 if !index_result.ty.is_unsigned() && !index_result.ty.is_error() {
1853 return Err(CompileError::new(
1854 ErrorKind::TypeMismatch {
1855 expected: "unsigned integer type".to_string(),
1856 found: index_result.ty.name().to_string(),
1857 },
1858 self.rir.get(*index).span,
1859 ));
1860 }
1861
1862 let array_length = length;
1863
1864 if let Some(const_index) = self.try_get_const_index(*index)
1866 && (const_index < 0 || const_index as u64 >= array_length)
1867 {
1868 return Err(CompileError::new(
1869 ErrorKind::IndexOutOfBounds {
1870 index: const_index,
1871 length: array_length,
1872 },
1873 self.rir.get(*index).span,
1874 ));
1875 }
1876
1877 let air_ref = air.add_inst(AirInst {
1882 data: AirInstData::IndexGet {
1883 base: base_result.air_ref,
1884 array_type: base_type,
1885 index: index_result.air_ref,
1886 },
1887 ty: element_type,
1888 span: inst.span,
1889 });
1890 return Ok(AnalysisResult::new(air_ref, element_type));
1891 }
1892
1893 self.analyze_inst(air, inst_ref, ctx)
1896 }
1897
1898 pub(crate) fn get_resolved_type(
1904 ctx: &AnalysisContext,
1905 inst_ref: InstRef,
1906 span: Span,
1907 context: &str,
1908 ) -> CompileResult<Type> {
1909 ctx.resolved_types.get(&inst_ref).copied().ok_or_else(|| {
1910 CompileError::new(
1911 ErrorKind::InternalError(format!(
1912 "type inference did not resolve type for {} (instruction {:?})",
1913 context, inst_ref
1914 )),
1915 span,
1916 )
1917 })
1918 }
1919
1920 pub(crate) fn analyze_inst(
1945 &mut self,
1946 air: &mut Air,
1947 inst_ref: InstRef,
1948 ctx: &mut AnalysisContext,
1949 ) -> CompileResult<AnalysisResult> {
1950 let inst = self.rir.get(inst_ref);
1951
1952 match &inst.data {
1953 InstData::IntConst(_)
1955 | InstData::FloatConst(_)
1956 | InstData::BoolConst(_)
1957 | InstData::StringConst(_)
1958 | InstData::UnitConst => self.analyze_literal(air, inst_ref, ctx),
1959
1960 InstData::Add { lhs, rhs } => {
1962 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::Add, inst.span, ctx)
1963 }
1964 InstData::Sub { lhs, rhs } => {
1965 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::Sub, inst.span, ctx)
1966 }
1967 InstData::Mul { lhs, rhs } => {
1968 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::Mul, inst.span, ctx)
1969 }
1970 InstData::Div { lhs, rhs } => {
1971 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::Div, inst.span, ctx)
1972 }
1973 InstData::Mod { lhs, rhs } => {
1974 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::Mod, inst.span, ctx)
1975 }
1976
1977 InstData::BitAnd { lhs, rhs } => {
1979 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::BitAnd, inst.span, ctx)
1980 }
1981 InstData::BitOr { lhs, rhs } => {
1982 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::BitOr, inst.span, ctx)
1983 }
1984 InstData::BitXor { lhs, rhs } => {
1985 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::BitXor, inst.span, ctx)
1986 }
1987 InstData::Shl { lhs, rhs } => {
1988 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::Shl, inst.span, ctx)
1989 }
1990 InstData::Shr { lhs, rhs } => {
1991 self.analyze_binary_arith(air, *lhs, *rhs, AirInstData::Shr, inst.span, ctx)
1992 }
1993
1994 InstData::Eq { lhs, rhs } => {
1996 self.analyze_comparison(air, (*lhs, *rhs), true, AirInstData::Eq, inst.span, ctx)
1997 }
1998 InstData::Ne { lhs, rhs } => {
1999 self.analyze_comparison(air, (*lhs, *rhs), true, AirInstData::Ne, inst.span, ctx)
2000 }
2001 InstData::Lt { lhs, rhs } => {
2002 self.analyze_comparison(air, (*lhs, *rhs), false, AirInstData::Lt, inst.span, ctx)
2003 }
2004 InstData::Gt { lhs, rhs } => {
2005 self.analyze_comparison(air, (*lhs, *rhs), false, AirInstData::Gt, inst.span, ctx)
2006 }
2007 InstData::Le { lhs, rhs } => {
2008 self.analyze_comparison(air, (*lhs, *rhs), false, AirInstData::Le, inst.span, ctx)
2009 }
2010 InstData::Ge { lhs, rhs } => {
2011 self.analyze_comparison(air, (*lhs, *rhs), false, AirInstData::Ge, inst.span, ctx)
2012 }
2013
2014 InstData::And { .. } | InstData::Or { .. } => {
2016 self.analyze_logical_op(air, inst_ref, ctx)
2017 }
2018
2019 InstData::Neg { .. } | InstData::Not { .. } | InstData::BitNot { .. } => {
2021 self.analyze_unary_op(air, inst_ref, ctx)
2022 }
2023
2024 InstData::Branch { .. }
2026 | InstData::Loop { .. }
2027 | InstData::For { .. }
2028 | InstData::InfiniteLoop { .. }
2029 | InstData::Match { .. }
2030 | InstData::Break
2031 | InstData::Continue
2032 | InstData::Ret(_)
2033 | InstData::Block { .. } => self.analyze_control_flow(air, inst_ref, ctx),
2034
2035 InstData::Alloc { .. }
2037 | InstData::StructDestructure { .. }
2038 | InstData::VarRef { .. }
2039 | InstData::ParamRef { .. }
2040 | InstData::Assign { .. } => self.analyze_variable_ops(air, inst_ref, ctx),
2041
2042 InstData::StructDecl { .. }
2044 | InstData::StructInit { .. }
2045 | InstData::FieldGet { .. }
2046 | InstData::FieldSet { .. } => self.analyze_struct_ops(air, inst_ref, ctx),
2047
2048 InstData::ArrayInit { .. } | InstData::IndexGet { .. } | InstData::IndexSet { .. } => {
2050 self.analyze_array_ops(air, inst_ref, ctx)
2051 }
2052
2053 InstData::EnumDecl { .. }
2055 | InstData::EnumVariant { .. }
2056 | InstData::EnumStructVariant { .. } => self.analyze_enum_ops(air, inst_ref, ctx),
2057
2058 InstData::Call { .. } | InstData::MethodCall { .. } | InstData::AssocFnCall { .. } => {
2060 self.analyze_call_ops(air, inst_ref, ctx)
2061 }
2062
2063 InstData::Intrinsic { .. } | InstData::TypeIntrinsic { .. } => {
2065 self.analyze_intrinsic_ops(air, inst_ref, ctx)
2066 }
2067
2068 InstData::DropFnDecl { .. } | InstData::FnDecl { .. } | InstData::ConstDecl { .. } => {
2070 self.analyze_decl_noop(air, inst_ref, ctx)
2071 }
2072
2073 InstData::Comptime { expr } => {
2075 let span = inst.span;
2076 let expr = *expr;
2077 match self.evaluate_comptime_block(expr, ctx, span)? {
2081 ConstValue::Integer(value) => {
2082 let ty =
2083 Self::get_resolved_type(ctx, inst_ref, span, "comptime block")?;
2084 if value < 0 {
2085 return Err(CompileError::new(
2086 ErrorKind::ComptimeEvaluationFailed {
2087 reason: "negative values not yet supported in comptime"
2088 .to_string(),
2089 },
2090 span,
2091 ));
2092 }
2093 let unsigned_value = value as u64;
2094 if !ty.literal_fits(unsigned_value) {
2095 return Err(CompileError::new(
2096 ErrorKind::LiteralOutOfRange {
2097 value: unsigned_value,
2098 ty: ty.name().to_string(),
2099 },
2100 span,
2101 ));
2102 }
2103 let air_ref = air.add_inst(AirInst {
2104 data: AirInstData::Const(unsigned_value),
2105 ty,
2106 span,
2107 });
2108 Ok(AnalysisResult::new(air_ref, ty))
2109 }
2110 ConstValue::Bool(value) => {
2111 let ty = Type::BOOL;
2112 let air_ref = air.add_inst(AirInst {
2113 data: AirInstData::BoolConst(value),
2114 ty,
2115 span,
2116 });
2117 Ok(AnalysisResult::new(air_ref, ty))
2118 }
2119 ConstValue::Type(_) => Err(CompileError::new(
2120 ErrorKind::ComptimeEvaluationFailed {
2121 reason: "type values cannot exist at runtime".to_string(),
2122 },
2123 span,
2124 )),
2125 ConstValue::ComptimeStr(str_idx) => {
2126 let content =
2128 self.resolve_comptime_str(str_idx, span)?.to_string();
2129 let ty = self.builtin_string_type();
2130 let local_string_id = ctx.add_local_string(content);
2131 let air_ref = air.add_inst(AirInst {
2132 data: AirInstData::StringConst(local_string_id),
2133 ty,
2134 span,
2135 });
2136 Ok(AnalysisResult::new(air_ref, ty))
2137 }
2138 ConstValue::Unit => {
2139 let air_ref = air.add_inst(AirInst {
2140 data: AirInstData::UnitConst,
2141 ty: Type::UNIT,
2142 span,
2143 });
2144 Ok(AnalysisResult::new(air_ref, Type::UNIT))
2145 }
2146 ConstValue::Struct(_)
2149 | ConstValue::Array(_)
2150 | ConstValue::EnumVariant { .. }
2151 | ConstValue::EnumData { .. }
2152 | ConstValue::EnumStruct { .. } => {
2153 Err(CompileError::new(
2154 ErrorKind::ComptimeEvaluationFailed {
2155 reason: "comptime composite values cannot be used at runtime; access individual fields or elements instead".into(),
2156 },
2157 span,
2158 ))
2159 }
2160 ConstValue::BreakSignal
2164 | ConstValue::ContinueSignal
2165 | ConstValue::ReturnSignal => {
2166 unreachable!("control-flow signal escaped evaluate_comptime_block")
2167 }
2168 }
2169 }
2170
2171 InstData::ComptimeUnrollFor {
2173 binding,
2174 iterable,
2175 body,
2176 } => {
2177 let span = inst.span;
2178 let binding = *binding;
2179 let iterable = *iterable;
2180 let body = *body;
2181
2182 let iterable_val = self.evaluate_comptime_block(iterable, ctx, span)?;
2185
2186 let elements = match iterable_val {
2190 ConstValue::Array(heap_idx) => match &self.comptime_heap[heap_idx as usize] {
2191 ComptimeHeapItem::Array(elems) => elems.clone(),
2192 _ => {
2193 return Err(CompileError::new(
2194 ErrorKind::ComptimeEvaluationFailed {
2195 reason: "comptime_unroll iterable is not an array".to_string(),
2196 },
2197 span,
2198 ));
2199 }
2200 },
2201 _ => {
2202 return Err(CompileError::new(
2203 ErrorKind::ComptimeEvaluationFailed {
2204 reason: "comptime_unroll for requires an array iterable"
2205 .to_string(),
2206 },
2207 span,
2208 ));
2209 }
2210 };
2211
2212 let mut body_air_refs = Vec::with_capacity(elements.len());
2216 for element in &elements {
2217 let prev_value = ctx.comptime_value_vars.insert(binding, *element);
2219
2220 let body_result = self.analyze_inst(air, body, ctx)?;
2222 body_air_refs.push(body_result.air_ref);
2223
2224 match prev_value {
2226 Some(v) => {
2227 ctx.comptime_value_vars.insert(binding, v);
2228 }
2229 None => {
2230 ctx.comptime_value_vars.remove(&binding);
2231 }
2232 }
2233 }
2234
2235 if body_air_refs.is_empty() {
2237 let air_ref = air.add_inst(AirInst {
2239 data: AirInstData::UnitConst,
2240 ty: Type::UNIT,
2241 span,
2242 });
2243 Ok(AnalysisResult::new(air_ref, Type::UNIT))
2244 } else if body_air_refs.len() == 1 {
2245 Ok(AnalysisResult::new(body_air_refs[0], Type::UNIT))
2246 } else {
2247 let last = body_air_refs.pop().unwrap();
2250 let stmts: Vec<u32> = body_air_refs.iter().map(|r| r.as_u32()).collect();
2251 let stmts_start = air.add_extra(&stmts);
2252 let block_ref = air.add_inst(AirInst {
2253 data: AirInstData::Block {
2254 stmts_start,
2255 stmts_len: stmts.len() as u32,
2256 value: last,
2257 },
2258 ty: Type::UNIT,
2259 span,
2260 });
2261 Ok(AnalysisResult::new(block_ref, Type::UNIT))
2262 }
2263 }
2264
2265 InstData::TypeConst { type_name } => {
2267 let ty = self.resolve_type(*type_name, inst.span)?;
2269 let air_ref = air.add_inst(AirInst {
2270 data: AirInstData::TypeConst(ty),
2271 ty: Type::COMPTIME_TYPE,
2272 span: inst.span,
2273 });
2274 Ok(AnalysisResult::new(air_ref, Type::COMPTIME_TYPE))
2275 }
2276
2277 InstData::AnonStructType {
2280 fields_start,
2281 fields_len,
2282 methods_start,
2283 methods_len,
2284 } => {
2285 let field_decls = self.rir.get_field_decls(*fields_start, *fields_len);
2287
2288 if field_decls.is_empty() && *methods_len == 0 {
2290 return Err(CompileError::new(ErrorKind::EmptyStruct, inst.span));
2291 }
2292
2293 let mut struct_fields = Vec::with_capacity(field_decls.len());
2297 for (name_sym, type_sym) in field_decls {
2298 let name_str = self.interner.resolve(&name_sym).to_string();
2299 let field_ty = self.resolve_type(type_sym, inst.span)?;
2300 struct_fields.push(StructField {
2301 name: name_str,
2302 ty: field_ty,
2303 });
2304 }
2305
2306 let method_sigs = self.extract_anon_method_sigs(*methods_start, *methods_len);
2309
2310 let (struct_ty, _is_new) =
2313 self.find_or_create_anon_struct(&struct_fields, &method_sigs, &HashMap::new());
2314
2315 let air_ref = air.add_inst(AirInst {
2333 data: AirInstData::TypeConst(struct_ty),
2334 ty: Type::COMPTIME_TYPE,
2335 span: inst.span,
2336 });
2337 Ok(AnalysisResult::new(air_ref, Type::COMPTIME_TYPE))
2338 }
2339
2340 InstData::AnonEnumType {
2343 variants_start,
2344 variants_len,
2345 methods_start,
2346 methods_len,
2347 } => {
2348 let variant_decls = self
2350 .rir
2351 .get_enum_variant_decls(*variants_start, *variants_len);
2352
2353 if variant_decls.is_empty() {
2355 return Err(CompileError::new(ErrorKind::EmptyAnonEnum, inst.span));
2356 }
2357
2358 let mut enum_variants = Vec::with_capacity(variant_decls.len());
2360 for (name_sym, field_type_syms, field_name_syms) in &variant_decls {
2361 let name_str = self.interner.resolve(name_sym).to_string();
2362 let mut fields = Vec::with_capacity(field_type_syms.len());
2363 for ty_sym in field_type_syms {
2364 let field_ty = self.resolve_type(*ty_sym, inst.span)?;
2365 fields.push(field_ty);
2366 }
2367 let field_names: Vec<String> = field_name_syms
2368 .iter()
2369 .map(|s| self.interner.resolve(s).to_string())
2370 .collect();
2371 enum_variants.push(EnumVariantDef {
2372 name: name_str,
2373 fields,
2374 field_names,
2375 });
2376 }
2377
2378 if *methods_len > 0 {
2380 let method_refs = self.rir.get_inst_refs(*methods_start, *methods_len);
2381 let mut seen_method_names: std::collections::HashSet<Spur> =
2382 std::collections::HashSet::new();
2383 for mref in method_refs {
2384 let minst = self.rir.get(mref);
2385 if let InstData::FnDecl {
2386 name: method_name, ..
2387 } = &minst.data
2388 && !seen_method_names.insert(*method_name)
2389 {
2390 let method_name_str = self.interner.resolve(method_name).to_string();
2391 return Err(CompileError::new(
2392 ErrorKind::DuplicateMethod {
2393 type_name: "anonymous enum".to_string(),
2394 method_name: method_name_str,
2395 },
2396 minst.span,
2397 ));
2398 }
2399 }
2400 }
2401
2402 let method_sigs = self.extract_anon_method_sigs(*methods_start, *methods_len);
2404
2405 let (enum_ty, _is_new) =
2407 self.find_or_create_anon_enum(&enum_variants, &method_sigs, &HashMap::new());
2408
2409 let air_ref = air.add_inst(AirInst {
2410 data: AirInstData::TypeConst(enum_ty),
2411 ty: Type::COMPTIME_TYPE,
2412 span: inst.span,
2413 });
2414 Ok(AnalysisResult::new(air_ref, Type::COMPTIME_TYPE))
2415 }
2416
2417 InstData::Checked { expr } => {
2419 ctx.checked_depth += 1;
2420 let result = self.analyze_inst(air, *expr, ctx);
2421 ctx.checked_depth -= 1;
2422 result
2423 }
2424 }
2425 }
2426
2427 pub(crate) fn analyze_field_set_impl(
2434 &mut self,
2435 air: &mut Air,
2436 base: InstRef,
2437 field: Spur,
2438 value: InstRef,
2439 span: Span,
2440 ctx: &mut AnalysisContext,
2441 ) -> CompileResult<AnalysisResult> {
2442 use crate::sema::analyze_ops::ProjectionInfo;
2443
2444 if let Some(mut trace) = self.try_trace_place(base, air, ctx)? {
2446 if let Some(state) = ctx.moved_vars.get(&trace.root_var)
2448 && let Some(moved_span) = state.full_move
2449 {
2450 let root_name = self.interner.resolve(&trace.root_var);
2451 return Err(CompileError::new(
2452 ErrorKind::UseAfterMove(root_name.to_string()),
2453 span,
2454 )
2455 .with_label("value moved here", moved_span));
2456 }
2457
2458 let root_name = self.interner.resolve(&trace.root_var).to_string();
2460 if !trace.is_root_mutable {
2461 if trace.is_borrow_param {
2463 return Err(CompileError::new(
2464 ErrorKind::MutateBorrowedValue {
2465 variable: root_name,
2466 },
2467 span,
2468 ));
2469 }
2470
2471 let root_type = trace.base_type;
2472 match trace.base {
2474 AirPlaceBase::Param(_) => {
2475 return Err(CompileError::new(
2476 ErrorKind::AssignToImmutable(root_name.clone()),
2477 span,
2478 )
2479 .with_help(format!(
2480 "consider making parameter `{}` inout: `inout {}: {}`",
2481 root_name,
2482 root_name,
2483 root_type.name()
2484 )));
2485 }
2486 AirPlaceBase::Local(_) => {
2487 return Err(CompileError::new(
2488 ErrorKind::AssignToImmutable(root_name),
2489 span,
2490 ));
2491 }
2492 }
2493 }
2494
2495 let base_type = trace.result_type();
2497 let struct_id = match base_type.as_struct() {
2498 Some(id) => id,
2499 None => {
2500 return Err(CompileError::new(
2501 ErrorKind::FieldAccessOnNonStruct {
2502 found: base_type.name().to_string(),
2503 },
2504 span,
2505 ));
2506 }
2507 };
2508
2509 let struct_def = self.type_pool.struct_def(struct_id);
2510 let field_name_str = self.interner.resolve(&field).to_string();
2511
2512 let (field_index, struct_field) =
2513 struct_def.find_field(&field_name_str).ok_or_compile_error(
2514 ErrorKind::UnknownField {
2515 struct_name: struct_def.name.clone(),
2516 field_name: field_name_str.clone(),
2517 },
2518 span,
2519 )?;
2520
2521 let field_type = struct_field.ty;
2522
2523 trace.projections.push(ProjectionInfo {
2525 proj: AirProjection::Field {
2526 struct_id,
2527 field_index: field_index as u32,
2528 },
2529 result_type: field_type,
2530 field_name: Some(field),
2531 });
2532
2533 let value_result = self.analyze_inst(air, value, ctx)?;
2535
2536 let place_ref = Self::build_place_ref(air, &trace);
2538 let air_ref = air.add_inst(AirInst {
2539 data: AirInstData::PlaceWrite {
2540 place: place_ref,
2541 value: value_result.air_ref,
2542 },
2543 ty: Type::UNIT,
2544 span,
2545 });
2546 return Ok(AnalysisResult::new(air_ref, Type::UNIT));
2547 }
2548
2549 Err(CompileError::new(ErrorKind::InvalidAssignmentTarget, span))
2552 }
2553
2554 pub(crate) fn analyze_index_set_impl(
2556 &mut self,
2557 air: &mut Air,
2558 base: InstRef,
2559 index: InstRef,
2560 value: InstRef,
2561 span: Span,
2562 ctx: &mut AnalysisContext,
2563 ) -> CompileResult<AnalysisResult> {
2564 use crate::sema::analyze_ops::ProjectionInfo;
2565
2566 if let Some(mut trace) = self.try_trace_place(base, air, ctx)? {
2568 if let Some(state) = ctx.moved_vars.get(&trace.root_var)
2570 && let Some(moved_span) = state.full_move
2571 {
2572 let root_name = self.interner.resolve(&trace.root_var);
2573 return Err(CompileError::new(
2574 ErrorKind::UseAfterMove(root_name.to_string()),
2575 span,
2576 )
2577 .with_label("value moved here", moved_span));
2578 }
2579
2580 let root_name = self.interner.resolve(&trace.root_var).to_string();
2582 if !trace.is_root_mutable {
2583 if trace.is_borrow_param {
2585 return Err(CompileError::new(
2586 ErrorKind::MutateBorrowedValue {
2587 variable: root_name,
2588 },
2589 span,
2590 ));
2591 }
2592
2593 let root_type = trace.base_type;
2594 match trace.base {
2595 AirPlaceBase::Param(_) => {
2596 return Err(CompileError::new(
2597 ErrorKind::AssignToImmutable(root_name.clone()),
2598 span,
2599 )
2600 .with_help(format!(
2601 "consider making parameter `{}` inout: `inout {}: {}`",
2602 root_name,
2603 root_name,
2604 root_type.name()
2605 )));
2606 }
2607 AirPlaceBase::Local(_) => {
2608 return Err(CompileError::new(
2609 ErrorKind::AssignToImmutable(root_name),
2610 span,
2611 ));
2612 }
2613 }
2614 }
2615
2616 let base_type = trace.result_type();
2618 let (_array_type_id, elem_type, array_len) = match base_type.as_array() {
2619 Some(id) => {
2620 let (elem, len) = self.type_pool.array_def(id);
2621 (id, elem, len)
2622 }
2623 None => {
2624 return Err(CompileError::new(
2625 ErrorKind::IndexOnNonArray {
2626 found: base_type.name().to_string(),
2627 },
2628 span,
2629 ));
2630 }
2631 };
2632
2633 let index_result = self.analyze_inst(air, index, ctx)?;
2635 if !index_result.ty.is_unsigned() && !index_result.ty.is_error() {
2636 return Err(CompileError::new(
2637 ErrorKind::TypeMismatch {
2638 expected: "unsigned integer type".to_string(),
2639 found: index_result.ty.name().to_string(),
2640 },
2641 self.rir.get(index).span,
2642 ));
2643 }
2644
2645 if let Some(const_index) = self.try_get_const_index(index)
2647 && (const_index < 0 || const_index as u64 >= array_len)
2648 {
2649 return Err(CompileError::new(
2650 ErrorKind::IndexOutOfBounds {
2651 index: const_index,
2652 length: array_len,
2653 },
2654 self.rir.get(index).span,
2655 ));
2656 }
2657
2658 trace.projections.push(ProjectionInfo {
2660 proj: AirProjection::Index {
2661 array_type: base_type,
2662 index: index_result.air_ref,
2663 },
2664 result_type: elem_type,
2665 field_name: None,
2666 });
2667
2668 let value_result = self.analyze_inst(air, value, ctx)?;
2670
2671 let place_ref = Self::build_place_ref(air, &trace);
2673 let air_ref = air.add_inst(AirInst {
2674 data: AirInstData::PlaceWrite {
2675 place: place_ref,
2676 value: value_result.air_ref,
2677 },
2678 ty: Type::UNIT,
2679 span,
2680 });
2681 return Ok(AnalysisResult::new(air_ref, Type::UNIT));
2682 }
2683
2684 Err(CompileError::new(ErrorKind::InvalidAssignmentTarget, span))
2686 }
2687
2688 pub(crate) fn analyze_method_call_impl(
2690 &mut self,
2691 air: &mut Air,
2692 receiver: InstRef,
2693 method: Spur,
2694 args: Vec<RirCallArg>,
2695 span: Span,
2696 ctx: &mut AnalysisContext,
2697 ) -> CompileResult<AnalysisResult> {
2698 let receiver_var = self.extract_root_variable(receiver);
2699 let method_name_str = self.interner.resolve(&method).to_string();
2700
2701 let is_builtin_mutation_method = self.is_builtin_mutation_method(&method_name_str);
2703
2704 let receiver_storage = if is_builtin_mutation_method {
2706 self.get_string_receiver_storage(receiver, ctx, span)?
2707 } else {
2708 None
2709 };
2710
2711 let receiver_result = self.analyze_inst(air, receiver, ctx)?;
2713 let receiver_type = receiver_result.ty;
2714
2715 if receiver_type.is_module() {
2717 return self.analyze_module_member_call_impl(air, method, args, span, ctx);
2718 }
2719
2720 if let TypeKind::Enum(enum_id) = receiver_type.kind() {
2723 let enum_def = self.type_pool.enum_def(enum_id);
2724 let enum_name_str = enum_def.name.clone();
2725
2726 let method_key = (enum_id, method);
2727 let method_info = self.enum_methods.get(&method_key).ok_or_compile_error(
2728 ErrorKind::UndefinedMethod {
2729 type_name: enum_name_str.clone(),
2730 method_name: method_name_str.clone(),
2731 },
2732 span,
2733 )?;
2734
2735 if !method_info.has_self {
2736 return Err(CompileError::new(
2737 ErrorKind::AssocFnCalledAsMethod {
2738 type_name: enum_name_str,
2739 function_name: method_name_str,
2740 },
2741 span,
2742 ));
2743 }
2744
2745 let method_param_types = self.param_arena.types(method_info.params);
2746 if args.len() != method_param_types.len() {
2747 return Err(CompileError::new(
2748 ErrorKind::WrongArgumentCount {
2749 expected: method_param_types.len(),
2750 found: args.len(),
2751 },
2752 span,
2753 ));
2754 }
2755
2756 self.check_exclusive_access(&args, span)?;
2757
2758 let return_type = method_info.return_type;
2759
2760 let mut air_args = vec![AirCallArg {
2761 value: receiver_result.air_ref,
2762 mode: AirArgMode::Normal,
2763 }];
2764 air_args.extend(self.analyze_call_args(air, &args, ctx)?);
2765
2766 let call_name = format!("{}.{}", enum_name_str, method_name_str);
2767 let call_name_sym = self.interner.get_or_intern(&call_name);
2768
2769 let args_len = air_args.len() as u32;
2770 let mut extra_data = Vec::with_capacity(air_args.len() * 2);
2771 for arg in &air_args {
2772 extra_data.push(arg.value.as_u32());
2773 extra_data.push(arg.mode.as_u32());
2774 }
2775 let args_start = air.add_extra(&extra_data);
2776
2777 let air_ref = air.add_inst(AirInst {
2778 data: AirInstData::Call {
2779 name: call_name_sym,
2780 args_start,
2781 args_len,
2782 },
2783 ty: return_type,
2784 span,
2785 });
2786 return Ok(AnalysisResult::new(air_ref, return_type));
2787 }
2788
2789 let struct_id = match receiver_type.kind() {
2790 TypeKind::Struct(id) => id,
2791 _ => {
2792 return Err(CompileError::new(
2793 ErrorKind::MethodCallOnNonStruct {
2794 found: receiver_type.name().to_string(),
2795 method_name: method_name_str,
2796 },
2797 span,
2798 ));
2799 }
2800 };
2801
2802 if let Some(builtin_def) = self.get_builtin_type_def(struct_id) {
2804 let method_ctx = BuiltinMethodContext {
2805 struct_id,
2806 builtin_def,
2807 method_name: &method_name_str,
2808 span,
2809 };
2810 let receiver_info = ReceiverInfo {
2811 result: receiver_result,
2812 var: receiver_var,
2813 storage: receiver_storage,
2814 };
2815 return self.analyze_builtin_method(air, ctx, &method_ctx, receiver_info, &args);
2816 }
2817
2818 let struct_def = self.type_pool.struct_def(struct_id);
2820 let struct_name_str = struct_def.name.clone();
2821
2822 let method_key = (struct_id, method);
2824 let method_info = self.methods.get(&method_key).ok_or_compile_error(
2825 ErrorKind::UndefinedMethod {
2826 type_name: struct_name_str.clone(),
2827 method_name: method_name_str.clone(),
2828 },
2829 span,
2830 )?;
2831
2832 if !method_info.has_self {
2834 return Err(CompileError::new(
2835 ErrorKind::AssocFnCalledAsMethod {
2836 type_name: struct_name_str,
2837 function_name: method_name_str,
2838 },
2839 span,
2840 ));
2841 }
2842
2843 if method_info.is_unchecked && ctx.checked_depth == 0 {
2845 return Err(CompileError::new(
2846 ErrorKind::UncheckedCallRequiresChecked(format!(
2847 "{}.{}",
2848 struct_name_str, method_name_str
2849 )),
2850 span,
2851 ));
2852 }
2853
2854 let method_param_types = self.param_arena.types(method_info.params);
2856 if args.len() != method_param_types.len() {
2857 return Err(CompileError::new(
2858 ErrorKind::WrongArgumentCount {
2859 expected: method_param_types.len(),
2860 found: args.len(),
2861 },
2862 span,
2863 ));
2864 }
2865
2866 self.check_exclusive_access(&args, span)?;
2868
2869 let return_type = method_info.return_type;
2871
2872 let mut air_args = vec![AirCallArg {
2874 value: receiver_result.air_ref,
2875 mode: AirArgMode::Normal,
2876 }];
2877 air_args.extend(self.analyze_call_args(air, &args, ctx)?);
2878
2879 let call_name = format!("{}.{}", struct_name_str, method_name_str);
2881 let call_name_sym = self.interner.get_or_intern(&call_name);
2882
2883 let args_len = air_args.len() as u32;
2885 let mut extra_data = Vec::with_capacity(air_args.len() * 2);
2886 for arg in &air_args {
2887 extra_data.push(arg.value.as_u32());
2888 extra_data.push(arg.mode.as_u32());
2889 }
2890 let args_start = air.add_extra(&extra_data);
2891
2892 let air_ref = air.add_inst(AirInst {
2893 data: AirInstData::Call {
2894 name: call_name_sym,
2895 args_start,
2896 args_len,
2897 },
2898 ty: return_type,
2899 span,
2900 });
2901 Ok(AnalysisResult::new(air_ref, return_type))
2902 }
2903
2904 fn analyze_module_member_call_impl(
2911 &mut self,
2912 air: &mut Air,
2913 function_name: Spur,
2914 args: Vec<RirCallArg>,
2915 span: Span,
2916 ctx: &mut AnalysisContext,
2917 ) -> CompileResult<AnalysisResult> {
2918 let fn_name_str = self.interner.resolve(&function_name).to_string();
2920 let fn_info = *self
2921 .functions
2922 .get(&function_name)
2923 .ok_or_compile_error(ErrorKind::UndefinedFunction(fn_name_str.clone()), span)?;
2924
2925 ctx.referenced_functions.insert(function_name);
2927
2928 let accessing_file_id = span.file_id;
2930 let target_file_id = fn_info.file_id;
2931 if !self.is_accessible(accessing_file_id, target_file_id, fn_info.is_pub) {
2932 return Err(CompileError::new(
2933 ErrorKind::PrivateMemberAccess {
2934 item_kind: "function".to_string(),
2935 name: fn_name_str,
2936 },
2937 span,
2938 ));
2939 }
2940
2941 let param_types = self.param_arena.types(fn_info.params);
2943 let param_modes = self.param_arena.modes(fn_info.params);
2944
2945 if args.len() != param_types.len() {
2947 let expected = param_types.len();
2948 let found = args.len();
2949 return Err(CompileError::new(
2950 ErrorKind::WrongArgumentCount { expected, found },
2951 span,
2952 ));
2953 }
2954
2955 for (i, (arg, expected_mode)) in args.iter().zip(param_modes.iter()).enumerate() {
2957 match expected_mode {
2958 RirParamMode::Inout => {
2959 if arg.mode != RirArgMode::Inout {
2960 return Err(CompileError::new(
2961 ErrorKind::InoutKeywordMissing,
2962 self.rir.get(args[i].value).span,
2963 ));
2964 }
2965 }
2966 RirParamMode::Borrow => {
2967 if arg.mode != RirArgMode::Borrow {
2968 return Err(CompileError::new(
2969 ErrorKind::BorrowKeywordMissing,
2970 self.rir.get(args[i].value).span,
2971 ));
2972 }
2973 }
2974 RirParamMode::Normal => {
2975 }
2977 RirParamMode::Comptime => {
2978 }
2980 }
2981 }
2982
2983 let air_args = self.analyze_call_args(air, &args, ctx)?;
2985
2986 let return_type = fn_info.return_type;
2987
2988 let mut extra_data = Vec::with_capacity(air_args.len() * 2);
2990 for arg in &air_args {
2991 extra_data.push(arg.value.as_u32());
2992 extra_data.push(arg.mode.as_u32());
2993 }
2994 let call_args_start = air.add_extra(&extra_data);
2995 let call_args_len = air_args.len() as u32;
2996
2997 let air_ref = air.add_inst(AirInst {
2998 data: AirInstData::Call {
2999 name: function_name,
3000 args_start: call_args_start,
3001 args_len: call_args_len,
3002 },
3003 ty: return_type,
3004 span,
3005 });
3006 Ok(AnalysisResult::new(air_ref, return_type))
3007 }
3008
3009 pub(crate) fn analyze_assoc_fn_call_impl(
3011 &mut self,
3012 air: &mut Air,
3013 type_name: Spur,
3014 function: Spur,
3015 args: Vec<RirCallArg>,
3016 span: Span,
3017 ctx: &mut AnalysisContext,
3018 ) -> CompileResult<AnalysisResult> {
3019 let type_name_str = self.interner.resolve(&type_name).to_string();
3020 let function_name_str = self.interner.resolve(&function).to_string();
3021
3022 if let Some(&enum_id) = self.enums.get(&type_name) {
3026 let enum_def = self.type_pool.enum_def(enum_id);
3027 if let Some(variant_index) = enum_def.find_variant(&function_name_str) {
3028 let variant_def = &enum_def.variants[variant_index];
3029 let field_types: Vec<Type> = variant_def.fields.clone();
3030 if !field_types.is_empty() {
3031 if variant_def.is_struct_variant() {
3033 return Err(CompileError::new(
3034 ErrorKind::TypeMismatch {
3035 expected: format!(
3036 "struct-style construction `{}::{} {{ ... }}`",
3037 type_name_str, function_name_str
3038 ),
3039 found: format!(
3040 "tuple-style construction `{}::{}(...)`",
3041 type_name_str, function_name_str
3042 ),
3043 },
3044 span,
3045 ));
3046 }
3047
3048 if args.len() != field_types.len() {
3050 return Err(CompileError::new(
3051 ErrorKind::WrongArgumentCount {
3052 expected: field_types.len(),
3053 found: args.len(),
3054 },
3055 span,
3056 ));
3057 }
3058
3059 let mut field_air_refs = Vec::with_capacity(args.len());
3061 for (i, arg) in args.iter().enumerate() {
3062 let result = self.analyze_inst(air, arg.value, ctx)?;
3063 if result.ty != field_types[i] {
3064 return Err(CompileError::new(
3065 ErrorKind::TypeMismatch {
3066 expected: field_types[i].name().to_string(),
3067 found: result.ty.name().to_string(),
3068 },
3069 span,
3070 ));
3071 }
3072 field_air_refs.push(result.air_ref.as_u32());
3073 }
3074
3075 let fields_len = field_air_refs.len() as u32;
3077 let fields_start = air.add_extra(&field_air_refs);
3078
3079 let enum_type = Type::new_enum(enum_id);
3080 let air_ref = air.add_inst(AirInst {
3081 data: AirInstData::EnumCreate {
3082 enum_id,
3083 variant_index: variant_index as u32,
3084 fields_start,
3085 fields_len,
3086 },
3087 ty: enum_type,
3088 span,
3089 });
3090 return Ok(AnalysisResult::new(air_ref, enum_type));
3091 }
3092 }
3094 }
3095
3096 if let Some(&ty) = ctx.comptime_type_vars.get(&type_name)
3099 && let TypeKind::Enum(enum_id) = ty.kind()
3100 {
3101 let enum_def = self.type_pool.enum_def(enum_id);
3102 if let Some(variant_index) = enum_def.find_variant(&function_name_str) {
3103 let variant_def = &enum_def.variants[variant_index];
3104 let field_types: Vec<Type> = variant_def.fields.clone();
3105 if !field_types.is_empty() {
3106 if variant_def.is_struct_variant() {
3107 return Err(CompileError::new(
3108 ErrorKind::TypeMismatch {
3109 expected: format!(
3110 "struct-style construction `{}::{} {{ ... }}`",
3111 type_name_str, function_name_str
3112 ),
3113 found: format!(
3114 "tuple-style construction `{}::{}(...)`",
3115 type_name_str, function_name_str
3116 ),
3117 },
3118 span,
3119 ));
3120 }
3121 if args.len() != field_types.len() {
3122 return Err(CompileError::new(
3123 ErrorKind::WrongArgumentCount {
3124 expected: field_types.len(),
3125 found: args.len(),
3126 },
3127 span,
3128 ));
3129 }
3130 let mut field_air_refs = Vec::with_capacity(args.len());
3131 for (i, arg) in args.iter().enumerate() {
3132 let result = self.analyze_inst(air, arg.value, ctx)?;
3133 if result.ty != field_types[i] {
3134 return Err(CompileError::new(
3135 ErrorKind::TypeMismatch {
3136 expected: field_types[i].name().to_string(),
3137 found: result.ty.name().to_string(),
3138 },
3139 span,
3140 ));
3141 }
3142 field_air_refs.push(result.air_ref.as_u32());
3143 }
3144 let fields_len = field_air_refs.len() as u32;
3145 let fields_start = air.add_extra(&field_air_refs);
3146 let enum_type = Type::new_enum(enum_id);
3147 let air_ref = air.add_inst(AirInst {
3148 data: AirInstData::EnumCreate {
3149 enum_id,
3150 variant_index: variant_index as u32,
3151 fields_start,
3152 fields_len,
3153 },
3154 ty: enum_type,
3155 span,
3156 });
3157 return Ok(AnalysisResult::new(air_ref, enum_type));
3158 }
3159 }
3161
3162 let method_key = (enum_id, function);
3164 if let Some(method_info) = self.enum_methods.get(&method_key).copied() {
3165 ctx.referenced_methods
3166 .insert((StructId(enum_id.0), function));
3167
3168 if method_info.has_self {
3169 return Err(CompileError::new(
3170 ErrorKind::MethodCalledAsAssocFn {
3171 type_name: type_name_str,
3172 method_name: function_name_str,
3173 },
3174 span,
3175 ));
3176 }
3177
3178 let method_param_types: Vec<Type> =
3179 self.param_arena.types(method_info.params).to_vec();
3180 if args.len() != method_param_types.len() {
3181 return Err(CompileError::new(
3182 ErrorKind::WrongArgumentCount {
3183 expected: method_param_types.len(),
3184 found: args.len(),
3185 },
3186 span,
3187 ));
3188 }
3189
3190 let mut extra_data = Vec::with_capacity(args.len() * 2);
3191 for (i, arg) in args.iter().enumerate() {
3192 let result = self.analyze_inst(air, arg.value, ctx)?;
3193 if result.ty != method_param_types[i] {
3194 return Err(CompileError::new(
3195 ErrorKind::TypeMismatch {
3196 expected: method_param_types[i].name().to_string(),
3197 found: result.ty.name().to_string(),
3198 },
3199 span,
3200 ));
3201 }
3202 extra_data.push(result.air_ref.as_u32());
3203 extra_data.push(AirArgMode::Normal.as_u32());
3204 }
3205
3206 let enum_def = self.type_pool.enum_def(enum_id);
3207 let type_name_str2 = enum_def.name.clone();
3208 let full_name = format!("{}::{}", type_name_str2, function_name_str);
3209 let callee_sym = self.interner.get_or_intern(&full_name);
3210
3211 let args_start = air.add_extra(&extra_data);
3212 let args_len = args.len() as u32;
3213 let air_ref = air.add_inst(AirInst {
3214 data: AirInstData::Call {
3215 name: callee_sym,
3216 args_start,
3217 args_len,
3218 },
3219 ty: method_info.return_type,
3220 span,
3221 });
3222 return Ok(AnalysisResult::new(air_ref, method_info.return_type));
3223 }
3224 }
3225
3226 let struct_id = if let Some(&ty) = ctx.comptime_type_vars.get(&type_name) {
3229 match ty.kind() {
3231 TypeKind::Struct(id) => id,
3232 _ => {
3233 return Err(CompileError::new(
3234 ErrorKind::TypeMismatch {
3235 expected: "struct type".to_string(),
3236 found: ty.name().to_string(),
3237 },
3238 span,
3239 ));
3240 }
3241 }
3242 } else {
3243 *self
3244 .structs
3245 .get(&type_name)
3246 .ok_or_compile_error(ErrorKind::UnknownType(type_name_str.clone()), span)?
3247 };
3248
3249 if let Some(builtin_def) = self.get_builtin_type_def(struct_id) {
3251 return self.analyze_builtin_assoc_fn(
3252 air,
3253 ctx,
3254 (struct_id, builtin_def),
3255 &function_name_str,
3256 &args,
3257 span,
3258 );
3259 }
3260
3261 let method_key = (struct_id, function);
3263 let method_info = self.methods.get(&method_key).ok_or_compile_error(
3264 ErrorKind::UndefinedAssocFn {
3265 type_name: type_name_str.clone(),
3266 function_name: function_name_str.clone(),
3267 },
3268 span,
3269 )?;
3270
3271 ctx.referenced_methods.insert(method_key);
3273
3274 if method_info.has_self {
3276 return Err(CompileError::new(
3277 ErrorKind::MethodCalledAsAssocFn {
3278 type_name: type_name_str,
3279 method_name: function_name_str,
3280 },
3281 span,
3282 ));
3283 }
3284
3285 if method_info.is_unchecked && ctx.checked_depth == 0 {
3287 return Err(CompileError::new(
3288 ErrorKind::UncheckedCallRequiresChecked(format!(
3289 "{}::{}",
3290 type_name_str, function_name_str
3291 )),
3292 span,
3293 ));
3294 }
3295
3296 let method_param_types = self.param_arena.types(method_info.params);
3298 if args.len() != method_param_types.len() {
3299 return Err(CompileError::new(
3300 ErrorKind::WrongArgumentCount {
3301 expected: method_param_types.len(),
3302 found: args.len(),
3303 },
3304 span,
3305 ));
3306 }
3307
3308 self.check_exclusive_access(&args, span)?;
3310
3311 let return_type = method_info.return_type;
3313
3314 let air_args = self.analyze_call_args(air, &args, ctx)?;
3316
3317 let struct_def = self.type_pool.struct_def(struct_id);
3321 let internal_type_name = &struct_def.name;
3322 let call_name = format!("{}::{}", internal_type_name, function_name_str);
3323 let call_name_sym = self.interner.get_or_intern(&call_name);
3324
3325 let args_len = air_args.len() as u32;
3327 let mut extra_data = Vec::with_capacity(air_args.len() * 2);
3328 for arg in &air_args {
3329 extra_data.push(arg.value.as_u32());
3330 extra_data.push(arg.mode.as_u32());
3331 }
3332 let args_start = air.add_extra(&extra_data);
3333
3334 let air_ref = air.add_inst(AirInst {
3335 data: AirInstData::Call {
3336 name: call_name_sym,
3337 args_start,
3338 args_len,
3339 },
3340 ty: return_type,
3341 span,
3342 });
3343 Ok(AnalysisResult::new(air_ref, return_type))
3344 }
3345
3346 pub(crate) fn analyze_intrinsic_impl(
3348 &mut self,
3349 air: &mut Air,
3350 inst_ref: InstRef,
3351 name: Spur,
3352 args: Vec<RirCallArg>,
3353 span: Span,
3354 ctx: &mut AnalysisContext,
3355 ) -> CompileResult<AnalysisResult> {
3356 let known = &self.known;
3357
3358 if name == known.dbg {
3360 self.analyze_dbg_intrinsic(air, inst_ref, &args, span, ctx)
3361 } else if name == known.int_cast {
3362 self.analyze_intcast_intrinsic(air, inst_ref, &args, span, ctx)
3363 } else if name == known.test_preview_gate {
3364 self.analyze_test_preview_gate_intrinsic(air, &args, span)
3365 } else if name == known.read_line {
3366 self.analyze_read_line_intrinsic(air, name, &args, span)
3367 } else if let Some(intrinsic_name_str) = known.get_parse_intrinsic_name(name) {
3368 self.analyze_parse_intrinsic(air, name, intrinsic_name_str, &args, span, ctx)
3369 } else if name == known.cast {
3370 self.analyze_cast_intrinsic(air, inst_ref, &args, span, ctx)
3371 } else if name == known.panic {
3372 self.analyze_panic_intrinsic(air, &args, span, ctx)
3373 } else if name == known.assert {
3374 self.analyze_assert_intrinsic(air, &args, span, ctx)
3375 } else if name == known.import {
3376 self.analyze_import_intrinsic(air, &args, span)
3377 } else if name == known.random_u32 {
3378 self.analyze_random_u32_intrinsic(air, name, &args, span)
3379 } else if name == known.random_u64 {
3380 self.analyze_random_u64_intrinsic(air, name, &args, span)
3381 } else if name == known.ptr_read {
3382 Self::require_checked_for_intrinsic(ctx, "ptr_read", span)?;
3383 self.analyze_ptr_read_intrinsic(air, name, &args, span, ctx)
3384 } else if name == known.ptr_write {
3385 Self::require_checked_for_intrinsic(ctx, "ptr_write", span)?;
3386 self.analyze_ptr_write_intrinsic(air, name, &args, span, ctx)
3387 } else if name == known.ptr_offset {
3388 Self::require_checked_for_intrinsic(ctx, "ptr_offset", span)?;
3389 self.analyze_ptr_offset_intrinsic(air, name, &args, span, ctx)
3390 } else if name == known.ptr_to_int {
3391 Self::require_checked_for_intrinsic(ctx, "ptr_to_int", span)?;
3392 self.analyze_ptr_to_int_intrinsic(air, name, &args, span, ctx)
3393 } else if name == known.int_to_ptr {
3394 Self::require_checked_for_intrinsic(ctx, "int_to_ptr", span)?;
3395 self.analyze_int_to_ptr_intrinsic(air, name, inst_ref, &args, span, ctx)
3396 } else if name == known.null_ptr {
3397 Self::require_checked_for_intrinsic(ctx, "null_ptr", span)?;
3398 self.analyze_null_ptr_intrinsic(air, name, inst_ref, &args, span, ctx)
3399 } else if name == known.is_null {
3400 Self::require_checked_for_intrinsic(ctx, "is_null", span)?;
3401 self.analyze_is_null_intrinsic(air, name, &args, span, ctx)
3402 } else if name == known.ptr_copy {
3403 Self::require_checked_for_intrinsic(ctx, "ptr_copy", span)?;
3404 self.analyze_ptr_copy_intrinsic(air, name, &args, span, ctx)
3405 } else if name == known.raw {
3406 Self::require_checked_for_intrinsic(ctx, "raw", span)?;
3407 self.analyze_addr_of_intrinsic(air, &args, span, ctx, false)
3408 } else if name == known.raw_mut {
3409 Self::require_checked_for_intrinsic(ctx, "raw_mut", span)?;
3410 self.analyze_addr_of_intrinsic(air, &args, span, ctx, true)
3411 } else if name == known.syscall {
3412 Self::require_checked_for_intrinsic(ctx, "syscall", span)?;
3413 self.analyze_syscall_intrinsic(air, name, &args, span, ctx)
3414 } else if name == known.target_arch {
3415 self.analyze_target_arch_intrinsic(air, &args, span)
3416 } else if name == known.target_os {
3417 self.analyze_target_os_intrinsic(air, &args, span)
3418 } else if name == known.compile_error {
3419 self.analyze_compile_error_intrinsic(air, &args, span)
3420 } else if name == known.field {
3421 self.analyze_field_intrinsic(air, &args, span, ctx)
3422 } else {
3423 let intrinsic_name_str = self.interner.resolve(&name);
3425 Err(CompileError::new(
3426 ErrorKind::UnknownIntrinsic(intrinsic_name_str.to_string()),
3427 span,
3428 ))
3429 }
3430 }
3431
3432 fn analyze_dbg_intrinsic(
3435 &mut self,
3436 air: &mut Air,
3437 _inst_ref: InstRef,
3438 args: &[RirCallArg],
3439 span: Span,
3440 ctx: &mut AnalysisContext,
3441 ) -> CompileResult<AnalysisResult> {
3442 let mut arg_air_refs = Vec::with_capacity(args.len());
3443 for arg in args {
3444 let arg_result = self.analyze_inst(air, arg.value, ctx)?;
3445 let arg_type = arg_result.ty;
3446
3447 if !arg_type.is_integer()
3453 && arg_type != Type::BOOL
3454 && !arg_type.is_struct()
3455 && !arg_type.is_enum()
3456 && !arg_type.is_array()
3457 && !arg_type.is_error()
3458 && !arg_type.is_never()
3459 {
3460 return Err(CompileError::new(
3461 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
3462 name: "dbg".to_string(),
3463 expected: "integer, bool, or string".to_string(),
3464 found: arg_type.name().to_string(),
3465 })),
3466 span,
3467 ));
3468 }
3469
3470 arg_air_refs.push(arg_result.air_ref.as_u32());
3471 }
3472
3473 let args_len = arg_air_refs.len() as u32;
3474 let args_start = air.add_extra(&arg_air_refs);
3475 let air_ref = air.add_inst(AirInst {
3476 data: AirInstData::Intrinsic {
3477 name: self.known.dbg,
3478 args_start,
3479 args_len,
3480 },
3481 ty: Type::UNIT,
3482 span,
3483 });
3484 Ok(AnalysisResult::new(air_ref, Type::UNIT))
3485 }
3486
3487 fn analyze_cast_intrinsic(
3488 &mut self,
3489 air: &mut Air,
3490 inst_ref: InstRef,
3491 args: &[RirCallArg],
3492 span: Span,
3493 ctx: &mut AnalysisContext,
3494 ) -> CompileResult<AnalysisResult> {
3495 if args.len() != 1 {
3496 return Err(CompileError::new(
3497 ErrorKind::IntrinsicWrongArgCount {
3498 name: "cast".to_string(),
3499 expected: 1,
3500 found: args.len(),
3501 },
3502 span,
3503 ));
3504 }
3505
3506 let target_type = Self::get_resolved_type(ctx, inst_ref, span, "@cast intrinsic")?;
3508
3509 let arg_result = self.analyze_inst(air, args[0].value, ctx)?;
3510 let source_type = arg_result.ty;
3511
3512 if !source_type.is_numeric() && !source_type.is_error() && !source_type.is_never() {
3514 return Err(CompileError::new(
3515 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
3516 name: "cast".to_string(),
3517 expected: "numeric type".to_string(),
3518 found: source_type.name().to_string(),
3519 })),
3520 span,
3521 ));
3522 }
3523 if !target_type.is_numeric() && !target_type.is_error() && !target_type.is_never() {
3525 return Err(CompileError::new(
3526 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
3527 name: "cast".to_string(),
3528 expected: "numeric target type".to_string(),
3529 found: target_type.name().to_string(),
3530 })),
3531 span,
3532 ));
3533 }
3534
3535 if source_type == target_type || source_type.is_error() || source_type.is_never() {
3537 return Ok(arg_result);
3538 }
3539
3540 let data = match (source_type.is_integer(), target_type.is_integer()) {
3542 (true, true) => AirInstData::IntCast {
3543 value: arg_result.air_ref,
3544 from_ty: source_type,
3545 },
3546 (true, false) => AirInstData::IntToFloat {
3547 value: arg_result.air_ref,
3548 from_ty: source_type,
3549 },
3550 (false, true) => AirInstData::FloatToInt {
3551 value: arg_result.air_ref,
3552 from_ty: source_type,
3553 },
3554 (false, false) => AirInstData::FloatCast {
3555 value: arg_result.air_ref,
3556 from_ty: source_type,
3557 },
3558 };
3559
3560 let air_ref = air.add_inst(AirInst {
3561 data,
3562 ty: target_type,
3563 span,
3564 });
3565 Ok(AnalysisResult::new(air_ref, target_type))
3566 }
3567
3568 fn analyze_panic_intrinsic(
3569 &mut self,
3570 air: &mut Air,
3571 args: &[RirCallArg],
3572 span: Span,
3573 ctx: &mut AnalysisContext,
3574 ) -> CompileResult<AnalysisResult> {
3575 if args.len() > 1 {
3577 return Err(CompileError::new(
3578 ErrorKind::IntrinsicWrongArgCount {
3579 name: "panic".to_string(),
3580 expected: 1,
3581 found: args.len(),
3582 },
3583 span,
3584 ));
3585 }
3586
3587 if args.is_empty() {
3588 let air_ref = air.add_inst(AirInst {
3590 data: AirInstData::UnitConst,
3591 ty: Type::NEVER,
3592 span,
3593 });
3594 return Ok(AnalysisResult::new(air_ref, Type::NEVER));
3595 }
3596
3597 let arg_result = self.analyze_inst(air, args[0].value, ctx)?;
3599
3600 let args_start = air.add_extra(&[arg_result.air_ref.as_u32()]);
3601 let air_ref = air.add_inst(AirInst {
3602 data: AirInstData::Intrinsic {
3603 name: self.known.panic,
3604 args_start,
3605 args_len: 1,
3606 },
3607 ty: Type::NEVER,
3608 span,
3609 });
3610 Ok(AnalysisResult::new(air_ref, Type::NEVER))
3611 }
3612
3613 fn analyze_assert_intrinsic(
3614 &mut self,
3615 air: &mut Air,
3616 args: &[RirCallArg],
3617 span: Span,
3618 ctx: &mut AnalysisContext,
3619 ) -> CompileResult<AnalysisResult> {
3620 if args.is_empty() || args.len() > 2 {
3622 return Err(CompileError::new(
3623 ErrorKind::IntrinsicWrongArgCount {
3624 name: "assert".to_string(),
3625 expected: 1,
3626 found: args.len(),
3627 },
3628 span,
3629 ));
3630 }
3631
3632 let cond_result = self.analyze_inst(air, args[0].value, ctx)?;
3633
3634 let mut extra_data = vec![cond_result.air_ref.as_u32()];
3636 if args.len() > 1 {
3637 let msg_result = self.analyze_inst(air, args[1].value, ctx)?;
3638 extra_data.push(msg_result.air_ref.as_u32());
3639 }
3640
3641 let args_len = extra_data.len() as u32;
3642 let args_start = air.add_extra(&extra_data);
3643 let air_ref = air.add_inst(AirInst {
3644 data: AirInstData::Intrinsic {
3645 name: self.known.assert,
3646 args_start,
3647 args_len,
3648 },
3649 ty: Type::UNIT,
3650 span,
3651 });
3652 Ok(AnalysisResult::new(air_ref, Type::UNIT))
3653 }
3654
3655 fn analyze_intcast_intrinsic(
3657 &mut self,
3658 air: &mut Air,
3659 inst_ref: InstRef,
3660 args: &[RirCallArg],
3661 span: Span,
3662 ctx: &mut AnalysisContext,
3663 ) -> CompileResult<AnalysisResult> {
3664 let intrinsic_name = "intCast";
3665
3666 if args.len() != 1 {
3668 return Err(CompileError::new(
3669 ErrorKind::IntrinsicWrongArgCount {
3670 name: intrinsic_name.to_string(),
3671 expected: 1,
3672 found: args.len(),
3673 },
3674 span,
3675 ));
3676 }
3677
3678 let arg_result = self.analyze_inst(air, args[0].value, ctx)?;
3680 let from_ty = arg_result.ty;
3681
3682 if !from_ty.is_integer() {
3684 return Err(CompileError::new(
3685 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
3686 name: intrinsic_name.to_string(),
3687 expected: "integer".to_string(),
3688 found: from_ty.name().to_string(),
3689 })),
3690 span,
3691 ));
3692 }
3693
3694 let target_ty = match ctx.resolved_types.get(&inst_ref).copied() {
3696 Some(ty) if ty.is_integer() => ty,
3697 Some(Type::ERROR) => {
3698 return Err(CompileError::new(ErrorKind::TypeAnnotationRequired, span));
3700 }
3701 Some(ty) => {
3702 return Err(CompileError::new(
3703 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
3704 name: intrinsic_name.to_string(),
3705 expected: "integer".to_string(),
3706 found: ty.name().to_string(),
3707 })),
3708 span,
3709 ));
3710 }
3711 None => {
3712 return Err(CompileError::new(ErrorKind::TypeAnnotationRequired, span));
3714 }
3715 };
3716
3717 let air_ref = air.add_inst(AirInst {
3718 data: AirInstData::IntCast {
3719 value: arg_result.air_ref,
3720 from_ty,
3721 },
3722 ty: target_ty,
3723 span,
3724 });
3725 Ok(AnalysisResult::new(air_ref, target_ty))
3726 }
3727
3728 fn analyze_test_preview_gate_intrinsic(
3730 &mut self,
3731 air: &mut Air,
3732 args: &[RirCallArg],
3733 span: Span,
3734 ) -> CompileResult<AnalysisResult> {
3735 self.require_preview(
3737 PreviewFeature::TestInfra,
3738 "@test_preview_gate() intrinsic",
3739 span,
3740 )?;
3741
3742 if !args.is_empty() {
3744 return Err(CompileError::new(
3745 ErrorKind::IntrinsicWrongArgCount {
3746 name: "test_preview_gate".to_string(),
3747 expected: 0,
3748 found: args.len(),
3749 },
3750 span,
3751 ));
3752 }
3753
3754 let air_ref = air.add_inst(AirInst {
3756 data: AirInstData::UnitConst,
3757 ty: Type::UNIT,
3758 span,
3759 });
3760 Ok(AnalysisResult::new(air_ref, Type::UNIT))
3761 }
3762
3763 fn analyze_compile_error_intrinsic(
3769 &mut self,
3770 air: &mut Air,
3771 args: &[RirCallArg],
3772 span: Span,
3773 ) -> CompileResult<AnalysisResult> {
3774 if args.len() != 1 {
3775 return Err(CompileError::new(
3776 ErrorKind::IntrinsicWrongArgCount {
3777 name: "compileError".to_string(),
3778 expected: 1,
3779 found: args.len(),
3780 },
3781 span,
3782 ));
3783 }
3784
3785 let arg_inst = self.rir.get(args[0].value);
3787 if !matches!(&arg_inst.data, gruel_rir::InstData::StringConst(_)) {
3788 return Err(CompileError::new(
3789 ErrorKind::ComptimeEvaluationFailed {
3790 reason: "@compileError requires a string literal argument".into(),
3791 },
3792 arg_inst.span,
3793 ));
3794 }
3795
3796 let air_ref = air.add_inst(AirInst {
3798 data: AirInstData::UnitConst,
3799 ty: Type::NEVER,
3800 span,
3801 });
3802 Ok(AnalysisResult::new(air_ref, Type::NEVER))
3803 }
3804
3805 fn analyze_field_intrinsic(
3811 &mut self,
3812 air: &mut Air,
3813 args: &[RirCallArg],
3814 span: Span,
3815 ctx: &mut AnalysisContext,
3816 ) -> CompileResult<AnalysisResult> {
3817 if args.len() != 2 {
3818 return Err(CompileError::new(
3819 ErrorKind::IntrinsicWrongArgCount {
3820 name: "field".to_string(),
3821 expected: 2,
3822 found: args.len(),
3823 },
3824 span,
3825 ));
3826 }
3827
3828 let value_result = self.analyze_inst_for_projection(air, args[0].value, ctx)?;
3831 let struct_ty = value_result.ty;
3832
3833 let struct_id = match struct_ty.kind() {
3835 TypeKind::Struct(id) => id,
3836 _ => {
3837 return Err(CompileError::new(
3838 ErrorKind::ComptimeEvaluationFailed {
3839 reason: format!("@field requires a struct value, got {}", struct_ty.name()),
3840 },
3841 span,
3842 ));
3843 }
3844 };
3845
3846 let field_name_val = self.evaluate_comptime_expr(args[1].value, ctx, span)?;
3850 let field_name = match field_name_val {
3851 ConstValue::ComptimeStr(str_idx) => match &self.comptime_heap[str_idx as usize] {
3852 ComptimeHeapItem::String(s) => s.clone(),
3853 _ => {
3854 return Err(CompileError::new(
3855 ErrorKind::ComptimeEvaluationFailed {
3856 reason: "@field second argument must be a comptime_str".to_string(),
3857 },
3858 span,
3859 ));
3860 }
3861 },
3862 _ => {
3863 return Err(CompileError::new(
3864 ErrorKind::ComptimeEvaluationFailed {
3865 reason: "@field second argument must be a comptime_str".to_string(),
3866 },
3867 span,
3868 ));
3869 }
3870 };
3871
3872 let struct_def = self.type_pool.struct_def(struct_id);
3874 let (field_idx, field_def) = struct_def.find_field(&field_name).ok_or_else(|| {
3875 CompileError::new(
3876 ErrorKind::ComptimeEvaluationFailed {
3877 reason: format!("struct '{}' has no field '{}'", struct_def.name, field_name),
3878 },
3879 span,
3880 )
3881 })?;
3882 let field_ty = field_def.ty;
3883
3884 let air_ref = air.add_inst(AirInst {
3886 data: AirInstData::FieldGet {
3887 base: value_result.air_ref,
3888 struct_id,
3889 field_index: field_idx as u32,
3890 },
3891 ty: field_ty,
3892 span,
3893 });
3894 Ok(AnalysisResult::new(air_ref, field_ty))
3895 }
3896
3897 fn analyze_read_line_intrinsic(
3899 &mut self,
3900 air: &mut Air,
3901 name: Spur,
3902 args: &[RirCallArg],
3903 span: Span,
3904 ) -> CompileResult<AnalysisResult> {
3905 if !args.is_empty() {
3908 return Err(CompileError::new(
3909 ErrorKind::IntrinsicWrongArgCount {
3910 name: "read_line".to_string(),
3911 expected: 0,
3912 found: args.len(),
3913 },
3914 span,
3915 ));
3916 }
3917
3918 let string_type = self.builtin_string_type();
3920
3921 let air_ref = air.add_inst(AirInst {
3923 data: AirInstData::Intrinsic {
3924 name,
3925 args_start: 0, args_len: 0,
3927 },
3928 ty: string_type,
3929 span,
3930 });
3931 Ok(AnalysisResult::new(air_ref, string_type))
3932 }
3933
3934 fn analyze_parse_intrinsic(
3936 &mut self,
3937 air: &mut Air,
3938 name: Spur,
3939 intrinsic_name_str: &str,
3940 args: &[RirCallArg],
3941 span: Span,
3942 ctx: &mut AnalysisContext,
3943 ) -> CompileResult<AnalysisResult> {
3944 if args.len() != 1 {
3946 return Err(CompileError::new(
3947 ErrorKind::IntrinsicWrongArgCount {
3948 name: intrinsic_name_str.to_string(),
3949 expected: 1,
3950 found: args.len(),
3951 },
3952 span,
3953 ));
3954 }
3955
3956 let arg_result = self.analyze_inst_for_projection(air, args[0].value, ctx)?;
3959 let arg_type = arg_result.ty;
3960
3961 if !self.is_builtin_string(arg_type) {
3963 return Err(CompileError::new(
3964 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
3965 name: format!("@{}", intrinsic_name_str),
3966 expected: "String".to_string(),
3967 found: arg_type.name().to_string(),
3968 })),
3969 span,
3970 ));
3971 }
3972
3973 let return_type = match intrinsic_name_str {
3975 "parse_i32" => Type::I32,
3976 "parse_i64" => Type::I64,
3977 "parse_u32" => Type::U32,
3978 "parse_u64" => Type::U64,
3979 _ => unreachable!(),
3980 };
3981
3982 let args_start = air.add_extra(&[arg_result.air_ref.as_u32()]);
3984 let air_ref = air.add_inst(AirInst {
3985 data: AirInstData::Intrinsic {
3986 name,
3987 args_start,
3988 args_len: 1,
3989 },
3990 ty: return_type,
3991 span,
3992 });
3993 Ok(AnalysisResult::new(air_ref, return_type))
3994 }
3995
3996 fn analyze_random_u32_intrinsic(
3998 &mut self,
3999 air: &mut Air,
4000 name: Spur,
4001 args: &[RirCallArg],
4002 span: Span,
4003 ) -> CompileResult<AnalysisResult> {
4004 if !args.is_empty() {
4006 return Err(CompileError::new(
4007 ErrorKind::IntrinsicWrongArgCount {
4008 name: "random_u32".to_string(),
4009 expected: 0,
4010 found: args.len(),
4011 },
4012 span,
4013 ));
4014 }
4015
4016 let air_ref = air.add_inst(AirInst {
4018 data: AirInstData::Intrinsic {
4019 name,
4020 args_start: 0, args_len: 0,
4022 },
4023 ty: Type::U32,
4024 span,
4025 });
4026 Ok(AnalysisResult::new(air_ref, Type::U32))
4027 }
4028
4029 fn analyze_random_u64_intrinsic(
4031 &mut self,
4032 air: &mut Air,
4033 name: Spur,
4034 args: &[RirCallArg],
4035 span: Span,
4036 ) -> CompileResult<AnalysisResult> {
4037 if !args.is_empty() {
4039 return Err(CompileError::new(
4040 ErrorKind::IntrinsicWrongArgCount {
4041 name: "random_u64".to_string(),
4042 expected: 0,
4043 found: args.len(),
4044 },
4045 span,
4046 ));
4047 }
4048
4049 let air_ref = air.add_inst(AirInst {
4051 data: AirInstData::Intrinsic {
4052 name,
4053 args_start: 0, args_len: 0,
4055 },
4056 ty: Type::U64,
4057 span,
4058 });
4059 Ok(AnalysisResult::new(air_ref, Type::U64))
4060 }
4061
4062 fn analyze_import_intrinsic(
4067 &mut self,
4068 air: &mut Air,
4069 args: &[RirCallArg],
4070 span: Span,
4071 ) -> CompileResult<AnalysisResult> {
4072 if args.len() != 1 {
4074 return Err(CompileError::new(
4075 ErrorKind::IntrinsicWrongArgCount {
4076 name: "import".to_string(),
4077 expected: 1,
4078 found: args.len(),
4079 },
4080 span,
4081 ));
4082 }
4083
4084 let arg_inst = self.rir.get(args[0].value);
4086 let import_path = match &arg_inst.data {
4087 gruel_rir::InstData::StringConst(path_spur) => {
4088 self.interner.resolve(path_spur).to_string()
4089 }
4090 _ => {
4091 return Err(CompileError::new(
4092 ErrorKind::ImportRequiresStringLiteral,
4093 arg_inst.span,
4094 ));
4095 }
4096 };
4097
4098 let resolved_path = self.resolve_import_path(&import_path, span)?;
4104
4105 let (module_id, _is_new) = self
4108 .module_registry
4109 .get_or_create(import_path.clone(), resolved_path);
4110
4111 let air_ref = air.add_inst(AirInst {
4115 data: AirInstData::UnitConst, ty: Type::new_module(module_id),
4117 span,
4118 });
4119 Ok(AnalysisResult::new(air_ref, Type::new_module(module_id)))
4120 }
4121
4122 pub(crate) fn resolve_import_path(
4131 &self,
4132 import_path: &str,
4133 span: Span,
4134 ) -> CompileResult<String> {
4135 use std::path::Path;
4136
4137 if import_path == "std" {
4140 return self.resolve_std_import(span);
4141 }
4142
4143 for path in self.file_paths.values() {
4146 if path == import_path {
4148 return Ok(path.clone());
4149 }
4150 if path.ends_with(import_path) {
4152 return Ok(path.clone());
4153 }
4154 let import_base = import_path.strip_suffix(".gruel").unwrap_or(import_path);
4156 let file_name = Path::new(path).file_stem().and_then(|s| s.to_str());
4157 if let Some(name) = file_name
4158 && name == import_base
4159 {
4160 return Ok(path.clone());
4161 }
4162 }
4163
4164 let source_path = self.get_source_path(span);
4167 let source_dir = source_path
4168 .and_then(|p| Path::new(p).parent())
4169 .unwrap_or(Path::new("."));
4170
4171 let mut candidates = Vec::new();
4172
4173 let base_name = import_path.strip_suffix(".gruel").unwrap_or(import_path);
4175
4176 let file_candidate = source_dir.join(format!("{}.gruel", base_name));
4179 candidates.push(file_candidate.display().to_string());
4180 if file_candidate.exists() {
4181 return Ok(file_candidate.to_string_lossy().to_string());
4182 }
4183
4184 if import_path.ends_with(".gruel") {
4186 let candidate = source_dir.join(import_path);
4187 if !candidates.contains(&candidate.display().to_string()) {
4188 candidates.push(candidate.display().to_string());
4189 }
4190 if candidate.exists() {
4191 return Ok(candidate.to_string_lossy().to_string());
4192 }
4193 }
4194
4195 let dir_module_root = source_dir.join(format!("_{}.gruel", base_name));
4197 let dir_path = source_dir.join(base_name);
4198 candidates.push(format!("{} + {}/", dir_module_root.display(), base_name));
4199 if dir_module_root.exists() && dir_path.is_dir() {
4200 return Ok(dir_module_root.to_string_lossy().to_string());
4201 }
4202
4203 if dir_module_root.exists() {
4206 return Ok(dir_module_root.to_string_lossy().to_string());
4207 }
4208
4209 Err(CompileError::new(
4211 ErrorKind::ModuleNotFound {
4212 path: import_path.to_string(),
4213 candidates,
4214 },
4215 span,
4216 ))
4217 }
4218
4219 fn resolve_std_import(&self, span: Span) -> CompileResult<String> {
4228 use std::path::Path;
4229
4230 for path in self.file_paths.values() {
4232 if path.ends_with("_std.gruel") || path.ends_with("std/_std.gruel") {
4234 return Ok(path.clone());
4235 }
4236 }
4237
4238 if let Ok(std_path) = std::env::var("GRUEL_STD_PATH") {
4240 let std_root = Path::new(&std_path).join("_std.gruel");
4241 if std_root.exists() {
4242 return Ok(std_root.to_string_lossy().to_string());
4243 }
4244 }
4245
4246 if let Some(source_path) = self.get_source_path(span) {
4248 let source_dir = Path::new(source_path).parent().unwrap_or(Path::new("."));
4249
4250 let std_root = source_dir.join("std").join("_std.gruel");
4252 if std_root.exists() {
4253 return Ok(std_root.to_string_lossy().to_string());
4254 }
4255 }
4256
4257 Err(CompileError::new(ErrorKind::StdLibNotFound, span))
4266 }
4267
4268 fn analyze_binary_arith<F>(
4280 &mut self,
4281 air: &mut Air,
4282 lhs: InstRef,
4283 rhs: InstRef,
4284 make_data: F,
4285 span: Span,
4286 ctx: &mut AnalysisContext,
4287 ) -> CompileResult<AnalysisResult>
4288 where
4289 F: FnOnce(AirRef, AirRef) -> AirInstData,
4290 {
4291 let lhs_result = self.analyze_inst(air, lhs, ctx)?;
4292 let rhs_result = self.analyze_inst(air, rhs, ctx)?;
4293
4294 if !lhs_result.ty.is_numeric() && !lhs_result.ty.is_error() && !lhs_result.ty.is_never() {
4296 return Err(CompileError::new(
4297 ErrorKind::TypeMismatch {
4298 expected: "numeric type".to_string(),
4299 found: lhs_result.ty.name().to_string(),
4300 },
4301 span,
4302 ));
4303 }
4304
4305 let air_ref = air.add_inst(AirInst {
4306 data: make_data(lhs_result.air_ref, rhs_result.air_ref),
4307 ty: lhs_result.ty,
4308 span,
4309 });
4310 Ok(AnalysisResult::new(air_ref, lhs_result.ty))
4311 }
4312
4313 fn analyze_comparison<F>(
4320 &mut self,
4321 air: &mut Air,
4322 (lhs, rhs): (InstRef, InstRef),
4323 allow_bool: bool,
4324 make_data: F,
4325 span: Span,
4326 ctx: &mut AnalysisContext,
4327 ) -> CompileResult<AnalysisResult>
4328 where
4329 F: FnOnce(AirRef, AirRef) -> AirInstData,
4330 {
4331 if self.is_comparison(lhs) {
4335 return Err(CompileError::new(ErrorKind::ChainedComparison, span)
4336 .with_help("use `&&` to combine comparisons: `a < b && b < c`"));
4337 }
4338
4339 let lhs_result = self.analyze_inst_for_projection(air, lhs, ctx)?;
4342 let rhs_result = self.analyze_inst_for_projection(air, rhs, ctx)?;
4343 let lhs_type = lhs_result.ty;
4344
4345 if lhs_type.is_never() || lhs_type.is_error() {
4347 let air_ref = air.add_inst(AirInst {
4348 data: make_data(lhs_result.air_ref, rhs_result.air_ref),
4349 ty: Type::BOOL,
4350 span,
4351 });
4352 return Ok(AnalysisResult::new(air_ref, Type::BOOL));
4353 }
4354
4355 if allow_bool {
4357 if !lhs_type.is_numeric()
4360 && lhs_type != Type::BOOL
4361 && lhs_type != Type::UNIT
4362 && !lhs_type.is_struct()
4363 && !self.is_builtin_string(lhs_type)
4364 {
4365 return Err(CompileError::new(
4366 ErrorKind::TypeMismatch {
4367 expected: "numeric, bool, string, unit, or struct".to_string(),
4368 found: lhs_type.name().to_string(),
4369 },
4370 self.rir.get(lhs).span,
4371 ));
4372 }
4373 } else if !lhs_type.is_numeric() && !self.is_builtin_string(lhs_type) {
4374 return Err(CompileError::new(
4375 ErrorKind::TypeMismatch {
4376 expected: "numeric or string".to_string(),
4377 found: lhs_type.name().to_string(),
4378 },
4379 self.rir.get(lhs).span,
4380 ));
4381 }
4382
4383 let air_ref = air.add_inst(AirInst {
4384 data: make_data(lhs_result.air_ref, rhs_result.air_ref),
4385 ty: Type::BOOL,
4386 span,
4387 });
4388 Ok(AnalysisResult::new(air_ref, Type::BOOL))
4389 }
4390
4391 pub(crate) fn try_evaluate_const(&mut self, inst_ref: InstRef) -> Option<ConstValue> {
4400 let inst = self.rir.get(inst_ref);
4401 match &inst.data {
4402 InstData::IntConst(value) => i64::try_from(*value).ok().map(ConstValue::Integer),
4404
4405 InstData::BoolConst(value) => Some(ConstValue::Bool(*value)),
4407
4408 InstData::Neg { operand } => match self.try_evaluate_const(*operand)? {
4410 ConstValue::Integer(n) => n.checked_neg().map(ConstValue::Integer),
4411 _ => None,
4412 },
4413
4414 InstData::Not { operand } => match self.try_evaluate_const(*operand)? {
4416 ConstValue::Bool(b) => Some(ConstValue::Bool(!b)),
4417 _ => None,
4418 },
4419
4420 InstData::Add { lhs, rhs } => {
4422 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4423 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4424 l.checked_add(r).map(ConstValue::Integer)
4425 }
4426 InstData::Sub { lhs, rhs } => {
4427 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4428 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4429 l.checked_sub(r).map(ConstValue::Integer)
4430 }
4431 InstData::Mul { lhs, rhs } => {
4432 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4433 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4434 l.checked_mul(r).map(ConstValue::Integer)
4435 }
4436 InstData::Div { lhs, rhs } => {
4437 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4438 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4439 if r == 0 {
4440 None } else {
4442 l.checked_div(r).map(ConstValue::Integer)
4443 }
4444 }
4445 InstData::Mod { lhs, rhs } => {
4446 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4447 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4448 if r == 0 {
4449 None } else {
4451 l.checked_rem(r).map(ConstValue::Integer)
4452 }
4453 }
4454
4455 InstData::Eq { lhs, rhs } => {
4457 let l = self.try_evaluate_const(*lhs)?;
4458 let r = self.try_evaluate_const(*rhs)?;
4459 match (l, r) {
4460 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
4461 Some(ConstValue::Bool(a == b))
4462 }
4463 (ConstValue::Bool(a), ConstValue::Bool(b)) => Some(ConstValue::Bool(a == b)),
4464 _ => None, }
4466 }
4467 InstData::Ne { lhs, rhs } => {
4468 let l = self.try_evaluate_const(*lhs)?;
4469 let r = self.try_evaluate_const(*rhs)?;
4470 match (l, r) {
4471 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
4472 Some(ConstValue::Bool(a != b))
4473 }
4474 (ConstValue::Bool(a), ConstValue::Bool(b)) => Some(ConstValue::Bool(a != b)),
4475 _ => None,
4476 }
4477 }
4478 InstData::Lt { lhs, rhs } => {
4479 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4480 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4481 Some(ConstValue::Bool(l < r))
4482 }
4483 InstData::Gt { lhs, rhs } => {
4484 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4485 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4486 Some(ConstValue::Bool(l > r))
4487 }
4488 InstData::Le { lhs, rhs } => {
4489 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4490 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4491 Some(ConstValue::Bool(l <= r))
4492 }
4493 InstData::Ge { lhs, rhs } => {
4494 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4495 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4496 Some(ConstValue::Bool(l >= r))
4497 }
4498
4499 InstData::And { lhs, rhs } => {
4501 let l = self.try_evaluate_const(*lhs)?.as_bool()?;
4502 let r = self.try_evaluate_const(*rhs)?.as_bool()?;
4503 Some(ConstValue::Bool(l && r))
4504 }
4505 InstData::Or { lhs, rhs } => {
4506 let l = self.try_evaluate_const(*lhs)?.as_bool()?;
4507 let r = self.try_evaluate_const(*rhs)?.as_bool()?;
4508 Some(ConstValue::Bool(l || r))
4509 }
4510
4511 InstData::BitAnd { lhs, rhs } => {
4513 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4514 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4515 Some(ConstValue::Integer(l & r))
4516 }
4517 InstData::BitOr { lhs, rhs } => {
4518 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4519 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4520 Some(ConstValue::Integer(l | r))
4521 }
4522 InstData::BitXor { lhs, rhs } => {
4523 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4524 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4525 Some(ConstValue::Integer(l ^ r))
4526 }
4527 InstData::Shl { lhs, rhs } => {
4528 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4529 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4530 if !(0..8).contains(&r) {
4534 return None;
4535 }
4536 Some(ConstValue::Integer(l << r))
4537 }
4538 InstData::Shr { lhs, rhs } => {
4539 let l = self.try_evaluate_const(*lhs)?.as_integer()?;
4540 let r = self.try_evaluate_const(*rhs)?.as_integer()?;
4541 if !(0..8).contains(&r) {
4544 return None;
4545 }
4546 Some(ConstValue::Integer(l >> r))
4547 }
4548 InstData::BitNot { operand } => {
4549 let n = self.try_evaluate_const(*operand)?.as_integer()?;
4550 Some(ConstValue::Integer(!n))
4551 }
4552
4553 InstData::Comptime { expr } => self.try_evaluate_const(*expr),
4555
4556 InstData::Block { extra_start, len } => {
4558 if *len == 1 {
4564 let inst_refs = self.rir.get_extra(*extra_start, *len);
4565 let result_ref = InstRef::from_raw(inst_refs[0]);
4566 self.try_evaluate_const(result_ref)
4567 } else {
4568 None }
4570 }
4571
4572 InstData::AnonStructType {
4574 fields_start,
4575 fields_len,
4576 methods_start,
4577 methods_len,
4578 } => {
4579 let field_decls = self.rir.get_field_decls(*fields_start, *fields_len);
4581
4582 let mut struct_fields = Vec::with_capacity(field_decls.len());
4584 for (name_sym, type_sym) in field_decls {
4585 let name_str = self.interner.resolve(&name_sym).to_string();
4586 let field_ty = self.resolve_type_for_comptime(type_sym)?;
4589 struct_fields.push(StructField {
4590 name: name_str,
4591 ty: field_ty,
4592 });
4593 }
4594
4595 let method_sigs = self.extract_anon_method_sigs(*methods_start, *methods_len);
4597
4598 let (struct_ty, is_new) =
4600 self.find_or_create_anon_struct(&struct_fields, &method_sigs, &HashMap::new());
4601
4602 if is_new && *methods_len > 0 {
4606 let struct_id = struct_ty.as_struct()?;
4607 self.register_anon_struct_methods_for_comptime_with_subst(
4609 AnonStructSpec {
4610 struct_id,
4611 struct_type: struct_ty,
4612 methods_start: *methods_start,
4613 methods_len: *methods_len,
4614 },
4615 inst.span,
4616 &HashMap::new(), &HashMap::new(), )?;
4619 }
4620 Some(ConstValue::Type(struct_ty))
4621 }
4622
4623 InstData::AnonEnumType {
4625 variants_start,
4626 variants_len,
4627 methods_start,
4628 methods_len,
4629 } => {
4630 let variant_decls = self
4631 .rir
4632 .get_enum_variant_decls(*variants_start, *variants_len);
4633
4634 let mut enum_variants = Vec::with_capacity(variant_decls.len());
4635 for (name_sym, field_type_syms, field_name_syms) in &variant_decls {
4636 let name_str = self.interner.resolve(name_sym).to_string();
4637 let mut fields = Vec::with_capacity(field_type_syms.len());
4638 for ty_sym in field_type_syms {
4639 let field_ty = self.resolve_type_for_comptime(*ty_sym)?;
4640 fields.push(field_ty);
4641 }
4642 let field_names: Vec<String> = field_name_syms
4643 .iter()
4644 .map(|s| self.interner.resolve(s).to_string())
4645 .collect();
4646 enum_variants.push(EnumVariantDef {
4647 name: name_str,
4648 fields,
4649 field_names,
4650 });
4651 }
4652
4653 let method_sigs = self.extract_anon_method_sigs(*methods_start, *methods_len);
4654
4655 let (enum_ty, is_new) =
4656 self.find_or_create_anon_enum(&enum_variants, &method_sigs, &HashMap::new());
4657
4658 if is_new
4660 && *methods_len > 0
4661 && let TypeKind::Enum(enum_id) = enum_ty.kind()
4662 {
4663 self.register_anon_enum_methods_for_comptime_with_subst(
4664 enum_id,
4665 enum_ty,
4666 *methods_start,
4667 *methods_len,
4668 &HashMap::new(),
4669 );
4670 }
4671
4672 Some(ConstValue::Type(enum_ty))
4673 }
4674
4675 InstData::TypeConst { type_name } => {
4677 let type_name_str = self.interner.resolve(type_name);
4678 let ty = match type_name_str {
4679 "i8" => Type::I8,
4680 "i16" => Type::I16,
4681 "i32" => Type::I32,
4682 "i64" => Type::I64,
4683 "u8" => Type::U8,
4684 "u16" => Type::U16,
4685 "u32" => Type::U32,
4686 "u64" => Type::U64,
4687 "bool" => Type::BOOL,
4688 "()" => Type::UNIT,
4689 "!" => Type::NEVER,
4690 _ => {
4691 if let Some(&struct_id) = self.structs.get(type_name) {
4693 Type::new_struct(struct_id)
4694 } else if let Some(&enum_id) = self.enums.get(type_name) {
4695 Type::new_enum(enum_id)
4696 } else {
4697 return None; }
4699 }
4700 };
4701 Some(ConstValue::Type(ty))
4702 }
4703
4704 InstData::VarRef { name } => {
4706 let name_str = self.interner.resolve(name);
4708 let ty = match name_str {
4709 "i8" => Type::I8,
4710 "i16" => Type::I16,
4711 "i32" => Type::I32,
4712 "i64" => Type::I64,
4713 "u8" => Type::U8,
4714 "u16" => Type::U16,
4715 "u32" => Type::U32,
4716 "u64" => Type::U64,
4717 "bool" => Type::BOOL,
4718 "()" => Type::UNIT,
4719 "!" => Type::NEVER,
4720 _ => {
4721 if let Some(&struct_id) = self.structs.get(name) {
4723 Type::new_struct(struct_id)
4724 } else if let Some(&enum_id) = self.enums.get(name) {
4725 Type::new_enum(enum_id)
4726 } else {
4727 return None; }
4729 }
4730 };
4731 Some(ConstValue::Type(ty))
4732 }
4733
4734 _ => None,
4736 }
4737 }
4738
4739 pub(crate) fn try_get_const_index(&mut self, inst_ref: InstRef) -> Option<i64> {
4744 self.try_evaluate_const(inst_ref)?.as_integer()
4745 }
4746
4747 pub(crate) fn try_evaluate_const_with_subst(
4755 &mut self,
4756 inst_ref: InstRef,
4757 type_subst: &std::collections::HashMap<Spur, Type>,
4758 value_subst: &std::collections::HashMap<Spur, ConstValue>,
4759 ) -> Option<ConstValue> {
4760 let inst = self.rir.get(inst_ref);
4761 match &inst.data {
4762 InstData::IntConst(value) => i64::try_from(*value).ok().map(ConstValue::Integer),
4764
4765 InstData::BoolConst(value) => Some(ConstValue::Bool(*value)),
4767
4768 InstData::Neg { operand } => {
4770 match self.try_evaluate_const_with_subst(*operand, type_subst, value_subst)? {
4771 ConstValue::Integer(n) => n.checked_neg().map(ConstValue::Integer),
4772 _ => None,
4773 }
4774 }
4775
4776 InstData::Not { operand } => {
4778 match self.try_evaluate_const_with_subst(*operand, type_subst, value_subst)? {
4779 ConstValue::Bool(b) => Some(ConstValue::Bool(!b)),
4780 _ => None,
4781 }
4782 }
4783
4784 InstData::Add { lhs, rhs } => {
4786 let l = self
4787 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4788 .as_integer()?;
4789 let r = self
4790 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4791 .as_integer()?;
4792 l.checked_add(r).map(ConstValue::Integer)
4793 }
4794 InstData::Sub { lhs, rhs } => {
4795 let l = self
4796 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4797 .as_integer()?;
4798 let r = self
4799 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4800 .as_integer()?;
4801 l.checked_sub(r).map(ConstValue::Integer)
4802 }
4803 InstData::Mul { lhs, rhs } => {
4804 let l = self
4805 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4806 .as_integer()?;
4807 let r = self
4808 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4809 .as_integer()?;
4810 l.checked_mul(r).map(ConstValue::Integer)
4811 }
4812 InstData::Div { lhs, rhs } => {
4813 let l = self
4814 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4815 .as_integer()?;
4816 let r = self
4817 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4818 .as_integer()?;
4819 if r == 0 {
4820 None
4821 } else {
4822 l.checked_div(r).map(ConstValue::Integer)
4823 }
4824 }
4825 InstData::Mod { lhs, rhs } => {
4826 let l = self
4827 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4828 .as_integer()?;
4829 let r = self
4830 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4831 .as_integer()?;
4832 if r == 0 {
4833 None
4834 } else {
4835 l.checked_rem(r).map(ConstValue::Integer)
4836 }
4837 }
4838
4839 InstData::Eq { lhs, rhs } => {
4841 let l = self.try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?;
4842 let r = self.try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?;
4843 match (l, r) {
4844 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
4845 Some(ConstValue::Bool(a == b))
4846 }
4847 (ConstValue::Bool(a), ConstValue::Bool(b)) => Some(ConstValue::Bool(a == b)),
4848 _ => None,
4849 }
4850 }
4851 InstData::Ne { lhs, rhs } => {
4852 let l = self.try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?;
4853 let r = self.try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?;
4854 match (l, r) {
4855 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
4856 Some(ConstValue::Bool(a != b))
4857 }
4858 (ConstValue::Bool(a), ConstValue::Bool(b)) => Some(ConstValue::Bool(a != b)),
4859 _ => None,
4860 }
4861 }
4862 InstData::Lt { lhs, rhs } => {
4863 let l = self
4864 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4865 .as_integer()?;
4866 let r = self
4867 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4868 .as_integer()?;
4869 Some(ConstValue::Bool(l < r))
4870 }
4871 InstData::Gt { lhs, rhs } => {
4872 let l = self
4873 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4874 .as_integer()?;
4875 let r = self
4876 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4877 .as_integer()?;
4878 Some(ConstValue::Bool(l > r))
4879 }
4880 InstData::Le { lhs, rhs } => {
4881 let l = self
4882 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4883 .as_integer()?;
4884 let r = self
4885 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4886 .as_integer()?;
4887 Some(ConstValue::Bool(l <= r))
4888 }
4889 InstData::Ge { lhs, rhs } => {
4890 let l = self
4891 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4892 .as_integer()?;
4893 let r = self
4894 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4895 .as_integer()?;
4896 Some(ConstValue::Bool(l >= r))
4897 }
4898
4899 InstData::And { lhs, rhs } => {
4901 let l = self
4902 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4903 .as_bool()?;
4904 let r = self
4905 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4906 .as_bool()?;
4907 Some(ConstValue::Bool(l && r))
4908 }
4909 InstData::Or { lhs, rhs } => {
4910 let l = self
4911 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4912 .as_bool()?;
4913 let r = self
4914 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4915 .as_bool()?;
4916 Some(ConstValue::Bool(l || r))
4917 }
4918
4919 InstData::BitAnd { lhs, rhs } => {
4921 let l = self
4922 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4923 .as_integer()?;
4924 let r = self
4925 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4926 .as_integer()?;
4927 Some(ConstValue::Integer(l & r))
4928 }
4929 InstData::BitOr { lhs, rhs } => {
4930 let l = self
4931 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4932 .as_integer()?;
4933 let r = self
4934 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4935 .as_integer()?;
4936 Some(ConstValue::Integer(l | r))
4937 }
4938 InstData::BitXor { lhs, rhs } => {
4939 let l = self
4940 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4941 .as_integer()?;
4942 let r = self
4943 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4944 .as_integer()?;
4945 Some(ConstValue::Integer(l ^ r))
4946 }
4947 InstData::Shl { lhs, rhs } => {
4948 let l = self
4949 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4950 .as_integer()?;
4951 let r = self
4952 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4953 .as_integer()?;
4954 if !(0..8).contains(&r) {
4955 return None;
4956 }
4957 Some(ConstValue::Integer(l << r))
4958 }
4959 InstData::Shr { lhs, rhs } => {
4960 let l = self
4961 .try_evaluate_const_with_subst(*lhs, type_subst, value_subst)?
4962 .as_integer()?;
4963 let r = self
4964 .try_evaluate_const_with_subst(*rhs, type_subst, value_subst)?
4965 .as_integer()?;
4966 if !(0..8).contains(&r) {
4967 return None;
4968 }
4969 Some(ConstValue::Integer(l >> r))
4970 }
4971 InstData::BitNot { operand } => {
4972 let n = self
4973 .try_evaluate_const_with_subst(*operand, type_subst, value_subst)?
4974 .as_integer()?;
4975 Some(ConstValue::Integer(!n))
4976 }
4977
4978 InstData::Comptime { expr } => {
4980 self.try_evaluate_const_with_subst(*expr, type_subst, value_subst)
4981 }
4982
4983 InstData::Block { extra_start, len } => {
4985 if *len == 1 {
4986 let inst_refs = self.rir.get_extra(*extra_start, *len);
4987 let result_ref = InstRef::from_raw(inst_refs[0]);
4988 self.try_evaluate_const_with_subst(result_ref, type_subst, value_subst)
4989 } else {
4990 None
4991 }
4992 }
4993
4994 InstData::AnonStructType {
4996 fields_start,
4997 fields_len,
4998 methods_start,
4999 methods_len,
5000 } => {
5001 let field_decls = self.rir.get_field_decls(*fields_start, *fields_len);
5002
5003 let mut struct_fields = Vec::with_capacity(field_decls.len());
5004 for (name_sym, type_sym) in field_decls {
5005 let name_str = self.interner.resolve(&name_sym).to_string();
5006 let field_ty =
5008 self.resolve_type_for_comptime_with_subst(type_sym, type_subst)?;
5009 struct_fields.push(StructField {
5010 name: name_str,
5011 ty: field_ty,
5012 });
5013 }
5014
5015 let method_sigs = self.extract_anon_method_sigs(*methods_start, *methods_len);
5017
5018 let (struct_ty, _is_new) =
5019 self.find_or_create_anon_struct(&struct_fields, &method_sigs, value_subst);
5020
5021 if *methods_len > 0 {
5026 let struct_id = struct_ty.as_struct()?;
5027
5028 let method_refs = self.rir.get_inst_refs(*methods_start, *methods_len);
5030 let first_method_ref = method_refs[0];
5031 let first_method_inst = self.rir.get(first_method_ref);
5032 if let InstData::FnDecl {
5033 name: method_name, ..
5034 } = &first_method_inst.data
5035 {
5036 let needs_registration =
5037 !self.methods.contains_key(&(struct_id, *method_name));
5038
5039 if needs_registration {
5040 self.register_anon_struct_methods_for_comptime_with_subst(
5042 AnonStructSpec {
5043 struct_id,
5044 struct_type: struct_ty,
5045 methods_start: *methods_start,
5046 methods_len: *methods_len,
5047 },
5048 inst.span,
5049 type_subst,
5050 value_subst,
5051 )?;
5052 }
5053 }
5054 }
5055 Some(ConstValue::Type(struct_ty))
5056 }
5057
5058 InstData::AnonEnumType {
5060 variants_start,
5061 variants_len,
5062 methods_start,
5063 methods_len,
5064 } => {
5065 let variant_decls = self
5066 .rir
5067 .get_enum_variant_decls(*variants_start, *variants_len);
5068
5069 let mut enum_variants = Vec::with_capacity(variant_decls.len());
5070 for (name_sym, field_type_syms, field_name_syms) in &variant_decls {
5071 let name_str = self.interner.resolve(name_sym).to_string();
5072 let mut fields = Vec::with_capacity(field_type_syms.len());
5073 for ty_sym in field_type_syms {
5074 let field_ty =
5075 self.resolve_type_for_comptime_with_subst(*ty_sym, type_subst)?;
5076 fields.push(field_ty);
5077 }
5078 let field_names: Vec<String> = field_name_syms
5079 .iter()
5080 .map(|s| self.interner.resolve(s).to_string())
5081 .collect();
5082 enum_variants.push(EnumVariantDef {
5083 name: name_str,
5084 fields,
5085 field_names,
5086 });
5087 }
5088
5089 let method_sigs = self.extract_anon_method_sigs(*methods_start, *methods_len);
5090
5091 let (enum_ty, is_new) =
5092 self.find_or_create_anon_enum(&enum_variants, &method_sigs, value_subst);
5093
5094 if is_new
5096 && *methods_len > 0
5097 && let TypeKind::Enum(enum_id) = enum_ty.kind()
5098 {
5099 self.register_anon_enum_methods_for_comptime_with_subst(
5100 enum_id,
5101 enum_ty,
5102 *methods_start,
5103 *methods_len,
5104 type_subst,
5105 );
5106 }
5107
5108 Some(ConstValue::Type(enum_ty))
5109 }
5110
5111 InstData::TypeConst { type_name } => {
5113 if let Some(&ty) = type_subst.get(type_name) {
5115 return Some(ConstValue::Type(ty));
5116 }
5117
5118 let type_name_str = self.interner.resolve(type_name);
5119 let ty = match type_name_str {
5120 "i8" => Type::I8,
5121 "i16" => Type::I16,
5122 "i32" => Type::I32,
5123 "i64" => Type::I64,
5124 "u8" => Type::U8,
5125 "u16" => Type::U16,
5126 "u32" => Type::U32,
5127 "u64" => Type::U64,
5128 "bool" => Type::BOOL,
5129 "()" => Type::UNIT,
5130 "!" => Type::NEVER,
5131 _ => {
5132 if let Some(&struct_id) = self.structs.get(type_name) {
5133 Type::new_struct(struct_id)
5134 } else if let Some(&enum_id) = self.enums.get(type_name) {
5135 Type::new_enum(enum_id)
5136 } else {
5137 return None;
5138 }
5139 }
5140 };
5141 Some(ConstValue::Type(ty))
5142 }
5143
5144 InstData::VarRef { name } => {
5146 if let Some(&ty) = type_subst.get(name) {
5148 return Some(ConstValue::Type(ty));
5149 }
5150
5151 if let Some(value) = value_subst.get(name) {
5153 return Some(*value);
5154 }
5155
5156 let name_str = self.interner.resolve(name);
5158 let ty = match name_str {
5159 "i8" => Type::I8,
5160 "i16" => Type::I16,
5161 "i32" => Type::I32,
5162 "i64" => Type::I64,
5163 "u8" => Type::U8,
5164 "u16" => Type::U16,
5165 "u32" => Type::U32,
5166 "u64" => Type::U64,
5167 "bool" => Type::BOOL,
5168 "()" => Type::UNIT,
5169 "!" => Type::NEVER,
5170 _ => {
5171 if let Some(&struct_id) = self.structs.get(name) {
5172 Type::new_struct(struct_id)
5173 } else if let Some(&enum_id) = self.enums.get(name) {
5174 Type::new_enum(enum_id)
5175 } else {
5176 return None;
5177 }
5178 }
5179 };
5180 Some(ConstValue::Type(ty))
5181 }
5182
5183 _ => None,
5185 }
5186 }
5187
5188 pub(crate) fn try_evaluate_comptime_arg(
5214 &mut self,
5215 inst_ref: InstRef,
5216 ctx: &AnalysisContext,
5217 outer_span: Span,
5218 ) -> Option<ConstValue> {
5219 if let Some(val) = self.try_evaluate_const(inst_ref) {
5221 return Some(val);
5222 }
5223 let prev_steps = self.comptime_steps_used;
5227 self.comptime_steps_used = 0;
5228 let mut locals = ctx.comptime_value_vars.clone();
5229 let result = self
5230 .evaluate_comptime_inst(inst_ref, &mut locals, ctx, outer_span)
5231 .ok();
5232 self.comptime_steps_used = prev_steps;
5233 result.filter(|v| {
5235 !matches!(
5236 v,
5237 ConstValue::BreakSignal | ConstValue::ContinueSignal | ConstValue::ReturnSignal
5238 )
5239 })
5240 }
5241
5242 fn format_const_value(&self, val: ConstValue, span: Span) -> CompileResult<String> {
5247 match val {
5248 ConstValue::Bool(b) => Ok(if b {
5249 "true".to_string()
5250 } else {
5251 "false".to_string()
5252 }),
5253 ConstValue::Integer(v) => Ok(format!("{v}")),
5254 ConstValue::Unit => Ok("()".to_string()),
5255 ConstValue::ComptimeStr(idx) => match &self.comptime_heap[idx as usize] {
5256 ComptimeHeapItem::String(s) => Ok(s.clone()),
5257 _ => Err(CompileError::new(
5258 ErrorKind::ComptimeEvaluationFailed {
5259 reason: "invalid comptime_str heap reference".into(),
5260 },
5261 span,
5262 )),
5263 },
5264 _ => Err(CompileError::new(
5265 ErrorKind::ComptimeEvaluationFailed {
5266 reason: "expression contains values that cannot be known at compile time"
5267 .into(),
5268 },
5269 span,
5270 )),
5271 }
5272 }
5273
5274 fn resolve_comptime_str(&self, idx: u32, span: Span) -> CompileResult<&str> {
5276 match &self.comptime_heap[idx as usize] {
5277 ComptimeHeapItem::String(s) => Ok(s.as_str()),
5278 _ => Err(CompileError::new(
5279 ErrorKind::ComptimeEvaluationFailed {
5280 reason: "invalid comptime_str heap reference".into(),
5281 },
5282 span,
5283 )),
5284 }
5285 }
5286
5287 #[allow(clippy::too_many_arguments)]
5292 fn evaluate_comptime_str_method(
5293 &mut self,
5294 str_idx: u32,
5295 method_name: &str,
5296 call_args: &[gruel_rir::RirCallArg],
5297 locals: &mut HashMap<Spur, ConstValue>,
5298 ctx: &AnalysisContext,
5299 outer_span: Span,
5300 inst_span: Span,
5301 ) -> CompileResult<ConstValue> {
5302 let s = self.resolve_comptime_str(str_idx, inst_span)?.to_string();
5303
5304 match method_name {
5305 "len" => {
5306 if !call_args.is_empty() {
5307 return Err(CompileError::new(
5308 ErrorKind::IntrinsicWrongArgCount {
5309 name: "len".to_string(),
5310 expected: 0,
5311 found: call_args.len(),
5312 },
5313 inst_span,
5314 ));
5315 }
5316 Ok(ConstValue::Integer(s.len() as i64))
5317 }
5318 "is_empty" => {
5319 if !call_args.is_empty() {
5320 return Err(CompileError::new(
5321 ErrorKind::IntrinsicWrongArgCount {
5322 name: "is_empty".to_string(),
5323 expected: 0,
5324 found: call_args.len(),
5325 },
5326 inst_span,
5327 ));
5328 }
5329 Ok(ConstValue::Bool(s.is_empty()))
5330 }
5331 "contains" | "starts_with" | "ends_with" | "eq" | "ne" | "lt" | "le" | "gt" | "ge" => {
5332 if call_args.len() != 1 {
5333 return Err(CompileError::new(
5334 ErrorKind::IntrinsicWrongArgCount {
5335 name: method_name.to_string(),
5336 expected: 1,
5337 found: call_args.len(),
5338 },
5339 inst_span,
5340 ));
5341 }
5342 let arg_val =
5343 self.evaluate_comptime_inst(call_args[0].value, locals, ctx, outer_span)?;
5344 let other_idx = match arg_val {
5345 ConstValue::ComptimeStr(idx) => idx,
5346 _ => {
5347 return Err(CompileError::new(
5348 ErrorKind::ComptimeEvaluationFailed {
5349 reason: format!(
5350 "comptime_str.{method_name} expects a comptime_str argument"
5351 ),
5352 },
5353 inst_span,
5354 ));
5355 }
5356 };
5357 let other = self.resolve_comptime_str(other_idx, inst_span)?.to_string();
5358 let result = match method_name {
5359 "contains" => s.contains(other.as_str()),
5360 "starts_with" => s.starts_with(other.as_str()),
5361 "ends_with" => s.ends_with(other.as_str()),
5362 "eq" => s == other,
5363 "ne" => s != other,
5364 "lt" => s < other,
5365 "le" => s <= other,
5366 "gt" => s > other,
5367 "ge" => s >= other,
5368 _ => unreachable!(),
5369 };
5370 Ok(ConstValue::Bool(result))
5371 }
5372 "concat" => {
5373 if call_args.len() != 1 {
5374 return Err(CompileError::new(
5375 ErrorKind::IntrinsicWrongArgCount {
5376 name: "concat".to_string(),
5377 expected: 1,
5378 found: call_args.len(),
5379 },
5380 inst_span,
5381 ));
5382 }
5383 let arg_val =
5384 self.evaluate_comptime_inst(call_args[0].value, locals, ctx, outer_span)?;
5385 let other_idx = match arg_val {
5386 ConstValue::ComptimeStr(idx) => idx,
5387 _ => {
5388 return Err(CompileError::new(
5389 ErrorKind::ComptimeEvaluationFailed {
5390 reason: "comptime_str.concat expects a comptime_str argument"
5391 .into(),
5392 },
5393 inst_span,
5394 ));
5395 }
5396 };
5397 let other = self.resolve_comptime_str(other_idx, inst_span)?.to_string();
5398 let result = format!("{s}{other}");
5399 let idx = self.comptime_heap.len() as u32;
5400 self.comptime_heap.push(ComptimeHeapItem::String(result));
5401 Ok(ConstValue::ComptimeStr(idx))
5402 }
5403 "clone" => {
5404 if !call_args.is_empty() {
5405 return Err(CompileError::new(
5406 ErrorKind::IntrinsicWrongArgCount {
5407 name: "clone".to_string(),
5408 expected: 0,
5409 found: call_args.len(),
5410 },
5411 inst_span,
5412 ));
5413 }
5414 Ok(self.alloc_comptime_str(s))
5415 }
5416 "push_str" | "push" | "clear" | "reserve" => Err(CompileError::new(
5417 ErrorKind::ComptimeEvaluationFailed {
5418 reason: format!(
5419 "cannot call .{method_name}() on a compile-time string; use .concat() to produce a new string"
5420 ),
5421 },
5422 inst_span,
5423 )),
5424 "capacity" => Err(CompileError::new(
5425 ErrorKind::ComptimeEvaluationFailed {
5426 reason: "capacity is not available for compile-time strings".into(),
5427 },
5428 inst_span,
5429 )),
5430 _ => Err(CompileError::new(
5431 ErrorKind::ComptimeEvaluationFailed {
5432 reason: format!("unknown comptime_str method '{method_name}'"),
5433 },
5434 inst_span,
5435 )),
5436 }
5437 }
5438
5439 fn evaluate_comptime_string_arg(
5444 &mut self,
5445 arg_ref: InstRef,
5446 locals: &mut HashMap<Spur, ConstValue>,
5447 ctx: &AnalysisContext,
5448 outer_span: Span,
5449 ) -> CompileResult<String> {
5450 let arg_inst = self.rir.get(arg_ref);
5451 if let gruel_rir::InstData::StringConst(spur) = &arg_inst.data {
5453 return Ok(self.interner.resolve(spur).to_string());
5454 }
5455 let val = self.evaluate_comptime_inst(arg_ref, locals, ctx, outer_span)?;
5457 match val {
5458 ConstValue::ComptimeStr(idx) => self
5459 .resolve_comptime_str(idx, arg_inst.span)
5460 .map(|s| s.to_string()),
5461 _ => Err(CompileError::new(
5462 ErrorKind::ComptimeEvaluationFailed {
5463 reason: "@compileError requires a string literal or comptime_str argument"
5464 .into(),
5465 },
5466 arg_inst.span,
5467 )),
5468 }
5469 }
5470
5471 fn alloc_comptime_str(&mut self, s: String) -> ConstValue {
5473 let idx = self.comptime_heap.len() as u32;
5474 self.comptime_heap.push(ComptimeHeapItem::String(s));
5475 ConstValue::ComptimeStr(idx)
5476 }
5477
5478 fn alloc_comptime_struct(
5480 &mut self,
5481 struct_id: StructId,
5482 fields: Vec<ConstValue>,
5483 ) -> ConstValue {
5484 let idx = self.comptime_heap.len() as u32;
5485 self.comptime_heap
5486 .push(ComptimeHeapItem::Struct { struct_id, fields });
5487 ConstValue::Struct(idx)
5488 }
5489
5490 fn alloc_comptime_array(&mut self, elements: Vec<ConstValue>) -> ConstValue {
5492 let idx = self.comptime_heap.len() as u32;
5493 self.comptime_heap.push(ComptimeHeapItem::Array(elements));
5494 ConstValue::Array(idx)
5495 }
5496
5497 fn typekind_variant_idx(&self, variant_name: &str) -> u32 {
5499 let enum_id = self
5500 .builtin_typekind_id
5501 .expect("TypeKind enum not injected");
5502 let enum_def = self.type_pool.enum_def(enum_id);
5503 enum_def
5504 .find_variant(variant_name)
5505 .unwrap_or_else(|| panic!("TypeKind variant '{variant_name}' not found")) as u32
5506 }
5507
5508 fn evaluate_comptime_type_name(&mut self, ty: Type, _span: Span) -> CompileResult<ConstValue> {
5510 let name = self.type_pool.format_type_name(ty);
5511 Ok(self.alloc_comptime_str(name))
5512 }
5513
5514 fn evaluate_comptime_type_info(&mut self, ty: Type, span: Span) -> CompileResult<ConstValue> {
5516 let typekind_enum_id = self
5517 .builtin_typekind_id
5518 .expect("TypeKind enum not injected");
5519 let typekind_type = Type::new_enum(typekind_enum_id);
5520
5521 match ty.kind() {
5522 TypeKind::Struct(struct_id) => {
5523 self.build_struct_type_info(struct_id, typekind_enum_id, typekind_type)
5524 }
5525 TypeKind::Enum(enum_id) => {
5526 self.build_enum_type_info(enum_id, typekind_enum_id, typekind_type)
5527 }
5528 TypeKind::I8 => {
5529 self.build_int_type_info("i8", 8, true, typekind_enum_id, typekind_type)
5530 }
5531 TypeKind::I16 => {
5532 self.build_int_type_info("i16", 16, true, typekind_enum_id, typekind_type)
5533 }
5534 TypeKind::I32 => {
5535 self.build_int_type_info("i32", 32, true, typekind_enum_id, typekind_type)
5536 }
5537 TypeKind::I64 => {
5538 self.build_int_type_info("i64", 64, true, typekind_enum_id, typekind_type)
5539 }
5540 TypeKind::U8 => {
5541 self.build_int_type_info("u8", 8, false, typekind_enum_id, typekind_type)
5542 }
5543 TypeKind::U16 => {
5544 self.build_int_type_info("u16", 16, false, typekind_enum_id, typekind_type)
5545 }
5546 TypeKind::U32 => {
5547 self.build_int_type_info("u32", 32, false, typekind_enum_id, typekind_type)
5548 }
5549 TypeKind::U64 => {
5550 self.build_int_type_info("u64", 64, false, typekind_enum_id, typekind_type)
5551 }
5552 TypeKind::Bool => {
5553 self.build_simple_type_info("bool", "Bool", typekind_enum_id, typekind_type)
5554 }
5555 TypeKind::Unit => {
5556 self.build_simple_type_info("()", "Unit", typekind_enum_id, typekind_type)
5557 }
5558 TypeKind::Never => {
5559 self.build_simple_type_info("!", "Never", typekind_enum_id, typekind_type)
5560 }
5561 TypeKind::Array(array_type_id) => {
5562 let (elem_ty, len) = self.type_pool.array_def(array_type_id);
5563 let elem_name = self.type_pool.format_type_name(elem_ty);
5564 let name = format!("[{elem_name}; {len}]");
5565 self.build_simple_type_info(&name, "Array", typekind_enum_id, typekind_type)
5566 }
5567 _ => Err(CompileError::new(
5568 ErrorKind::ComptimeEvaluationFailed {
5569 reason: format!("@typeInfo not supported for type '{}'", ty.name()),
5570 },
5571 span,
5572 )),
5573 }
5574 }
5575
5576 fn build_simple_type_info(
5578 &mut self,
5579 type_name: &str,
5580 kind_variant_name: &str,
5581 typekind_enum_id: EnumId,
5582 typekind_type: Type,
5583 ) -> CompileResult<ConstValue> {
5584 let kind_val = ConstValue::EnumVariant {
5585 enum_id: typekind_enum_id,
5586 variant_idx: self.typekind_variant_idx(kind_variant_name),
5587 };
5588 let name_val = self.alloc_comptime_str(type_name.to_string());
5589
5590 let fields = vec![
5591 StructField {
5592 name: "kind".to_string(),
5593 ty: typekind_type,
5594 },
5595 StructField {
5596 name: "name".to_string(),
5597 ty: Type::COMPTIME_STR,
5598 },
5599 ];
5600 let (info_type, _) = self.find_or_create_anon_struct(&fields, &[], &HashMap::new());
5601 let info_struct_id = match info_type.kind() {
5602 TypeKind::Struct(id) => id,
5603 _ => unreachable!(),
5604 };
5605
5606 Ok(self.alloc_comptime_struct(info_struct_id, vec![kind_val, name_val]))
5607 }
5608
5609 #[allow(clippy::too_many_arguments)]
5611 fn build_int_type_info(
5612 &mut self,
5613 type_name: &str,
5614 bits: i32,
5615 is_signed: bool,
5616 typekind_enum_id: EnumId,
5617 typekind_type: Type,
5618 ) -> CompileResult<ConstValue> {
5619 let kind_val = ConstValue::EnumVariant {
5620 enum_id: typekind_enum_id,
5621 variant_idx: self.typekind_variant_idx("Int"),
5622 };
5623 let name_val = self.alloc_comptime_str(type_name.to_string());
5624
5625 let fields = vec![
5626 StructField {
5627 name: "kind".to_string(),
5628 ty: typekind_type,
5629 },
5630 StructField {
5631 name: "name".to_string(),
5632 ty: Type::COMPTIME_STR,
5633 },
5634 StructField {
5635 name: "bits".to_string(),
5636 ty: Type::I32,
5637 },
5638 StructField {
5639 name: "is_signed".to_string(),
5640 ty: Type::BOOL,
5641 },
5642 ];
5643 let (info_type, _) = self.find_or_create_anon_struct(&fields, &[], &HashMap::new());
5644 let info_struct_id = match info_type.kind() {
5645 TypeKind::Struct(id) => id,
5646 _ => unreachable!(),
5647 };
5648
5649 Ok(self.alloc_comptime_struct(
5650 info_struct_id,
5651 vec![
5652 kind_val,
5653 name_val,
5654 ConstValue::Integer(bits as i64),
5655 ConstValue::Bool(is_signed),
5656 ],
5657 ))
5658 }
5659
5660 fn build_struct_type_info(
5662 &mut self,
5663 struct_id: StructId,
5664 typekind_enum_id: EnumId,
5665 typekind_type: Type,
5666 ) -> CompileResult<ConstValue> {
5667 let kind_val = ConstValue::EnumVariant {
5668 enum_id: typekind_enum_id,
5669 variant_idx: self.typekind_variant_idx("Struct"),
5670 };
5671
5672 let struct_def = self.type_pool.struct_def(struct_id);
5674 let struct_name = struct_def.name.clone();
5675 let field_defs: Vec<(String, Type)> = struct_def
5676 .fields
5677 .iter()
5678 .map(|f| (f.name.clone(), f.ty))
5679 .collect();
5680 let field_count = field_defs.len() as i32;
5681
5682 let name_val = self.alloc_comptime_str(struct_name);
5683
5684 let field_info_fields = vec![
5686 StructField {
5687 name: "name".to_string(),
5688 ty: Type::COMPTIME_STR,
5689 },
5690 StructField {
5691 name: "field_type".to_string(),
5692 ty: Type::COMPTIME_TYPE,
5693 },
5694 ];
5695 let (field_info_type, _) =
5696 self.find_or_create_anon_struct(&field_info_fields, &[], &HashMap::new());
5697 let field_info_struct_id = match field_info_type.kind() {
5698 TypeKind::Struct(id) => id,
5699 _ => unreachable!(),
5700 };
5701
5702 let mut field_values = Vec::with_capacity(field_defs.len());
5704 for (fname, ftype) in &field_defs {
5705 let fname_val = self.alloc_comptime_str(fname.clone());
5706 let ftype_val = ConstValue::Type(*ftype);
5707 let field_info =
5708 self.alloc_comptime_struct(field_info_struct_id, vec![fname_val, ftype_val]);
5709 field_values.push(field_info);
5710 }
5711
5712 let fields_array = self.alloc_comptime_array(field_values);
5714
5715 let fields_array_type =
5717 Type::new_array(self.get_or_create_array_type(field_info_type, field_count as u64));
5718
5719 let info_fields = vec![
5721 StructField {
5722 name: "kind".to_string(),
5723 ty: typekind_type,
5724 },
5725 StructField {
5726 name: "name".to_string(),
5727 ty: Type::COMPTIME_STR,
5728 },
5729 StructField {
5730 name: "field_count".to_string(),
5731 ty: Type::I32,
5732 },
5733 StructField {
5734 name: "fields".to_string(),
5735 ty: fields_array_type,
5736 },
5737 ];
5738 let (info_type, _) = self.find_or_create_anon_struct(&info_fields, &[], &HashMap::new());
5739 let info_struct_id = match info_type.kind() {
5740 TypeKind::Struct(id) => id,
5741 _ => unreachable!(),
5742 };
5743
5744 Ok(self.alloc_comptime_struct(
5745 info_struct_id,
5746 vec![
5747 kind_val,
5748 name_val,
5749 ConstValue::Integer(field_count as i64),
5750 fields_array,
5751 ],
5752 ))
5753 }
5754
5755 fn build_enum_type_info(
5757 &mut self,
5758 enum_id: EnumId,
5759 typekind_enum_id: EnumId,
5760 typekind_type: Type,
5761 ) -> CompileResult<ConstValue> {
5762 let kind_val = ConstValue::EnumVariant {
5763 enum_id: typekind_enum_id,
5764 variant_idx: self.typekind_variant_idx("Enum"),
5765 };
5766
5767 let enum_def = self.type_pool.enum_def(enum_id);
5769 let enum_name = enum_def.name.clone();
5770 let variant_defs: Vec<(String, Vec<(String, Type)>)> = enum_def
5771 .variants
5772 .iter()
5773 .map(|v| {
5774 let vfields: Vec<(String, Type)> = if v.is_struct_variant() {
5775 v.field_names
5777 .iter()
5778 .zip(v.fields.iter())
5779 .map(|(name, ty)| (name.clone(), *ty))
5780 .collect()
5781 } else {
5782 v.fields
5784 .iter()
5785 .enumerate()
5786 .map(|(i, ty)| (format!("{i}"), *ty))
5787 .collect()
5788 };
5789 (v.name.clone(), vfields)
5790 })
5791 .collect();
5792 let variant_count = variant_defs.len() as i32;
5793
5794 let name_val = self.alloc_comptime_str(enum_name);
5795
5796 let field_info_fields = vec![
5798 StructField {
5799 name: "name".to_string(),
5800 ty: Type::COMPTIME_STR,
5801 },
5802 StructField {
5803 name: "field_type".to_string(),
5804 ty: Type::COMPTIME_TYPE,
5805 },
5806 ];
5807 let (field_info_type, _) =
5808 self.find_or_create_anon_struct(&field_info_fields, &[], &HashMap::new());
5809 let field_info_struct_id = match field_info_type.kind() {
5810 TypeKind::Struct(id) => id,
5811 _ => unreachable!(),
5812 };
5813
5814 let mut variant_values = Vec::with_capacity(variant_defs.len());
5816 for (vname, vfields) in &variant_defs {
5817 let vname_val = self.alloc_comptime_str(vname.clone());
5818
5819 let mut vfield_values = Vec::new();
5821 for (fname, ftype) in vfields {
5822 let fname_val = self.alloc_comptime_str(fname.clone());
5823 let ftype_val = ConstValue::Type(*ftype);
5824 let field_info =
5825 self.alloc_comptime_struct(field_info_struct_id, vec![fname_val, ftype_val]);
5826 vfield_values.push(field_info);
5827 }
5828 let vfields_array = self.alloc_comptime_array(vfield_values);
5829 let vfield_count = vfields.len() as i32;
5830
5831 let vfields_array_type = Type::new_array(
5833 self.get_or_create_array_type(field_info_type, vfields.len() as u64),
5834 );
5835 let variant_info_fields = vec![
5836 StructField {
5837 name: "name".to_string(),
5838 ty: Type::COMPTIME_STR,
5839 },
5840 StructField {
5841 name: "field_count".to_string(),
5842 ty: Type::I32,
5843 },
5844 StructField {
5845 name: "fields".to_string(),
5846 ty: vfields_array_type,
5847 },
5848 ];
5849 let (variant_info_type, _) =
5850 self.find_or_create_anon_struct(&variant_info_fields, &[], &HashMap::new());
5851 let variant_info_struct_id = match variant_info_type.kind() {
5852 TypeKind::Struct(id) => id,
5853 _ => unreachable!(),
5854 };
5855
5856 let variant_info = self.alloc_comptime_struct(
5857 variant_info_struct_id,
5858 vec![
5859 vname_val,
5860 ConstValue::Integer(vfield_count as i64),
5861 vfields_array,
5862 ],
5863 );
5864 variant_values.push(variant_info);
5865 }
5866
5867 let variants_array = self.alloc_comptime_array(variant_values);
5869
5870 let empty_fields_array_type =
5872 Type::new_array(self.get_or_create_array_type(field_info_type, 0));
5873 let variant_info_fields = vec![
5874 StructField {
5875 name: "name".to_string(),
5876 ty: Type::COMPTIME_STR,
5877 },
5878 StructField {
5879 name: "field_count".to_string(),
5880 ty: Type::I32,
5881 },
5882 StructField {
5883 name: "fields".to_string(),
5884 ty: empty_fields_array_type,
5885 },
5886 ];
5887 let (variant_info_type, _) =
5888 self.find_or_create_anon_struct(&variant_info_fields, &[], &HashMap::new());
5889 let variants_array_type =
5890 Type::new_array(self.get_or_create_array_type(variant_info_type, variant_count as u64));
5891
5892 let info_fields = vec![
5894 StructField {
5895 name: "kind".to_string(),
5896 ty: typekind_type,
5897 },
5898 StructField {
5899 name: "name".to_string(),
5900 ty: Type::COMPTIME_STR,
5901 },
5902 StructField {
5903 name: "variant_count".to_string(),
5904 ty: Type::I32,
5905 },
5906 StructField {
5907 name: "variants".to_string(),
5908 ty: variants_array_type,
5909 },
5910 ];
5911 let (info_type, _) = self.find_or_create_anon_struct(&info_fields, &[], &HashMap::new());
5912 let info_struct_id = match info_type.kind() {
5913 TypeKind::Struct(id) => id,
5914 _ => unreachable!(),
5915 };
5916
5917 Ok(self.alloc_comptime_struct(
5918 info_struct_id,
5919 vec![
5920 kind_val,
5921 name_val,
5922 ConstValue::Integer(variant_count as i64),
5923 variants_array,
5924 ],
5925 ))
5926 }
5927
5928 fn evaluate_comptime_block(
5934 &mut self,
5935 inst_ref: InstRef,
5936 ctx: &AnalysisContext,
5937 span: Span,
5938 ) -> CompileResult<ConstValue> {
5939 let _span = info_span!("comptime").entered();
5940 self.comptime_steps_used = 0;
5942 self.comptime_heap.clear();
5943 let mut locals = ctx.comptime_value_vars.clone();
5946 let result = self.evaluate_comptime_inst(inst_ref, &mut locals, ctx, span)?;
5947 match result {
5951 ConstValue::BreakSignal | ConstValue::ContinueSignal => Err(CompileError::new(
5952 ErrorKind::ComptimeEvaluationFailed {
5953 reason: "break/continue outside a loop in comptime block".into(),
5954 },
5955 span,
5956 )),
5957 ConstValue::ReturnSignal => Err(CompileError::new(
5958 ErrorKind::ComptimeEvaluationFailed {
5959 reason: "return outside a function in comptime block".into(),
5960 },
5961 span,
5962 )),
5963 val => Ok(val),
5964 }
5965 }
5966
5967 fn evaluate_comptime_expr(
5972 &mut self,
5973 inst_ref: InstRef,
5974 ctx: &AnalysisContext,
5975 span: Span,
5976 ) -> CompileResult<ConstValue> {
5977 let prev_steps = self.comptime_steps_used;
5978 self.comptime_steps_used = 0;
5979 let mut locals = ctx.comptime_value_vars.clone();
5980 let result = self.evaluate_comptime_inst(inst_ref, &mut locals, ctx, span)?;
5981 self.comptime_steps_used = prev_steps;
5982 match result {
5983 ConstValue::BreakSignal | ConstValue::ContinueSignal => Err(CompileError::new(
5984 ErrorKind::ComptimeEvaluationFailed {
5985 reason: "break/continue outside a loop in comptime expression".into(),
5986 },
5987 span,
5988 )),
5989 ConstValue::ReturnSignal => Err(CompileError::new(
5990 ErrorKind::ComptimeEvaluationFailed {
5991 reason: "return outside a function in comptime expression".into(),
5992 },
5993 span,
5994 )),
5995 val => Ok(val),
5996 }
5997 }
5998
5999 #[allow(clippy::only_used_in_recursion)]
6005 fn evaluate_comptime_inst(
6006 &mut self,
6007 inst_ref: InstRef,
6008 locals: &mut HashMap<Spur, ConstValue>,
6009 ctx: &AnalysisContext,
6010 outer_span: Span,
6011 ) -> CompileResult<ConstValue> {
6012 let (inst_span, inst_data) = {
6015 let inst = self.rir.get(inst_ref);
6016 (inst.span, inst.data.clone())
6017 };
6018
6019 #[inline]
6021 fn not_const(span: Span) -> CompileError {
6022 CompileError::new(
6023 ErrorKind::ComptimeEvaluationFailed {
6024 reason: "expression contains values that cannot be known at compile time"
6025 .into(),
6026 },
6027 span,
6028 )
6029 }
6030
6031 #[inline]
6033 fn overflow(span: Span) -> CompileError {
6034 CompileError::new(
6035 ErrorKind::ComptimeEvaluationFailed {
6036 reason: "arithmetic overflow in comptime evaluation".into(),
6037 },
6038 span,
6039 )
6040 }
6041
6042 #[inline]
6044 fn int(v: ConstValue, span: Span) -> CompileResult<i64> {
6045 v.as_integer().ok_or_else(|| not_const(span))
6046 }
6047
6048 #[inline]
6050 fn bool_val(v: ConstValue, span: Span) -> CompileResult<bool> {
6051 v.as_bool().ok_or_else(|| not_const(span))
6052 }
6053
6054 match inst_data {
6055 InstData::IntConst(value) => {
6057 i64::try_from(value).map(ConstValue::Integer).map_err(|_| {
6058 CompileError::new(
6059 ErrorKind::ComptimeEvaluationFailed {
6060 reason: "integer constant too large for comptime evaluation".into(),
6061 },
6062 inst_span,
6063 )
6064 })
6065 }
6066
6067 InstData::BoolConst(value) => Ok(ConstValue::Bool(value)),
6068
6069 InstData::UnitConst => Ok(ConstValue::Unit),
6070
6071 InstData::StringConst(spur) => {
6072 let s = self.interner.resolve(&spur).to_string();
6073 let idx = self.comptime_heap.len() as u32;
6074 self.comptime_heap.push(ComptimeHeapItem::String(s));
6075 Ok(ConstValue::ComptimeStr(idx))
6076 }
6077
6078 InstData::Neg { operand } => {
6080 let n = int(
6081 self.evaluate_comptime_inst(operand, locals, ctx, outer_span)?,
6082 inst_span,
6083 )?;
6084 n.checked_neg()
6085 .map(ConstValue::Integer)
6086 .ok_or_else(|| overflow(inst_span))
6087 }
6088
6089 InstData::Not { operand } => {
6090 let b = bool_val(
6091 self.evaluate_comptime_inst(operand, locals, ctx, outer_span)?,
6092 inst_span,
6093 )?;
6094 Ok(ConstValue::Bool(!b))
6095 }
6096
6097 InstData::BitNot { operand } => {
6098 let n = int(
6099 self.evaluate_comptime_inst(operand, locals, ctx, outer_span)?,
6100 inst_span,
6101 )?;
6102 Ok(ConstValue::Integer(!n))
6103 }
6104
6105 InstData::Add { lhs, rhs } => {
6107 let l = int(
6108 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6109 inst_span,
6110 )?;
6111 let r = int(
6112 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6113 inst_span,
6114 )?;
6115 l.checked_add(r)
6116 .map(ConstValue::Integer)
6117 .ok_or_else(|| overflow(inst_span))
6118 }
6119 InstData::Sub { lhs, rhs } => {
6120 let l = int(
6121 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6122 inst_span,
6123 )?;
6124 let r = int(
6125 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6126 inst_span,
6127 )?;
6128 l.checked_sub(r)
6129 .map(ConstValue::Integer)
6130 .ok_or_else(|| overflow(inst_span))
6131 }
6132 InstData::Mul { lhs, rhs } => {
6133 let l = int(
6134 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6135 inst_span,
6136 )?;
6137 let r = int(
6138 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6139 inst_span,
6140 )?;
6141 l.checked_mul(r)
6142 .map(ConstValue::Integer)
6143 .ok_or_else(|| overflow(inst_span))
6144 }
6145 InstData::Div { lhs, rhs } => {
6146 let l = int(
6147 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6148 inst_span,
6149 )?;
6150 let r = int(
6151 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6152 inst_span,
6153 )?;
6154 if r == 0 {
6155 return Err(CompileError::new(
6156 ErrorKind::ComptimeEvaluationFailed {
6157 reason: "division by zero in comptime evaluation".into(),
6158 },
6159 inst_span,
6160 ));
6161 }
6162 l.checked_div(r)
6163 .map(ConstValue::Integer)
6164 .ok_or_else(|| overflow(inst_span))
6165 }
6166 InstData::Mod { lhs, rhs } => {
6167 let l = int(
6168 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6169 inst_span,
6170 )?;
6171 let r = int(
6172 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6173 inst_span,
6174 )?;
6175 if r == 0 {
6176 return Err(CompileError::new(
6177 ErrorKind::ComptimeEvaluationFailed {
6178 reason: "modulo by zero in comptime evaluation".into(),
6179 },
6180 inst_span,
6181 ));
6182 }
6183 l.checked_rem(r)
6184 .map(ConstValue::Integer)
6185 .ok_or_else(|| overflow(inst_span))
6186 }
6187
6188 InstData::Eq { lhs, rhs } => {
6190 let l = self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?;
6191 let r = self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?;
6192 match (l, r) {
6193 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
6194 Ok(ConstValue::Bool(a == b))
6195 }
6196 (ConstValue::Bool(a), ConstValue::Bool(b)) => Ok(ConstValue::Bool(a == b)),
6197 (ConstValue::ComptimeStr(a), ConstValue::ComptimeStr(b)) => {
6198 let sa = self.resolve_comptime_str(a, inst_span)?;
6199 let sb = self.resolve_comptime_str(b, inst_span)?;
6200 Ok(ConstValue::Bool(sa == sb))
6201 }
6202 _ => Err(not_const(inst_span)),
6203 }
6204 }
6205 InstData::Ne { lhs, rhs } => {
6206 let l = self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?;
6207 let r = self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?;
6208 match (l, r) {
6209 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
6210 Ok(ConstValue::Bool(a != b))
6211 }
6212 (ConstValue::Bool(a), ConstValue::Bool(b)) => Ok(ConstValue::Bool(a != b)),
6213 (ConstValue::ComptimeStr(a), ConstValue::ComptimeStr(b)) => {
6214 let sa = self.resolve_comptime_str(a, inst_span)?;
6215 let sb = self.resolve_comptime_str(b, inst_span)?;
6216 Ok(ConstValue::Bool(sa != sb))
6217 }
6218 _ => Err(not_const(inst_span)),
6219 }
6220 }
6221 InstData::Lt { lhs, rhs } => {
6222 let l = self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?;
6223 let r = self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?;
6224 match (l, r) {
6225 (ConstValue::Integer(a), ConstValue::Integer(b)) => Ok(ConstValue::Bool(a < b)),
6226 (ConstValue::ComptimeStr(a), ConstValue::ComptimeStr(b)) => {
6227 let sa = self.resolve_comptime_str(a, inst_span)?;
6228 let sb = self.resolve_comptime_str(b, inst_span)?;
6229 Ok(ConstValue::Bool(sa < sb))
6230 }
6231 _ => Err(not_const(inst_span)),
6232 }
6233 }
6234 InstData::Gt { lhs, rhs } => {
6235 let l = self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?;
6236 let r = self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?;
6237 match (l, r) {
6238 (ConstValue::Integer(a), ConstValue::Integer(b)) => Ok(ConstValue::Bool(a > b)),
6239 (ConstValue::ComptimeStr(a), ConstValue::ComptimeStr(b)) => {
6240 let sa = self.resolve_comptime_str(a, inst_span)?;
6241 let sb = self.resolve_comptime_str(b, inst_span)?;
6242 Ok(ConstValue::Bool(sa > sb))
6243 }
6244 _ => Err(not_const(inst_span)),
6245 }
6246 }
6247 InstData::Le { lhs, rhs } => {
6248 let l = self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?;
6249 let r = self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?;
6250 match (l, r) {
6251 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
6252 Ok(ConstValue::Bool(a <= b))
6253 }
6254 (ConstValue::ComptimeStr(a), ConstValue::ComptimeStr(b)) => {
6255 let sa = self.resolve_comptime_str(a, inst_span)?;
6256 let sb = self.resolve_comptime_str(b, inst_span)?;
6257 Ok(ConstValue::Bool(sa <= sb))
6258 }
6259 _ => Err(not_const(inst_span)),
6260 }
6261 }
6262 InstData::Ge { lhs, rhs } => {
6263 let l = self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?;
6264 let r = self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?;
6265 match (l, r) {
6266 (ConstValue::Integer(a), ConstValue::Integer(b)) => {
6267 Ok(ConstValue::Bool(a >= b))
6268 }
6269 (ConstValue::ComptimeStr(a), ConstValue::ComptimeStr(b)) => {
6270 let sa = self.resolve_comptime_str(a, inst_span)?;
6271 let sb = self.resolve_comptime_str(b, inst_span)?;
6272 Ok(ConstValue::Bool(sa >= sb))
6273 }
6274 _ => Err(not_const(inst_span)),
6275 }
6276 }
6277
6278 InstData::And { lhs, rhs } => {
6280 let l = bool_val(
6281 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6282 inst_span,
6283 )?;
6284 let r = bool_val(
6285 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6286 inst_span,
6287 )?;
6288 Ok(ConstValue::Bool(l && r))
6289 }
6290 InstData::Or { lhs, rhs } => {
6291 let l = bool_val(
6292 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6293 inst_span,
6294 )?;
6295 let r = bool_val(
6296 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6297 inst_span,
6298 )?;
6299 Ok(ConstValue::Bool(l || r))
6300 }
6301
6302 InstData::BitAnd { lhs, rhs } => {
6304 let l = int(
6305 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6306 inst_span,
6307 )?;
6308 let r = int(
6309 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6310 inst_span,
6311 )?;
6312 Ok(ConstValue::Integer(l & r))
6313 }
6314 InstData::BitOr { lhs, rhs } => {
6315 let l = int(
6316 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6317 inst_span,
6318 )?;
6319 let r = int(
6320 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6321 inst_span,
6322 )?;
6323 Ok(ConstValue::Integer(l | r))
6324 }
6325 InstData::BitXor { lhs, rhs } => {
6326 let l = int(
6327 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6328 inst_span,
6329 )?;
6330 let r = int(
6331 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6332 inst_span,
6333 )?;
6334 Ok(ConstValue::Integer(l ^ r))
6335 }
6336 InstData::Shl { lhs, rhs } => {
6337 let l = int(
6338 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6339 inst_span,
6340 )?;
6341 let r = int(
6342 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6343 inst_span,
6344 )?;
6345 if !(0..64).contains(&r) {
6346 return Err(CompileError::new(
6347 ErrorKind::ComptimeEvaluationFailed {
6348 reason: "shift amount out of range in comptime evaluation".into(),
6349 },
6350 inst_span,
6351 ));
6352 }
6353 Ok(ConstValue::Integer(l << r))
6354 }
6355 InstData::Shr { lhs, rhs } => {
6356 let l = int(
6357 self.evaluate_comptime_inst(lhs, locals, ctx, outer_span)?,
6358 inst_span,
6359 )?;
6360 let r = int(
6361 self.evaluate_comptime_inst(rhs, locals, ctx, outer_span)?,
6362 inst_span,
6363 )?;
6364 if !(0..64).contains(&r) {
6365 return Err(CompileError::new(
6366 ErrorKind::ComptimeEvaluationFailed {
6367 reason: "shift amount out of range in comptime evaluation".into(),
6368 },
6369 inst_span,
6370 ));
6371 }
6372 Ok(ConstValue::Integer(l >> r))
6373 }
6374
6375 InstData::Block { extra_start, len } => {
6377 let raw_refs: Vec<u32> = self.rir.get_extra(extra_start, len).to_vec();
6380 let mut last_val = ConstValue::Unit;
6381 for raw_ref in raw_refs {
6382 last_val = self.evaluate_comptime_inst(
6383 InstRef::from_raw(raw_ref),
6384 locals,
6385 ctx,
6386 outer_span,
6387 )?;
6388 if matches!(
6391 last_val,
6392 ConstValue::BreakSignal
6393 | ConstValue::ContinueSignal
6394 | ConstValue::ReturnSignal
6395 ) {
6396 return Ok(last_val);
6397 }
6398 }
6399 Ok(last_val)
6400 }
6401
6402 InstData::Alloc { name, init, .. } => {
6404 let val = self.evaluate_comptime_inst(init, locals, ctx, outer_span)?;
6405 if let Some(name_sym) = name {
6406 locals.insert(name_sym, val);
6407 }
6408 Ok(ConstValue::Unit)
6409 }
6410
6411 InstData::VarRef { name } => {
6413 if let Some(&val) = locals.get(&name) {
6415 return Ok(val);
6416 }
6417 if let Some(&ty) = self.comptime_type_overrides.get(&name) {
6419 return Ok(ConstValue::Type(ty));
6420 }
6421 if let Some(&ty) = ctx.comptime_type_vars.get(&name) {
6424 return Ok(ConstValue::Type(ty));
6425 }
6426 let name_str = self.interner.resolve(&name).to_string();
6428 let builtin_ty = match name_str.as_str() {
6429 "i8" => Some(Type::I8),
6430 "i16" => Some(Type::I16),
6431 "i32" => Some(Type::I32),
6432 "i64" => Some(Type::I64),
6433 "u8" => Some(Type::U8),
6434 "u16" => Some(Type::U16),
6435 "u32" => Some(Type::U32),
6436 "u64" => Some(Type::U64),
6437 "bool" => Some(Type::BOOL),
6438 "()" => Some(Type::UNIT),
6439 "!" => Some(Type::NEVER),
6440 _ => None,
6441 };
6442 if let Some(ty) = builtin_ty {
6443 return Ok(ConstValue::Type(ty));
6444 }
6445 if let Some(&struct_id) = self.structs.get(&name) {
6447 return Ok(ConstValue::Type(Type::new_struct(struct_id)));
6448 }
6449 if let Some(&enum_id) = self.enums.get(&name) {
6450 return Ok(ConstValue::Type(Type::new_enum(enum_id)));
6451 }
6452 Err(not_const(inst_span))
6454 }
6455
6456 InstData::Assign { name, value } => {
6458 let val = self.evaluate_comptime_inst(value, locals, ctx, outer_span)?;
6459 locals.insert(name, val);
6460 Ok(ConstValue::Unit)
6461 }
6462
6463 InstData::Branch {
6465 cond,
6466 then_block,
6467 else_block,
6468 } => {
6469 let cond_val = self.evaluate_comptime_inst(cond, locals, ctx, outer_span)?;
6470 match cond_val {
6471 ConstValue::Bool(true) => {
6472 self.evaluate_comptime_inst(then_block, locals, ctx, outer_span)
6473 }
6474 ConstValue::Bool(false) => {
6475 if let Some(else_ref) = else_block {
6476 self.evaluate_comptime_inst(else_ref, locals, ctx, outer_span)
6477 } else {
6478 Ok(ConstValue::Unit)
6479 }
6480 }
6481 _ => Err(not_const(inst_span)),
6482 }
6483 }
6484
6485 InstData::Comptime { expr } => {
6487 self.evaluate_comptime_inst(expr, locals, ctx, outer_span)
6488 }
6489
6490 InstData::FnDecl { .. }
6492 | InstData::DropFnDecl { .. }
6493 | InstData::ConstDecl { .. }
6494 | InstData::StructDecl { .. }
6495 | InstData::EnumDecl { .. } => Ok(ConstValue::Unit),
6496
6497 InstData::AnonStructType { .. }
6501 | InstData::AnonEnumType { .. }
6502 | InstData::TypeConst { .. } => self
6503 .try_evaluate_const(inst_ref)
6504 .ok_or_else(|| not_const(inst_span)),
6505
6506 InstData::Loop { cond, body } => {
6509 const COMPTIME_MAX_STEPS: u64 = 1_000_000;
6510 loop {
6511 let cond_val = self.evaluate_comptime_inst(cond, locals, ctx, outer_span)?;
6512 if !bool_val(cond_val, inst_span)? {
6513 break;
6514 }
6515 self.comptime_steps_used += 1;
6516 if self.comptime_steps_used > COMPTIME_MAX_STEPS {
6517 return Err(CompileError::new(
6518 ErrorKind::ComptimeEvaluationFailed {
6519 reason: format!(
6520 "comptime evaluation exceeded step budget of {} iterations",
6521 COMPTIME_MAX_STEPS
6522 ),
6523 },
6524 inst_span,
6525 ));
6526 }
6527 match self.evaluate_comptime_inst(body, locals, ctx, outer_span)? {
6528 ConstValue::BreakSignal => break,
6529 ConstValue::ContinueSignal => continue,
6530 _ => {}
6531 }
6532 }
6533 Ok(ConstValue::Unit)
6534 }
6535
6536 InstData::For { .. } => Err(not_const(inst_span)),
6539
6540 InstData::InfiniteLoop { body } => {
6543 const COMPTIME_MAX_STEPS: u64 = 1_000_000;
6544 loop {
6545 self.comptime_steps_used += 1;
6546 if self.comptime_steps_used > COMPTIME_MAX_STEPS {
6547 return Err(CompileError::new(
6548 ErrorKind::ComptimeEvaluationFailed {
6549 reason: format!(
6550 "comptime evaluation exceeded step budget of {} iterations",
6551 COMPTIME_MAX_STEPS
6552 ),
6553 },
6554 inst_span,
6555 ));
6556 }
6557 match self.evaluate_comptime_inst(body, locals, ctx, outer_span)? {
6558 ConstValue::BreakSignal => break,
6559 ConstValue::ContinueSignal => continue,
6560 _ => {}
6561 }
6562 }
6563 Ok(ConstValue::Unit)
6564 }
6565
6566 InstData::Break => Ok(ConstValue::BreakSignal),
6568 InstData::Continue => Ok(ConstValue::ContinueSignal),
6569
6570 InstData::Ret(opt_ref) => {
6574 let return_val = match opt_ref {
6575 Some(val_ref) => {
6576 self.evaluate_comptime_inst(val_ref, locals, ctx, outer_span)?
6577 }
6578 None => ConstValue::Unit,
6579 };
6580 self.comptime_return_value = Some(return_val);
6581 Ok(ConstValue::ReturnSignal)
6582 }
6583
6584 InstData::Call {
6587 name,
6588 args_start,
6589 args_len,
6590 } => {
6591 const COMPTIME_CALL_DEPTH_LIMIT: u32 = 64;
6592
6593 let fn_info = match self.functions.get(&name) {
6595 Some(info) => *info,
6596 None => return Err(not_const(inst_span)),
6597 };
6598
6599 let call_args = self.rir.get_call_args(args_start, args_len);
6601 let mut arg_values = Vec::with_capacity(call_args.len());
6602 for call_arg in &call_args {
6603 let val =
6604 self.evaluate_comptime_inst(call_arg.value, locals, ctx, outer_span)?;
6605 arg_values.push(val);
6606 }
6607
6608 let param_comptime = self.param_arena.comptime(fn_info.params).to_vec();
6612 let param_names = self.param_arena.names(fn_info.params).to_vec();
6613 let mut type_overrides: HashMap<Spur, Type> = HashMap::new();
6614 if fn_info.is_generic {
6615 for (i, is_comptime) in param_comptime.iter().enumerate() {
6616 if *is_comptime && let Some(ConstValue::Type(ty)) = arg_values.get(i) {
6617 type_overrides.insert(param_names[i], *ty);
6618 }
6619 }
6620 }
6621
6622 if self.comptime_call_depth >= COMPTIME_CALL_DEPTH_LIMIT {
6624 return Err(CompileError::new(
6625 ErrorKind::ComptimeEvaluationFailed {
6626 reason: format!(
6627 "comptime call stack depth exceeded {} levels (possible infinite recursion)",
6628 COMPTIME_CALL_DEPTH_LIMIT
6629 ),
6630 },
6631 inst_span,
6632 ));
6633 }
6634
6635 let mut call_locals: HashMap<Spur, ConstValue> =
6639 HashMap::with_capacity(param_names.len());
6640 for (i, (param_name, arg_val)) in
6641 param_names.iter().zip(arg_values.iter()).enumerate()
6642 {
6643 if !param_comptime.get(i).copied().unwrap_or(false) {
6644 call_locals.insert(*param_name, *arg_val);
6645 }
6646 }
6647
6648 let saved_overrides =
6650 std::mem::replace(&mut self.comptime_type_overrides, type_overrides);
6651
6652 self.comptime_call_depth += 1;
6654 let body_result =
6655 self.evaluate_comptime_inst(fn_info.body, &mut call_locals, ctx, outer_span);
6656 self.comptime_call_depth -= 1;
6657
6658 self.comptime_type_overrides = saved_overrides;
6660
6661 let body_result = body_result?;
6662
6663 match body_result {
6665 ConstValue::ReturnSignal => {
6666 self.comptime_return_value.take().ok_or_else(|| {
6668 CompileError::new(
6669 ErrorKind::ComptimeEvaluationFailed {
6670 reason: "comptime return signal missing its value".into(),
6671 },
6672 inst_span,
6673 )
6674 })
6675 }
6676 ConstValue::BreakSignal | ConstValue::ContinueSignal => {
6677 Err(CompileError::new(
6679 ErrorKind::ComptimeEvaluationFailed {
6680 reason: "break/continue outside a loop in comptime function".into(),
6681 },
6682 inst_span,
6683 ))
6684 }
6685 val => Ok(val),
6686 }
6687 }
6688
6689 InstData::StructInit {
6691 module,
6692 type_name,
6693 fields_start,
6694 fields_len,
6695 } => {
6696 if module.is_some() {
6698 return Err(not_const(inst_span));
6699 }
6700
6701 let struct_id = match self.resolve_comptime_struct(type_name, ctx) {
6703 Some(id) => id,
6704 None => return Err(not_const(inst_span)),
6705 };
6706
6707 let struct_def = self.type_pool.struct_def(struct_id);
6709 let field_count = struct_def.fields.len();
6710
6711 let field_index_map: std::collections::HashMap<String, usize> = struct_def
6713 .fields
6714 .iter()
6715 .enumerate()
6716 .map(|(i, f)| (f.name.clone(), i))
6717 .collect();
6718
6719 let field_inits = self.rir.get_field_inits(fields_start, fields_len);
6721
6722 let mut field_values = vec![ConstValue::Unit; field_count];
6724 for (field_name_sym, field_value_ref) in &field_inits {
6725 let field_name = self.interner.resolve(field_name_sym).to_string();
6726 let idx = match field_index_map.get(&field_name) {
6727 Some(&i) => i,
6728 None => return Err(not_const(inst_span)),
6729 };
6730 let val =
6731 self.evaluate_comptime_inst(*field_value_ref, locals, ctx, outer_span)?;
6732 field_values[idx] = val;
6733 }
6734
6735 let heap_idx = self.comptime_heap.len() as u32;
6737 self.comptime_heap.push(ComptimeHeapItem::Struct {
6738 struct_id,
6739 fields: field_values,
6740 });
6741 Ok(ConstValue::Struct(heap_idx))
6742 }
6743
6744 InstData::FieldGet { base, field } => {
6746 let base_val = self.evaluate_comptime_inst(base, locals, ctx, outer_span)?;
6747 match base_val {
6748 ConstValue::Struct(heap_idx) => {
6749 let (struct_id, fields) = match &self.comptime_heap[heap_idx as usize] {
6751 ComptimeHeapItem::Struct { struct_id, fields } => {
6752 (*struct_id, fields.clone())
6753 }
6754 _ => return Err(not_const(inst_span)),
6755 };
6756 let struct_def = self.type_pool.struct_def(struct_id);
6757 let field_name = self.interner.resolve(&field);
6758 let (field_idx, _) =
6759 struct_def.find_field(field_name).ok_or_else(|| {
6760 CompileError::new(
6761 ErrorKind::ComptimeEvaluationFailed {
6762 reason: format!(
6763 "unknown field '{}' in comptime struct",
6764 field_name
6765 ),
6766 },
6767 inst_span,
6768 )
6769 })?;
6770 Ok(fields[field_idx])
6771 }
6772 _ => Err(not_const(inst_span)),
6773 }
6774 }
6775
6776 InstData::ArrayInit {
6778 elems_start,
6779 elems_len,
6780 } => {
6781 let elem_refs = self.rir.get_inst_refs(elems_start, elems_len);
6782 let mut elem_values = Vec::with_capacity(elem_refs.len());
6783 for elem_ref in &elem_refs {
6784 let val = self.evaluate_comptime_inst(*elem_ref, locals, ctx, outer_span)?;
6785 elem_values.push(val);
6786 }
6787 let heap_idx = self.comptime_heap.len() as u32;
6788 self.comptime_heap
6789 .push(ComptimeHeapItem::Array(elem_values));
6790 Ok(ConstValue::Array(heap_idx))
6791 }
6792
6793 InstData::IndexGet { base, index } => {
6795 let base_val = self.evaluate_comptime_inst(base, locals, ctx, outer_span)?;
6796 let index_val = self.evaluate_comptime_inst(index, locals, ctx, outer_span)?;
6797 match base_val {
6798 ConstValue::Array(heap_idx) => {
6799 let idx = int(index_val, inst_span)?;
6800 let elems = match &self.comptime_heap[heap_idx as usize] {
6802 ComptimeHeapItem::Array(elems) => elems.clone(),
6803 _ => return Err(not_const(inst_span)),
6804 };
6805 if idx < 0 || idx as usize >= elems.len() {
6806 return Err(CompileError::new(
6807 ErrorKind::ComptimeEvaluationFailed {
6808 reason: format!(
6809 "array index {} out of bounds (length {})",
6810 idx,
6811 elems.len()
6812 ),
6813 },
6814 inst_span,
6815 ));
6816 }
6817 Ok(elems[idx as usize])
6818 }
6819 _ => Err(not_const(inst_span)),
6820 }
6821 }
6822
6823 InstData::FieldSet { base, field, value } => {
6825 let var_name = match &self.rir.get(base).data {
6827 InstData::VarRef { name } => *name,
6828 _ => return Err(not_const(inst_span)),
6829 };
6830 let heap_idx = match locals.get(&var_name) {
6831 Some(ConstValue::Struct(idx)) => *idx,
6832 _ => return Err(not_const(inst_span)),
6833 };
6834 let val = self.evaluate_comptime_inst(value, locals, ctx, outer_span)?;
6835 let struct_id = match &self.comptime_heap[heap_idx as usize] {
6837 ComptimeHeapItem::Struct { struct_id, .. } => *struct_id,
6838 _ => return Err(not_const(inst_span)),
6839 };
6840 let struct_def = self.type_pool.struct_def(struct_id);
6841 let field_name = self.interner.resolve(&field);
6842 let (field_idx, _) = struct_def.find_field(field_name).ok_or_else(|| {
6843 CompileError::new(
6844 ErrorKind::ComptimeEvaluationFailed {
6845 reason: format!("unknown field '{}' in comptime struct", field_name),
6846 },
6847 inst_span,
6848 )
6849 })?;
6850 match &mut self.comptime_heap[heap_idx as usize] {
6852 ComptimeHeapItem::Struct { fields, .. } => {
6853 fields[field_idx] = val;
6854 }
6855 _ => return Err(not_const(inst_span)),
6856 }
6857 Ok(ConstValue::Unit)
6858 }
6859
6860 InstData::IndexSet { base, index, value } => {
6862 let var_name = match &self.rir.get(base).data {
6864 InstData::VarRef { name } => *name,
6865 _ => return Err(not_const(inst_span)),
6866 };
6867 let heap_idx = match locals.get(&var_name) {
6868 Some(ConstValue::Array(idx)) => *idx,
6869 _ => return Err(not_const(inst_span)),
6870 };
6871 let idx = int(
6872 self.evaluate_comptime_inst(index, locals, ctx, outer_span)?,
6873 inst_span,
6874 )?;
6875 let val = self.evaluate_comptime_inst(value, locals, ctx, outer_span)?;
6876 let len = match &self.comptime_heap[heap_idx as usize] {
6878 ComptimeHeapItem::Array(elems) => elems.len(),
6879 _ => return Err(not_const(inst_span)),
6880 };
6881 if idx < 0 || idx as usize >= len {
6882 return Err(CompileError::new(
6883 ErrorKind::ComptimeEvaluationFailed {
6884 reason: format!("array index {} out of bounds (length {})", idx, len),
6885 },
6886 inst_span,
6887 ));
6888 }
6889 match &mut self.comptime_heap[heap_idx as usize] {
6890 ComptimeHeapItem::Array(elems) => {
6891 elems[idx as usize] = val;
6892 }
6893 _ => return Err(not_const(inst_span)),
6894 }
6895 Ok(ConstValue::Unit)
6896 }
6897
6898 InstData::EnumVariant {
6900 module: _,
6901 type_name,
6902 variant,
6903 } => {
6904 let enum_id = if let Some(&id) = self.enums.get(&type_name) {
6906 id
6907 } else if let Some(&ty) = ctx.comptime_type_vars.get(&type_name) {
6908 match ty.kind() {
6909 TypeKind::Enum(id) => id,
6910 _ => return Err(not_const(inst_span)),
6911 }
6912 } else {
6913 return Err(not_const(inst_span));
6914 };
6915 let enum_def = self.type_pool.enum_def(enum_id);
6916 let variant_name = self.interner.resolve(&variant);
6917 let variant_idx = enum_def
6918 .find_variant(variant_name)
6919 .ok_or_else(|| not_const(inst_span))? as u32;
6920 Ok(ConstValue::EnumVariant {
6921 enum_id,
6922 variant_idx,
6923 })
6924 }
6925
6926 InstData::EnumStructVariant {
6928 module: _,
6929 type_name,
6930 variant,
6931 fields_start,
6932 fields_len,
6933 } => {
6934 let enum_id = if let Some(&id) = self.enums.get(&type_name) {
6936 id
6937 } else if let Some(&ty) = ctx.comptime_type_vars.get(&type_name) {
6938 match ty.kind() {
6939 TypeKind::Enum(id) => id,
6940 _ => return Err(not_const(inst_span)),
6941 }
6942 } else {
6943 return Err(not_const(inst_span));
6944 };
6945 let enum_def = self.type_pool.enum_def(enum_id);
6946 let variant_name = self.interner.resolve(&variant);
6947 let variant_idx = enum_def
6948 .find_variant(variant_name)
6949 .ok_or_else(|| not_const(inst_span))? as u32;
6950 let variant_def = &enum_def.variants[variant_idx as usize];
6951
6952 let field_inits = self.rir.get_field_inits(fields_start, fields_len);
6954 let mut field_values = vec![ConstValue::Unit; variant_def.fields.len()];
6955 for (init_field_name, field_value_ref) in &field_inits {
6956 let field_name_str = self.interner.resolve(init_field_name);
6957 let field_idx = variant_def
6958 .find_field(field_name_str)
6959 .ok_or_else(|| not_const(inst_span))?;
6960 let val =
6961 self.evaluate_comptime_inst(*field_value_ref, locals, ctx, outer_span)?;
6962 field_values[field_idx] = val;
6963 }
6964
6965 let heap_idx = self.comptime_heap.len() as u32;
6966 self.comptime_heap
6967 .push(ComptimeHeapItem::EnumStruct(field_values));
6968 Ok(ConstValue::EnumStruct {
6969 enum_id,
6970 variant_idx,
6971 heap_idx,
6972 })
6973 }
6974
6975 InstData::AssocFnCall {
6977 type_name,
6978 function,
6979 args_start,
6980 args_len,
6981 } => {
6982 let enum_id = if let Some(&id) = self.enums.get(&type_name) {
6984 Some(id)
6985 } else if let Some(&ty) = ctx.comptime_type_vars.get(&type_name) {
6986 match ty.kind() {
6987 TypeKind::Enum(id) => Some(id),
6988 _ => None,
6989 }
6990 } else {
6991 None
6992 };
6993
6994 if let Some(enum_id) = enum_id {
6995 let enum_def = self.type_pool.enum_def(enum_id);
6996 let variant_name = self.interner.resolve(&function);
6997 if let Some(variant_idx) = enum_def.find_variant(variant_name) {
6998 let variant_def = &enum_def.variants[variant_idx];
6999 if variant_def.has_data() && !variant_def.is_struct_variant() {
7000 let call_args = self.rir.get_call_args(args_start, args_len);
7002 let mut field_values = Vec::with_capacity(variant_def.fields.len());
7003 for arg in &call_args {
7004 let val = self
7005 .evaluate_comptime_inst(arg.value, locals, ctx, outer_span)?;
7006 field_values.push(val);
7007 }
7008 let heap_idx = self.comptime_heap.len() as u32;
7009 self.comptime_heap
7010 .push(ComptimeHeapItem::EnumData(field_values));
7011 return Ok(ConstValue::EnumData {
7012 enum_id,
7013 variant_idx: variant_idx as u32,
7014 heap_idx,
7015 });
7016 }
7017 }
7018 }
7019
7020 Err(not_const(inst_span))
7022 }
7023
7024 InstData::Match {
7026 scrutinee,
7027 arms_start,
7028 arms_len,
7029 } => {
7030 let scrut_val = self.evaluate_comptime_inst(scrutinee, locals, ctx, outer_span)?;
7031 let arms = self.rir.get_match_arms(arms_start, arms_len);
7032
7033 for (pattern, body) in &arms {
7034 match pattern {
7035 RirPattern::Wildcard(_) => {
7036 return self.evaluate_comptime_inst(*body, locals, ctx, outer_span);
7038 }
7039 RirPattern::Int(n, _) => {
7040 if let ConstValue::Integer(val) = scrut_val
7041 && val == *n
7042 {
7043 return self.evaluate_comptime_inst(*body, locals, ctx, outer_span);
7044 }
7045 }
7046 RirPattern::Bool(b, _) => {
7047 if let ConstValue::Bool(val) = scrut_val
7048 && val == *b
7049 {
7050 return self.evaluate_comptime_inst(*body, locals, ctx, outer_span);
7051 }
7052 }
7053 RirPattern::Path {
7054 type_name, variant, ..
7055 } => {
7056 let pat_enum_id = self.resolve_comptime_enum(*type_name, ctx);
7058 if let Some(pat_enum_id) = pat_enum_id {
7059 let enum_def = self.type_pool.enum_def(pat_enum_id);
7060 let variant_name = self.interner.resolve(variant);
7061 if let Some(pat_variant_idx) = enum_def.find_variant(variant_name) {
7062 let matches = match scrut_val {
7063 ConstValue::EnumVariant {
7064 enum_id,
7065 variant_idx,
7066 } => {
7067 enum_id == pat_enum_id
7068 && variant_idx == pat_variant_idx as u32
7069 }
7070 ConstValue::EnumData {
7071 enum_id,
7072 variant_idx,
7073 ..
7074 } => {
7075 enum_id == pat_enum_id
7076 && variant_idx == pat_variant_idx as u32
7077 }
7078 ConstValue::EnumStruct {
7079 enum_id,
7080 variant_idx,
7081 ..
7082 } => {
7083 enum_id == pat_enum_id
7084 && variant_idx == pat_variant_idx as u32
7085 }
7086 _ => false,
7087 };
7088 if matches {
7089 return self.evaluate_comptime_inst(
7090 *body, locals, ctx, outer_span,
7091 );
7092 }
7093 }
7094 }
7095 }
7096 RirPattern::DataVariant {
7097 type_name,
7098 variant,
7099 bindings,
7100 ..
7101 } => {
7102 let pat_enum_id = self.resolve_comptime_enum(*type_name, ctx);
7104 if let Some(pat_enum_id) = pat_enum_id {
7105 let enum_def = self.type_pool.enum_def(pat_enum_id);
7106 let variant_name = self.interner.resolve(variant);
7107 if let Some(pat_variant_idx) = enum_def.find_variant(variant_name) {
7108 let (matches, heap_idx_opt) = match scrut_val {
7109 ConstValue::EnumData {
7110 enum_id,
7111 variant_idx,
7112 heap_idx,
7113 } if enum_id == pat_enum_id
7114 && variant_idx == pat_variant_idx as u32 =>
7115 {
7116 (true, Some(heap_idx))
7117 }
7118 _ => (false, None),
7119 };
7120 if matches {
7121 if let Some(heap_idx) = heap_idx_opt {
7123 let field_values =
7124 match &self.comptime_heap[heap_idx as usize] {
7125 ComptimeHeapItem::EnumData(fields) => {
7126 fields.clone()
7127 }
7128 _ => return Err(not_const(inst_span)),
7129 };
7130 for (i, binding) in bindings.iter().enumerate() {
7131 if !binding.is_wildcard
7132 && let Some(name) = binding.name
7133 {
7134 locals.insert(name, field_values[i]);
7135 }
7136 }
7137 }
7138 return self.evaluate_comptime_inst(
7139 *body, locals, ctx, outer_span,
7140 );
7141 }
7142 }
7143 }
7144 }
7145 RirPattern::StructVariant {
7146 type_name,
7147 variant,
7148 field_bindings,
7149 ..
7150 } => {
7151 let pat_enum_id = self.resolve_comptime_enum(*type_name, ctx);
7153 if let Some(pat_enum_id) = pat_enum_id {
7154 let enum_def = self.type_pool.enum_def(pat_enum_id);
7155 let variant_name = self.interner.resolve(variant);
7156 if let Some(pat_variant_idx) = enum_def.find_variant(variant_name) {
7157 let (matches, heap_idx_opt) = match scrut_val {
7158 ConstValue::EnumStruct {
7159 enum_id,
7160 variant_idx,
7161 heap_idx,
7162 } if enum_id == pat_enum_id
7163 && variant_idx == pat_variant_idx as u32 =>
7164 {
7165 (true, Some(heap_idx))
7166 }
7167 _ => (false, None),
7168 };
7169 if matches {
7170 if let Some(heap_idx) = heap_idx_opt {
7171 let field_values =
7172 match &self.comptime_heap[heap_idx as usize] {
7173 ComptimeHeapItem::EnumStruct(fields) => {
7174 fields.clone()
7175 }
7176 _ => return Err(not_const(inst_span)),
7177 };
7178 let variant_def = &enum_def.variants[pat_variant_idx];
7179 for fb in field_bindings {
7180 if !fb.binding.is_wildcard
7181 && let Some(name) = fb.binding.name
7182 {
7183 let field_name_str =
7184 self.interner.resolve(&fb.field_name);
7185 let field_idx = match variant_def
7186 .find_field(field_name_str)
7187 {
7188 Some(idx) => idx,
7189 None => return Err(not_const(inst_span)),
7190 };
7191 locals.insert(name, field_values[field_idx]);
7192 }
7193 }
7194 }
7195 return self.evaluate_comptime_inst(
7196 *body, locals, ctx, outer_span,
7197 );
7198 }
7199 }
7200 }
7201 }
7202 }
7203 }
7204
7205 Err(CompileError::new(
7207 ErrorKind::ComptimeEvaluationFailed {
7208 reason: "no match arm matched in comptime evaluation".into(),
7209 },
7210 inst_span,
7211 ))
7212 }
7213
7214 InstData::StructDestructure {
7216 type_name,
7217 fields_start,
7218 fields_len,
7219 init,
7220 } => {
7221 let init_val = self.evaluate_comptime_inst(init, locals, ctx, outer_span)?;
7223 let heap_idx = match init_val {
7224 ConstValue::Struct(idx) => idx,
7225 _ => return Err(not_const(inst_span)),
7226 };
7227
7228 let struct_id = match self.resolve_comptime_struct(type_name, ctx) {
7230 Some(id) => id,
7231 None => return Err(not_const(inst_span)),
7232 };
7233
7234 let field_values = match &self.comptime_heap[heap_idx as usize] {
7236 ComptimeHeapItem::Struct { fields, .. } => fields.clone(),
7237 _ => return Err(not_const(inst_span)),
7238 };
7239
7240 let struct_def = self.type_pool.struct_def(struct_id);
7242 let field_name_to_idx: HashMap<String, usize> = struct_def
7243 .fields
7244 .iter()
7245 .enumerate()
7246 .map(|(i, f)| (f.name.clone(), i))
7247 .collect();
7248
7249 let destr_fields = self.rir.get_destructure_fields(fields_start, fields_len);
7251 for field in &destr_fields {
7252 if field.is_wildcard {
7253 continue;
7254 }
7255 let field_name = self.interner.resolve(&field.field_name).to_string();
7256 let field_idx = match field_name_to_idx.get(&field_name) {
7257 Some(&idx) => idx,
7258 None => return Err(not_const(inst_span)),
7259 };
7260 let binding_name = field.binding_name.unwrap_or(field.field_name);
7261 locals.insert(binding_name, field_values[field_idx]);
7262 }
7263 Ok(ConstValue::Unit)
7264 }
7265
7266 InstData::MethodCall {
7268 receiver,
7269 method,
7270 args_start,
7271 args_len,
7272 } => {
7273 const COMPTIME_CALL_DEPTH_LIMIT: u32 = 64;
7274
7275 let receiver_val =
7277 self.evaluate_comptime_inst(receiver, locals, ctx, outer_span)?;
7278
7279 if let ConstValue::ComptimeStr(str_idx) = receiver_val {
7281 let method_name = self.interner.resolve(&method).to_string();
7282 let call_args = self.rir.get_call_args(args_start, args_len);
7283 return self.evaluate_comptime_str_method(
7284 str_idx,
7285 &method_name,
7286 &call_args,
7287 locals,
7288 ctx,
7289 outer_span,
7290 inst_span,
7291 );
7292 }
7293
7294 let struct_id = match receiver_val {
7296 ConstValue::Struct(heap_idx) => match &self.comptime_heap[heap_idx as usize] {
7297 ComptimeHeapItem::Struct { struct_id, .. } => *struct_id,
7298 _ => return Err(not_const(inst_span)),
7299 },
7300 _ => return Err(not_const(inst_span)),
7301 };
7302
7303 let method_key = (struct_id, method);
7305 let method_info = match self.methods.get(&method_key) {
7306 Some(info) => *info,
7307 None => return Err(not_const(inst_span)),
7308 };
7309
7310 let call_args = self.rir.get_call_args(args_start, args_len);
7312 let mut arg_values = Vec::with_capacity(call_args.len());
7313 for call_arg in &call_args {
7314 let val =
7315 self.evaluate_comptime_inst(call_arg.value, locals, ctx, outer_span)?;
7316 arg_values.push(val);
7317 }
7318
7319 if self.comptime_call_depth >= COMPTIME_CALL_DEPTH_LIMIT {
7321 return Err(CompileError::new(
7322 ErrorKind::ComptimeEvaluationFailed {
7323 reason: format!(
7324 "comptime call stack depth exceeded {} levels",
7325 COMPTIME_CALL_DEPTH_LIMIT
7326 ),
7327 },
7328 inst_span,
7329 ));
7330 }
7331
7332 let param_names = self.param_arena.names(method_info.params).to_vec();
7334 let mut call_locals: HashMap<Spur, ConstValue> =
7335 HashMap::with_capacity(param_names.len() + 1);
7336 let self_sym = self.interner.get_or_intern("self");
7338 call_locals.insert(self_sym, receiver_val);
7339 for (param_name, arg_val) in param_names.iter().zip(arg_values.iter()) {
7340 call_locals.insert(*param_name, *arg_val);
7341 }
7342
7343 self.comptime_call_depth += 1;
7345 let body_result = self.evaluate_comptime_inst(
7346 method_info.body,
7347 &mut call_locals,
7348 ctx,
7349 outer_span,
7350 );
7351 self.comptime_call_depth -= 1;
7352 let body_result = body_result?;
7353
7354 match body_result {
7355 ConstValue::ReturnSignal => {
7356 self.comptime_return_value.take().ok_or_else(|| {
7357 CompileError::new(
7358 ErrorKind::ComptimeEvaluationFailed {
7359 reason: "comptime return signal missing its value".into(),
7360 },
7361 inst_span,
7362 )
7363 })
7364 }
7365 ConstValue::BreakSignal | ConstValue::ContinueSignal => Err(CompileError::new(
7366 ErrorKind::ComptimeEvaluationFailed {
7367 reason: "break/continue outside a loop in comptime method".into(),
7368 },
7369 inst_span,
7370 )),
7371 val => Ok(val),
7372 }
7373 }
7374
7375 InstData::Intrinsic {
7377 name,
7378 args_start,
7379 args_len,
7380 } => {
7381 if name == self.known.int_cast || name == self.known.cast {
7383 let arg_refs = self.rir.get_inst_refs(args_start, args_len);
7384 if arg_refs.len() != 1 {
7385 return Err(not_const(inst_span));
7386 }
7387 return self.evaluate_comptime_inst(arg_refs[0], locals, ctx, outer_span);
7388 }
7389 if name == self.known.dbg {
7393 let arg_refs = self.rir.get_inst_refs(args_start, args_len);
7394 let mut parts = Vec::with_capacity(arg_refs.len());
7395 for &arg_ref in &arg_refs {
7396 let val = self.evaluate_comptime_inst(arg_ref, locals, ctx, outer_span)?;
7397 parts.push(self.format_const_value(val, inst_span)?);
7398 }
7399 let msg = parts.join(" ");
7400 if !self.suppress_comptime_dbg_print {
7401 eprintln!("comptime dbg: {msg}");
7402 }
7403 self.comptime_dbg_output.push(msg.clone());
7404 self.comptime_log_output.push((msg, inst_span));
7405 return Ok(ConstValue::Unit);
7406 }
7407 if name == self.known.compile_error {
7409 let arg_refs = self.rir.get_inst_refs(args_start, args_len);
7410 if arg_refs.len() != 1 {
7411 return Err(CompileError::new(
7412 ErrorKind::IntrinsicWrongArgCount {
7413 name: "compileError".to_string(),
7414 expected: 1,
7415 found: arg_refs.len(),
7416 },
7417 inst_span,
7418 ));
7419 }
7420 let msg =
7421 self.evaluate_comptime_string_arg(arg_refs[0], locals, ctx, outer_span)?;
7422 return Err(CompileError::new(
7423 ErrorKind::ComptimeUserError(msg),
7424 inst_span,
7425 ));
7426 }
7427 if name == self.known.range {
7429 let arg_refs = self.rir.get_inst_refs(args_start, args_len);
7430 let (start, end, stride) = match arg_refs.len() {
7431 1 => {
7432 let end = int(
7433 self.evaluate_comptime_inst(arg_refs[0], locals, ctx, outer_span)?,
7434 inst_span,
7435 )?;
7436 (0i64, end, 1i64)
7437 }
7438 2 => {
7439 let s = int(
7440 self.evaluate_comptime_inst(arg_refs[0], locals, ctx, outer_span)?,
7441 inst_span,
7442 )?;
7443 let e = int(
7444 self.evaluate_comptime_inst(arg_refs[1], locals, ctx, outer_span)?,
7445 inst_span,
7446 )?;
7447 (s, e, 1i64)
7448 }
7449 3 => {
7450 let s = int(
7451 self.evaluate_comptime_inst(arg_refs[0], locals, ctx, outer_span)?,
7452 inst_span,
7453 )?;
7454 let e = int(
7455 self.evaluate_comptime_inst(arg_refs[1], locals, ctx, outer_span)?,
7456 inst_span,
7457 )?;
7458 let st = int(
7459 self.evaluate_comptime_inst(arg_refs[2], locals, ctx, outer_span)?,
7460 inst_span,
7461 )?;
7462 (s, e, st)
7463 }
7464 _ => {
7465 return Err(CompileError::new(
7466 ErrorKind::IntrinsicWrongArgCount {
7467 name: "range".to_string(),
7468 expected: 1,
7469 found: arg_refs.len(),
7470 },
7471 inst_span,
7472 ));
7473 }
7474 };
7475 if stride == 0 {
7476 return Err(CompileError::new(
7477 ErrorKind::ComptimeEvaluationFailed {
7478 reason: "@range stride must not be zero".into(),
7479 },
7480 inst_span,
7481 ));
7482 }
7483 const MAX_RANGE_ELEMENTS: usize = 1_000_000;
7485 let mut elements = Vec::new();
7486 let mut i = start;
7487 if stride > 0 {
7488 while i < end {
7489 if elements.len() >= MAX_RANGE_ELEMENTS {
7490 return Err(CompileError::new(
7491 ErrorKind::ComptimeEvaluationFailed {
7492 reason: format!(
7493 "@range produces too many elements (limit is {})",
7494 MAX_RANGE_ELEMENTS
7495 ),
7496 },
7497 inst_span,
7498 ));
7499 }
7500 elements.push(ConstValue::Integer(i));
7501 i = i.checked_add(stride).ok_or_else(|| overflow(inst_span))?;
7502 }
7503 } else {
7504 while i > end {
7505 if elements.len() >= MAX_RANGE_ELEMENTS {
7506 return Err(CompileError::new(
7507 ErrorKind::ComptimeEvaluationFailed {
7508 reason: format!(
7509 "@range produces too many elements (limit is {})",
7510 MAX_RANGE_ELEMENTS
7511 ),
7512 },
7513 inst_span,
7514 ));
7515 }
7516 elements.push(ConstValue::Integer(i));
7517 i = i.checked_add(stride).ok_or_else(|| overflow(inst_span))?;
7518 }
7519 }
7520 let idx = self.comptime_heap.len() as u32;
7521 self.comptime_heap.push(ComptimeHeapItem::Array(elements));
7522 return Ok(ConstValue::Array(idx));
7523 }
7524 let intrinsic_name = self.interner.resolve(&name).to_string();
7529 Err(CompileError::new(
7530 ErrorKind::UnknownIntrinsic(intrinsic_name),
7531 inst_span,
7532 ))
7533 }
7534
7535 InstData::TypeIntrinsic { name, type_arg } => {
7537 let intrinsic_name = self.interner.resolve(&name).to_string();
7538 let ty = if let Some(&override_ty) = self.comptime_type_overrides.get(&type_arg) {
7543 override_ty
7544 } else if let Some(&ctx_ty) = ctx.comptime_type_vars.get(&type_arg) {
7545 ctx_ty
7546 } else if let Some(&var_ty) = locals.iter().find_map(|(k, v)| {
7547 if *k == type_arg {
7548 if let ConstValue::Type(t) = v {
7549 Some(t)
7550 } else {
7551 None
7552 }
7553 } else {
7554 None
7555 }
7556 }) {
7557 var_ty
7558 } else {
7559 self.resolve_type(type_arg, inst_span)
7560 .map_err(|_| not_const(inst_span))?
7561 };
7562 match intrinsic_name.as_str() {
7563 "size_of" => {
7564 let slot_count = self.abi_slot_count(ty);
7565 Ok(ConstValue::Integer((slot_count as i64) * 8))
7566 }
7567 "align_of" => {
7568 let slot_count = self.abi_slot_count(ty);
7569 Ok(ConstValue::Integer(if slot_count == 0 { 1 } else { 8 }))
7570 }
7571 "typeName" => {
7572 self.evaluate_comptime_type_name(ty, inst_span)
7573 }
7574 "typeInfo" => {
7575 self.evaluate_comptime_type_info(ty, inst_span)
7576 }
7577 _ => Err(not_const(inst_span)),
7578 }
7579 }
7580
7581 _ => Err(not_const(inst_span)),
7583 }
7584 }
7585
7586 pub(crate) fn is_comptime_type_var(&self, inst_ref: InstRef, ctx: &AnalysisContext) -> bool {
7591 if let InstData::VarRef { name } = &self.rir.get(inst_ref).data {
7592 ctx.comptime_type_vars.contains_key(name)
7593 } else {
7594 false
7595 }
7596 }
7597
7598 fn resolve_comptime_enum(&self, type_name: Spur, ctx: &AnalysisContext) -> Option<EnumId> {
7601 if let Some(&id) = self.enums.get(&type_name) {
7602 Some(id)
7603 } else if let Some(&ty) = self.comptime_type_overrides.get(&type_name) {
7604 match ty.kind() {
7605 TypeKind::Enum(id) => Some(id),
7606 _ => None,
7607 }
7608 } else if let Some(&ty) = ctx.comptime_type_vars.get(&type_name) {
7609 match ty.kind() {
7610 TypeKind::Enum(id) => Some(id),
7611 _ => None,
7612 }
7613 } else {
7614 None
7615 }
7616 }
7617
7618 fn resolve_comptime_struct(&self, type_name: Spur, ctx: &AnalysisContext) -> Option<StructId> {
7621 if let Some(&id) = self.structs.get(&type_name) {
7622 Some(id)
7623 } else if let Some(&ty) = self.comptime_type_overrides.get(&type_name) {
7624 match ty.kind() {
7625 TypeKind::Struct(id) => Some(id),
7626 _ => None,
7627 }
7628 } else if let Some(&ty) = ctx.comptime_type_vars.get(&type_name) {
7629 match ty.kind() {
7630 TypeKind::Struct(id) => Some(id),
7631 _ => None,
7632 }
7633 } else {
7634 None
7635 }
7636 }
7637
7638 fn is_comparison(&self, inst_ref: InstRef) -> bool {
7643 matches!(
7644 self.rir.get(inst_ref).data,
7645 InstData::Lt { .. }
7646 | InstData::Gt { .. }
7647 | InstData::Le { .. }
7648 | InstData::Ge { .. }
7649 | InstData::Eq { .. }
7650 | InstData::Ne { .. }
7651 )
7652 }
7653
7654 fn analyze_builtin_assoc_fn(
7658 &mut self,
7659 air: &mut Air,
7660 ctx: &mut AnalysisContext,
7661 (struct_id, builtin_def): (StructId, &'static BuiltinTypeDef),
7662 function_name: &str,
7663 args: &[RirCallArg],
7664 span: Span,
7665 ) -> CompileResult<AnalysisResult> {
7666 use gruel_builtins::{BuiltinParamType, BuiltinReturnType};
7667
7668 let assoc_fn = builtin_def
7670 .find_associated_fn(function_name)
7671 .ok_or_else(|| {
7672 CompileError::new(
7673 ErrorKind::UndefinedAssocFn {
7674 type_name: builtin_def.name.to_string(),
7675 function_name: function_name.to_string(),
7676 },
7677 span,
7678 )
7679 })?;
7680
7681 if args.len() != assoc_fn.params.len() {
7683 return Err(CompileError::new(
7684 ErrorKind::WrongArgumentCount {
7685 expected: assoc_fn.params.len(),
7686 found: args.len(),
7687 },
7688 span,
7689 ));
7690 }
7691
7692 let mut air_args: Vec<(AirRef, AirArgMode)> = Vec::with_capacity(args.len());
7694 for (i, arg) in args.iter().enumerate() {
7695 let arg_result = self.analyze_inst(air, arg.value, ctx)?;
7696
7697 let expected_ty = match assoc_fn.params[i].ty {
7699 BuiltinParamType::U64 => Type::U64,
7700 BuiltinParamType::U8 => Type::U8,
7701 BuiltinParamType::Bool => Type::BOOL,
7702 BuiltinParamType::SelfType => Type::new_struct(struct_id),
7703 };
7704
7705 if arg_result.ty != expected_ty && !arg_result.ty.is_error() {
7707 return Err(CompileError::new(
7708 ErrorKind::TypeMismatch {
7709 expected: expected_ty.name().to_string(),
7710 found: arg_result.ty.name().to_string(),
7711 },
7712 span,
7713 ));
7714 }
7715
7716 air_args.push((arg_result.air_ref, AirArgMode::Normal));
7717 }
7718
7719 let return_ty = match assoc_fn.return_ty {
7722 BuiltinReturnType::Unit => Type::UNIT,
7723 BuiltinReturnType::U64 => Type::U64,
7724 BuiltinReturnType::U8 => Type::U8,
7725 BuiltinReturnType::Bool => Type::BOOL,
7726 BuiltinReturnType::SelfType => self.builtin_air_type(struct_id),
7727 };
7728
7729 let call_name = self.interner.get_or_intern(assoc_fn.runtime_fn);
7731
7732 let mut extra_data: Vec<u32> = Vec::with_capacity(air_args.len() * 2);
7734 for (air_ref, mode) in &air_args {
7735 extra_data.push(air_ref.as_u32());
7736 extra_data.push(mode.as_u32());
7737 }
7738 let args_start = air.add_extra(&extra_data);
7739
7740 let air_ref = air.add_inst(AirInst {
7741 data: AirInstData::Call {
7742 name: call_name,
7743 args_start,
7744 args_len: air_args.len() as u32,
7745 },
7746 ty: return_ty,
7747 span,
7748 });
7749
7750 Ok(AnalysisResult::new(air_ref, return_ty))
7751 }
7752
7753 fn analyze_builtin_method(
7759 &mut self,
7760 air: &mut Air,
7761 ctx: &mut AnalysisContext,
7762 method_ctx: &BuiltinMethodContext<'_>,
7763 receiver: ReceiverInfo,
7764 args: &[RirCallArg],
7765 ) -> CompileResult<AnalysisResult> {
7766 use gruel_builtins::{BuiltinParamType, BuiltinReturnType, ReceiverMode};
7767
7768 let method = method_ctx
7770 .builtin_def
7771 .find_method(method_ctx.method_name)
7772 .ok_or_else(|| {
7773 CompileError::new(
7774 ErrorKind::UndefinedMethod {
7775 type_name: method_ctx.builtin_def.name.to_string(),
7776 method_name: method_ctx.method_name.to_string(),
7777 },
7778 method_ctx.span,
7779 )
7780 })?;
7781
7782 match method.receiver_mode {
7784 ReceiverMode::ByRef => {
7785 if let Some(var_symbol) = receiver.var {
7787 ctx.moved_vars.remove(&var_symbol);
7788 }
7789 }
7790 ReceiverMode::ByMutRef => {
7791 if let Some(var_symbol) = receiver.var {
7793 ctx.moved_vars.remove(&var_symbol);
7794 }
7795 }
7796 ReceiverMode::ByValue => {
7797 }
7799 }
7800
7801 if args.len() != method.params.len() {
7803 return Err(CompileError::new(
7804 ErrorKind::WrongArgumentCount {
7805 expected: method.params.len(),
7806 found: args.len(),
7807 },
7808 method_ctx.span,
7809 ));
7810 }
7811
7812 let mut air_args: Vec<(AirRef, AirArgMode)> = Vec::with_capacity(args.len() + 1);
7814
7815 air_args.push((receiver.result.air_ref, AirArgMode::Normal));
7817
7818 for (i, arg) in args.iter().enumerate() {
7820 let arg_result = self.analyze_inst(air, arg.value, ctx)?;
7821
7822 let expected_ty = match method.params[i].ty {
7824 BuiltinParamType::U64 => Type::U64,
7825 BuiltinParamType::U8 => Type::U8,
7826 BuiltinParamType::Bool => Type::BOOL,
7827 BuiltinParamType::SelfType => Type::new_struct(method_ctx.struct_id),
7828 };
7829
7830 if arg_result.ty != expected_ty
7832 && !arg_result.ty.is_error()
7833 && !(self.is_builtin_string(arg_result.ty)
7834 && matches!(method.params[i].ty, BuiltinParamType::SelfType))
7835 {
7836 return Err(CompileError::new(
7837 ErrorKind::TypeMismatch {
7838 expected: expected_ty.name().to_string(),
7839 found: arg_result.ty.name().to_string(),
7840 },
7841 method_ctx.span,
7842 ));
7843 }
7844
7845 air_args.push((arg_result.air_ref, AirArgMode::Normal));
7846 }
7847
7848 let return_ty = match method.return_ty {
7851 BuiltinReturnType::Unit => Type::UNIT,
7852 BuiltinReturnType::U64 => Type::U64,
7853 BuiltinReturnType::U8 => Type::U8,
7854 BuiltinReturnType::Bool => Type::BOOL,
7855 BuiltinReturnType::SelfType => self.builtin_air_type(method_ctx.struct_id),
7856 };
7857
7858 let call_name = self.interner.get_or_intern(method.runtime_fn);
7860
7861 let mut extra_data: Vec<u32> = Vec::with_capacity(air_args.len() * 2);
7863 for (air_ref, mode) in &air_args {
7864 extra_data.push(air_ref.as_u32());
7865 extra_data.push(mode.as_u32());
7866 }
7867 let args_start = air.add_extra(&extra_data);
7868
7869 let call_ref = air.add_inst(AirInst {
7870 data: AirInstData::Call {
7871 name: call_name,
7872 args_start,
7873 args_len: air_args.len() as u32,
7874 },
7875 ty: return_ty,
7876 span: method_ctx.span,
7877 });
7878
7879 if method.receiver_mode == ReceiverMode::ByMutRef {
7881 let storage = receiver.storage.ok_or_else(|| {
7882 CompileError::new(ErrorKind::InvalidAssignmentTarget, method_ctx.span)
7883 })?;
7884 return self.store_string_result(air, call_ref, storage, method_ctx.span);
7885 }
7886
7887 Ok(AnalysisResult::new(call_ref, return_ty))
7888 }
7889
7890 fn get_string_receiver_storage(
7901 &self,
7902 receiver_ref: InstRef,
7903 ctx: &AnalysisContext,
7904 span: Span,
7905 ) -> CompileResult<Option<StringReceiverStorage>> {
7906 let receiver_inst = self.rir.get(receiver_ref);
7907
7908 match &receiver_inst.data {
7909 InstData::VarRef { name } => {
7910 if let Some(param_info) = ctx.params.iter().find(|p| p.name == *name) {
7912 match param_info.mode {
7914 RirParamMode::Inout => {
7915 return Ok(Some(StringReceiverStorage::Param {
7916 abi_slot: param_info.abi_slot,
7917 }));
7918 }
7919 RirParamMode::Borrow => {
7920 let name_str = self.interner.resolve(name);
7921 return Err(CompileError::new(
7922 ErrorKind::MutateBorrowedValue {
7923 variable: name_str.to_string(),
7924 },
7925 span,
7926 ));
7927 }
7928 RirParamMode::Normal | RirParamMode::Comptime => {
7929 let name_str = self.interner.resolve(name);
7931 return Err(CompileError::new(
7932 ErrorKind::AssignToImmutable(name_str.to_string()),
7933 span,
7934 ));
7935 }
7936 }
7937 }
7938
7939 if let Some(local) = ctx.locals.get(name) {
7941 if !local.is_mut {
7942 let name_str = self.interner.resolve(name);
7943 return Err(CompileError::new(
7944 ErrorKind::AssignToImmutable(name_str.to_string()),
7945 span,
7946 ));
7947 }
7948 return Ok(Some(StringReceiverStorage::Local { slot: local.slot }));
7949 }
7950
7951 let name_str = self.interner.resolve(name);
7953 Err(CompileError::new(
7954 ErrorKind::UndefinedVariable(name_str.to_string()),
7955 span,
7956 ))
7957 }
7958
7959 _ => Err(CompileError::new(ErrorKind::InvalidAssignmentTarget, span)),
7962 }
7963 }
7964
7965 fn store_string_result(
7969 &self,
7970 air: &mut Air,
7971 call_ref: AirRef,
7972 storage: StringReceiverStorage,
7973 span: Span,
7974 ) -> CompileResult<AnalysisResult> {
7975 let store_ref = match storage {
7976 StringReceiverStorage::Local { slot } => air.add_inst(AirInst {
7977 data: AirInstData::Store {
7978 slot,
7979 value: call_ref,
7980 had_live_value: false,
7983 },
7984 ty: Type::UNIT,
7985 span,
7986 }),
7987 StringReceiverStorage::Param { abi_slot } => air.add_inst(AirInst {
7988 data: AirInstData::ParamStore {
7989 param_slot: abi_slot,
7990 value: call_ref,
7991 },
7992 ty: Type::UNIT,
7993 span,
7994 }),
7995 };
7996
7997 Ok(AnalysisResult::new(store_ref, Type::UNIT))
7998 }
7999
8000 pub(crate) fn has_allow_directive(
8002 &self,
8003 directives: &[RirDirective],
8004 warning_name: &str,
8005 ) -> bool {
8006 let allow_sym = self.interner.get("allow");
8007 let warning_sym = self.interner.get(warning_name);
8008
8009 for directive in directives {
8010 if Some(directive.name) == allow_sym {
8011 for arg in &directive.args {
8012 if Some(*arg) == warning_sym {
8013 return true;
8014 }
8015 }
8016 }
8017 }
8018 false
8019 }
8020
8021 pub(crate) fn check_unused_locals_in_current_scope(&self, ctx: &mut AnalysisContext) {
8024 let Some(current_scope) = ctx.scope_stack.last() else {
8026 return;
8027 };
8028
8029 for (symbol, _old_value) in current_scope {
8030 if ctx.used_locals.contains(symbol) {
8032 continue;
8033 }
8034
8035 let Some(local) = ctx.locals.get(symbol) else {
8037 continue;
8038 };
8039
8040 let name = self.interner.resolve(symbol);
8042
8043 if name.starts_with('_') {
8045 continue;
8046 }
8047
8048 if local.allow_unused {
8050 continue;
8051 }
8052
8053 ctx.warnings.push(
8055 CompileWarning::new(WarningKind::UnusedVariable(name.to_string()), local.span)
8056 .with_help(format!(
8057 "if this is intentional, prefix it with an underscore: `_{}`",
8058 name
8059 )),
8060 );
8061 }
8062 }
8063
8064 pub(crate) fn check_unconsumed_linear_values(
8068 &self,
8069 ctx: &AnalysisContext,
8070 ) -> CompileResult<()> {
8071 let Some(current_scope) = ctx.scope_stack.last() else {
8073 return Ok(());
8074 };
8075
8076 for (symbol, _old_value) in current_scope {
8077 let Some(local) = ctx.locals.get(symbol) else {
8079 continue;
8080 };
8081
8082 if !self.is_type_linear(local.ty) {
8084 continue;
8085 }
8086
8087 let was_consumed = ctx
8089 .moved_vars
8090 .get(symbol)
8091 .is_some_and(|state| state.full_move.is_some());
8092
8093 if !was_consumed {
8094 let name = self.interner.resolve(symbol);
8095 return Err(CompileError::new(
8096 ErrorKind::LinearValueNotConsumed(name.to_string()),
8097 local.span,
8098 ));
8099 }
8100 }
8101
8102 Ok(())
8103 }
8104
8105 pub(crate) fn extract_root_variable(&self, inst_ref: InstRef) -> Option<Spur> {
8118 let inst = self.rir.get(inst_ref);
8119 match &inst.data {
8120 InstData::VarRef { name } => Some(*name),
8121 InstData::ParamRef { name, .. } => Some(*name),
8122 InstData::FieldGet { base, .. } => self.extract_root_variable(*base),
8123 InstData::IndexGet { base, .. } => self.extract_root_variable(*base),
8124 _ => None,
8125 }
8126 }
8127
8128 pub(crate) fn check_exclusive_access(
8137 &self,
8138 args: &[RirCallArg],
8139 call_span: Span,
8140 ) -> CompileResult<()> {
8141 use std::collections::HashSet;
8142 let mut inout_vars: HashSet<Spur> = HashSet::new();
8143 let mut borrow_vars: HashSet<Spur> = HashSet::new();
8144
8145 for arg in args {
8146 let maybe_var_symbol = self.extract_root_variable(arg.value);
8147
8148 if arg.is_inout() && maybe_var_symbol.is_none() {
8150 return Err(CompileError::new(
8151 ErrorKind::InoutNonLvalue,
8152 self.rir.get(arg.value).span,
8153 ));
8154 }
8155 if arg.is_borrow() && maybe_var_symbol.is_none() {
8156 return Err(CompileError::new(
8157 ErrorKind::BorrowNonLvalue,
8158 self.rir.get(arg.value).span,
8159 ));
8160 }
8161
8162 if let Some(var_symbol) = maybe_var_symbol {
8163 if arg.is_inout() {
8164 if !inout_vars.insert(var_symbol) {
8166 let var_name = self.interner.resolve(&var_symbol).to_string();
8167 return Err(CompileError::new(
8168 ErrorKind::InoutExclusiveAccess { variable: var_name },
8169 call_span,
8170 ));
8171 }
8172 if borrow_vars.contains(&var_symbol) {
8174 let var_name = self.interner.resolve(&var_symbol).to_string();
8175 return Err(CompileError::new(
8176 ErrorKind::BorrowInoutConflict { variable: var_name },
8177 call_span,
8178 ));
8179 }
8180 } else if arg.is_borrow() {
8181 borrow_vars.insert(var_symbol);
8182 if inout_vars.contains(&var_symbol) {
8184 let var_name = self.interner.resolve(&var_symbol).to_string();
8185 return Err(CompileError::new(
8186 ErrorKind::BorrowInoutConflict { variable: var_name },
8187 call_span,
8188 ));
8189 }
8190 }
8191 }
8192 }
8193 Ok(())
8194 }
8195
8196 pub(crate) fn analyze_call_args(
8201 &mut self,
8202 air: &mut Air,
8203 args: &[RirCallArg],
8204 ctx: &mut AnalysisContext,
8205 ) -> CompileResult<Vec<AirCallArg>> {
8206 let mut air_args = Vec::new();
8207 for arg in args.iter() {
8208 let borrowed_var = if arg.is_inout() || arg.is_borrow() {
8211 self.extract_root_variable(arg.value)
8212 } else {
8213 None
8214 };
8215
8216 let arg_result = self.analyze_inst(air, arg.value, ctx)?;
8217
8218 if let Some(var_symbol) = borrowed_var {
8221 ctx.moved_vars.remove(&var_symbol);
8222 }
8223
8224 air_args.push(AirCallArg {
8225 value: arg_result.air_ref,
8226 mode: AirArgMode::from(arg.mode),
8227 });
8228 }
8229 Ok(air_args)
8230 }
8231
8232 fn register_anon_struct_methods_for_comptime_with_subst(
8238 &mut self,
8239 spec: AnonStructSpec,
8240 _span: Span,
8241 type_subst: &std::collections::HashMap<Spur, Type>,
8242 _value_subst: &std::collections::HashMap<Spur, ConstValue>,
8243 ) -> Option<()> {
8244 let AnonStructSpec {
8245 struct_id,
8246 struct_type,
8247 methods_start,
8248 methods_len,
8249 } = spec;
8250 let method_refs = self.rir.get_inst_refs(methods_start, methods_len);
8251
8252 let mut seen_methods: std::collections::HashSet<Spur> = std::collections::HashSet::new();
8253
8254 for method_ref in method_refs {
8255 let method_inst = self.rir.get(method_ref);
8256 if let InstData::FnDecl {
8257 name: method_name,
8258 is_unchecked,
8259 params_start,
8260 params_len,
8261 return_type,
8262 body,
8263 has_self,
8264 ..
8265 } = &method_inst.data
8266 {
8267 let key = (struct_id, *method_name);
8268
8269 if seen_methods.contains(method_name) {
8270 return None;
8271 }
8272 seen_methods.insert(*method_name);
8273
8274 if self.methods.contains_key(&key) {
8275 return None;
8276 }
8277
8278 let params = self.rir.get_params(*params_start, *params_len);
8279 let param_names: Vec<Spur> = params.iter().map(|p| p.name).collect();
8280 let mut param_types: Vec<Type> = Vec::with_capacity(params.len());
8281
8282 for p in params {
8283 let type_str = self.interner.resolve(&p.ty);
8284 let resolved_ty = if type_str == "Self" {
8285 struct_type
8286 } else {
8287 self.resolve_type_for_comptime_with_subst(p.ty, type_subst)?
8288 };
8289 param_types.push(resolved_ty);
8290 }
8291
8292 let ret_type_str = self.interner.resolve(return_type);
8293 let ret_type = if ret_type_str == "Self" {
8294 struct_type
8295 } else {
8296 self.resolve_type_for_comptime_with_subst(*return_type, type_subst)?
8297 };
8298
8299 let param_range = self
8300 .param_arena
8301 .alloc_method(param_names.into_iter(), param_types.into_iter());
8302
8303 self.methods.insert(
8304 key,
8305 MethodInfo {
8306 struct_type,
8307 has_self: *has_self,
8308 params: param_range,
8309 return_type: ret_type,
8310 body: *body,
8311 span: method_inst.span,
8312 is_unchecked: *is_unchecked,
8313 },
8314 );
8315 }
8316 }
8317 Some(())
8318 }
8319
8320 fn register_anon_enum_methods_for_comptime_with_subst(
8325 &mut self,
8326 enum_id: EnumId,
8327 enum_type: crate::types::Type,
8328 methods_start: u32,
8329 methods_len: u32,
8330 type_subst: &std::collections::HashMap<Spur, Type>,
8331 ) -> Option<()> {
8332 let method_refs = self.rir.get_inst_refs(methods_start, methods_len);
8333
8334 let mut seen_methods: std::collections::HashSet<Spur> = std::collections::HashSet::new();
8335
8336 for method_ref in method_refs {
8337 let method_inst = self.rir.get(method_ref);
8338 if let InstData::FnDecl {
8339 name: method_name,
8340 is_unchecked,
8341 params_start,
8342 params_len,
8343 return_type,
8344 body,
8345 has_self,
8346 ..
8347 } = &method_inst.data
8348 {
8349 let key = (enum_id, *method_name);
8350
8351 if seen_methods.contains(method_name) {
8352 return None;
8353 }
8354 seen_methods.insert(*method_name);
8355
8356 if self.enum_methods.contains_key(&key) {
8357 return None;
8358 }
8359
8360 let params = self.rir.get_params(*params_start, *params_len);
8361 let param_names: Vec<Spur> = params.iter().map(|p| p.name).collect();
8362 let mut param_types: Vec<Type> = Vec::with_capacity(params.len());
8363
8364 for p in params {
8365 let type_str = self.interner.resolve(&p.ty);
8366 let resolved_ty = if type_str == "Self" {
8367 enum_type
8368 } else {
8369 self.resolve_type_for_comptime_with_subst(p.ty, type_subst)?
8370 };
8371 param_types.push(resolved_ty);
8372 }
8373
8374 let ret_type_str = self.interner.resolve(return_type);
8375 let ret_type = if ret_type_str == "Self" {
8376 enum_type
8377 } else {
8378 self.resolve_type_for_comptime_with_subst(*return_type, type_subst)?
8379 };
8380
8381 let param_range = self
8382 .param_arena
8383 .alloc_method(param_names.into_iter(), param_types.into_iter());
8384
8385 self.enum_methods.insert(
8386 key,
8387 MethodInfo {
8388 struct_type: enum_type,
8389 has_self: *has_self,
8390 params: param_range,
8391 return_type: ret_type,
8392 body: *body,
8393 span: method_inst.span,
8394 is_unchecked: *is_unchecked,
8395 },
8396 );
8397 }
8398 }
8399 Some(())
8400 }
8401
8402 fn extract_anon_method_sigs(
8408 &self,
8409 methods_start: u32,
8410 methods_len: u32,
8411 ) -> Vec<super::AnonMethodSig> {
8412 let method_refs = self.rir.get_inst_refs(methods_start, methods_len);
8413 let mut sigs = Vec::with_capacity(method_refs.len());
8414
8415 for method_ref in method_refs {
8416 let method_inst = self.rir.get(method_ref);
8417 if let InstData::FnDecl {
8418 name,
8419 params_start,
8420 params_len,
8421 return_type,
8422 has_self,
8423 ..
8424 } = &method_inst.data
8425 {
8426 let params = self.rir.get_params(*params_start, *params_len);
8428 let param_types: Vec<Spur> = params.iter().map(|p| p.ty).collect();
8429
8430 sigs.push(super::AnonMethodSig {
8431 name: *name,
8432 has_self: *has_self,
8433 param_types,
8434 return_type: *return_type,
8435 });
8436 }
8437 }
8438
8439 sigs
8440 }
8441
8442 fn analyze_ptr_read_intrinsic(
8449 &mut self,
8450 air: &mut Air,
8451 name: Spur,
8452 args: &[RirCallArg],
8453 span: Span,
8454 ctx: &mut AnalysisContext,
8455 ) -> CompileResult<AnalysisResult> {
8456 if args.len() != 1 {
8457 return Err(CompileError::new(
8458 ErrorKind::IntrinsicWrongArgCount {
8459 name: "ptr_read".to_string(),
8460 expected: 1,
8461 found: args.len(),
8462 },
8463 span,
8464 ));
8465 }
8466
8467 let ptr_result = self.analyze_inst(air, args[0].value, ctx)?;
8468 let ptr_type = ptr_result.ty;
8469
8470 let pointee_type = match ptr_type.kind() {
8472 TypeKind::PtrConst(ptr_id) => self.type_pool.ptr_const_def(ptr_id),
8473 TypeKind::PtrMut(ptr_id) => self.type_pool.ptr_mut_def(ptr_id),
8474 _ => {
8475 return Err(CompileError::new(
8476 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8477 name: "ptr_read".to_string(),
8478 expected: "ptr const T or ptr mut T".to_string(),
8479 found: self.format_type_name(ptr_type),
8480 })),
8481 span,
8482 ));
8483 }
8484 };
8485
8486 let args_start = air.add_extra(&[ptr_result.air_ref.as_u32()]);
8488 let air_ref = air.add_inst(AirInst {
8489 data: AirInstData::Intrinsic {
8490 name,
8491 args_start,
8492 args_len: 1,
8493 },
8494 ty: pointee_type,
8495 span,
8496 });
8497 Ok(AnalysisResult::new(air_ref, pointee_type))
8498 }
8499
8500 fn analyze_ptr_write_intrinsic(
8503 &mut self,
8504 air: &mut Air,
8505 name: Spur,
8506 args: &[RirCallArg],
8507 span: Span,
8508 ctx: &mut AnalysisContext,
8509 ) -> CompileResult<AnalysisResult> {
8510 if args.len() != 2 {
8511 return Err(CompileError::new(
8512 ErrorKind::IntrinsicWrongArgCount {
8513 name: "ptr_write".to_string(),
8514 expected: 2,
8515 found: args.len(),
8516 },
8517 span,
8518 ));
8519 }
8520
8521 let ptr_result = self.analyze_inst(air, args[0].value, ctx)?;
8522 let value_result = self.analyze_inst(air, args[1].value, ctx)?;
8523 let ptr_type = ptr_result.ty;
8524 let value_type = value_result.ty;
8525
8526 let pointee_type = match ptr_type.kind() {
8528 TypeKind::PtrMut(ptr_id) => self.type_pool.ptr_mut_def(ptr_id),
8529 TypeKind::PtrConst(_) => {
8530 return Err(CompileError::new(
8531 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8532 name: "ptr_write".to_string(),
8533 expected: "ptr mut T (cannot write through ptr const)".to_string(),
8534 found: self.format_type_name(ptr_type),
8535 })),
8536 span,
8537 ));
8538 }
8539 _ => {
8540 return Err(CompileError::new(
8541 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8542 name: "ptr_write".to_string(),
8543 expected: "ptr mut T".to_string(),
8544 found: self.format_type_name(ptr_type),
8545 })),
8546 span,
8547 ));
8548 }
8549 };
8550
8551 if value_type != pointee_type && !value_type.is_error() && !value_type.is_never() {
8553 return Err(CompileError::new(
8554 ErrorKind::TypeMismatch {
8555 expected: self.format_type_name(pointee_type),
8556 found: self.format_type_name(value_type),
8557 },
8558 span,
8559 ));
8560 }
8561
8562 let args_start =
8564 air.add_extra(&[ptr_result.air_ref.as_u32(), value_result.air_ref.as_u32()]);
8565 let air_ref = air.add_inst(AirInst {
8566 data: AirInstData::Intrinsic {
8567 name,
8568 args_start,
8569 args_len: 2,
8570 },
8571 ty: Type::UNIT,
8572 span,
8573 });
8574 Ok(AnalysisResult::new(air_ref, Type::UNIT))
8575 }
8576
8577 fn analyze_ptr_offset_intrinsic(
8580 &mut self,
8581 air: &mut Air,
8582 name: Spur,
8583 args: &[RirCallArg],
8584 span: Span,
8585 ctx: &mut AnalysisContext,
8586 ) -> CompileResult<AnalysisResult> {
8587 if args.len() != 2 {
8588 return Err(CompileError::new(
8589 ErrorKind::IntrinsicWrongArgCount {
8590 name: "ptr_offset".to_string(),
8591 expected: 2,
8592 found: args.len(),
8593 },
8594 span,
8595 ));
8596 }
8597
8598 let ptr_result = self.analyze_inst(air, args[0].value, ctx)?;
8599 let offset_result = self.analyze_inst(air, args[1].value, ctx)?;
8600 let ptr_type = ptr_result.ty;
8601 let offset_type = offset_result.ty;
8602
8603 if !ptr_type.is_ptr() && !ptr_type.is_error() && !ptr_type.is_never() {
8605 return Err(CompileError::new(
8606 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8607 name: "ptr_offset".to_string(),
8608 expected: "ptr const T or ptr mut T".to_string(),
8609 found: self.format_type_name(ptr_type),
8610 })),
8611 span,
8612 ));
8613 }
8614
8615 if !offset_type.is_integer() && !offset_type.is_error() && !offset_type.is_never() {
8617 return Err(CompileError::new(
8618 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8619 name: "ptr_offset".to_string(),
8620 expected: "integer offset".to_string(),
8621 found: self.format_type_name(offset_type),
8622 })),
8623 span,
8624 ));
8625 }
8626
8627 let args_start =
8629 air.add_extra(&[ptr_result.air_ref.as_u32(), offset_result.air_ref.as_u32()]);
8630 let air_ref = air.add_inst(AirInst {
8631 data: AirInstData::Intrinsic {
8632 name,
8633 args_start,
8634 args_len: 2,
8635 },
8636 ty: ptr_type,
8637 span,
8638 });
8639 Ok(AnalysisResult::new(air_ref, ptr_type))
8640 }
8641
8642 fn analyze_ptr_to_int_intrinsic(
8645 &mut self,
8646 air: &mut Air,
8647 name: Spur,
8648 args: &[RirCallArg],
8649 span: Span,
8650 ctx: &mut AnalysisContext,
8651 ) -> CompileResult<AnalysisResult> {
8652 if args.len() != 1 {
8653 return Err(CompileError::new(
8654 ErrorKind::IntrinsicWrongArgCount {
8655 name: "ptr_to_int".to_string(),
8656 expected: 1,
8657 found: args.len(),
8658 },
8659 span,
8660 ));
8661 }
8662
8663 let ptr_result = self.analyze_inst(air, args[0].value, ctx)?;
8664 let ptr_type = ptr_result.ty;
8665
8666 if !ptr_type.is_ptr() && !ptr_type.is_error() && !ptr_type.is_never() {
8668 return Err(CompileError::new(
8669 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8670 name: "ptr_to_int".to_string(),
8671 expected: "ptr const T or ptr mut T".to_string(),
8672 found: self.format_type_name(ptr_type),
8673 })),
8674 span,
8675 ));
8676 }
8677
8678 let args_start = air.add_extra(&[ptr_result.air_ref.as_u32()]);
8680 let air_ref = air.add_inst(AirInst {
8681 data: AirInstData::Intrinsic {
8682 name,
8683 args_start,
8684 args_len: 1,
8685 },
8686 ty: Type::U64,
8687 span,
8688 });
8689 Ok(AnalysisResult::new(air_ref, Type::U64))
8690 }
8691
8692 fn analyze_int_to_ptr_intrinsic(
8696 &mut self,
8697 air: &mut Air,
8698 name: Spur,
8699 inst_ref: InstRef,
8700 args: &[RirCallArg],
8701 span: Span,
8702 ctx: &mut AnalysisContext,
8703 ) -> CompileResult<AnalysisResult> {
8704 if args.len() != 1 {
8705 return Err(CompileError::new(
8706 ErrorKind::IntrinsicWrongArgCount {
8707 name: "int_to_ptr".to_string(),
8708 expected: 1,
8709 found: args.len(),
8710 },
8711 span,
8712 ));
8713 }
8714
8715 let addr_result = self.analyze_inst(air, args[0].value, ctx)?;
8716 let addr_type = addr_result.ty;
8717
8718 if addr_type != Type::U64 && !addr_type.is_error() && !addr_type.is_never() {
8720 return Err(CompileError::new(
8721 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8722 name: "int_to_ptr".to_string(),
8723 expected: "u64".to_string(),
8724 found: self.format_type_name(addr_type),
8725 })),
8726 span,
8727 ));
8728 }
8729
8730 let result_type = Self::get_resolved_type(ctx, inst_ref, span, "@int_to_ptr intrinsic")?;
8732
8733 if !result_type.is_ptr_mut() && !result_type.is_error() && !result_type.is_never() {
8735 return Err(CompileError::new(
8736 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8737 name: "int_to_ptr".to_string(),
8738 expected: "ptr mut T".to_string(),
8739 found: self.format_type_name(result_type),
8740 })),
8741 span,
8742 ));
8743 }
8744
8745 let args_start = air.add_extra(&[addr_result.air_ref.as_u32()]);
8747 let air_ref = air.add_inst(AirInst {
8748 data: AirInstData::Intrinsic {
8749 name,
8750 args_start,
8751 args_len: 1,
8752 },
8753 ty: result_type,
8754 span,
8755 });
8756 Ok(AnalysisResult::new(air_ref, result_type))
8757 }
8758
8759 fn analyze_null_ptr_intrinsic(
8763 &mut self,
8764 air: &mut Air,
8765 name: Spur,
8766 inst_ref: InstRef,
8767 args: &[RirCallArg],
8768 span: Span,
8769 ctx: &mut AnalysisContext,
8770 ) -> CompileResult<AnalysisResult> {
8771 if !args.is_empty() {
8772 return Err(CompileError::new(
8773 ErrorKind::IntrinsicWrongArgCount {
8774 name: "null_ptr".to_string(),
8775 expected: 0,
8776 found: args.len(),
8777 },
8778 span,
8779 ));
8780 }
8781
8782 let result_type = Self::get_resolved_type(ctx, inst_ref, span, "@null_ptr intrinsic")?;
8784
8785 if !result_type.is_ptr() && !result_type.is_error() && !result_type.is_never() {
8787 return Err(CompileError::new(
8788 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8789 name: "null_ptr".to_string(),
8790 expected: "ptr const T or ptr mut T".to_string(),
8791 found: self.format_type_name(result_type),
8792 })),
8793 span,
8794 ));
8795 }
8796
8797 let args_start = air.add_extra(&[]);
8799 let air_ref = air.add_inst(AirInst {
8800 data: AirInstData::Intrinsic {
8801 name,
8802 args_start,
8803 args_len: 0,
8804 },
8805 ty: result_type,
8806 span,
8807 });
8808 Ok(AnalysisResult::new(air_ref, result_type))
8809 }
8810
8811 fn analyze_is_null_intrinsic(
8814 &mut self,
8815 air: &mut Air,
8816 name: Spur,
8817 args: &[RirCallArg],
8818 span: Span,
8819 ctx: &mut AnalysisContext,
8820 ) -> CompileResult<AnalysisResult> {
8821 if args.len() != 1 {
8822 return Err(CompileError::new(
8823 ErrorKind::IntrinsicWrongArgCount {
8824 name: "is_null".to_string(),
8825 expected: 1,
8826 found: args.len(),
8827 },
8828 span,
8829 ));
8830 }
8831
8832 let ptr_result = self.analyze_inst(air, args[0].value, ctx)?;
8833 let ptr_type = ptr_result.ty;
8834
8835 if !ptr_type.is_ptr() && !ptr_type.is_error() && !ptr_type.is_never() {
8837 return Err(CompileError::new(
8838 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8839 name: "is_null".to_string(),
8840 expected: "ptr const T or ptr mut T".to_string(),
8841 found: self.format_type_name(ptr_type),
8842 })),
8843 span,
8844 ));
8845 }
8846
8847 let args_start = air.add_extra(&[ptr_result.air_ref.as_u32()]);
8849 let air_ref = air.add_inst(AirInst {
8850 data: AirInstData::Intrinsic {
8851 name,
8852 args_start,
8853 args_len: 1,
8854 },
8855 ty: Type::BOOL,
8856 span,
8857 });
8858 Ok(AnalysisResult::new(air_ref, Type::BOOL))
8859 }
8860
8861 fn analyze_ptr_copy_intrinsic(
8864 &mut self,
8865 air: &mut Air,
8866 name: Spur,
8867 args: &[RirCallArg],
8868 span: Span,
8869 ctx: &mut AnalysisContext,
8870 ) -> CompileResult<AnalysisResult> {
8871 if args.len() != 3 {
8872 return Err(CompileError::new(
8873 ErrorKind::IntrinsicWrongArgCount {
8874 name: "ptr_copy".to_string(),
8875 expected: 3,
8876 found: args.len(),
8877 },
8878 span,
8879 ));
8880 }
8881
8882 let dst_result = self.analyze_inst(air, args[0].value, ctx)?;
8883 let src_result = self.analyze_inst(air, args[1].value, ctx)?;
8884 let count_result = self.analyze_inst(air, args[2].value, ctx)?;
8885 let dst_type = dst_result.ty;
8886 let src_type = src_result.ty;
8887 let count_type = count_result.ty;
8888
8889 let dst_pointee = match dst_type.kind() {
8891 TypeKind::PtrMut(ptr_id) => self.type_pool.ptr_mut_def(ptr_id),
8892 TypeKind::PtrConst(_) => {
8893 return Err(CompileError::new(
8894 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8895 name: "ptr_copy".to_string(),
8896 expected: "ptr mut T (cannot copy into ptr const)".to_string(),
8897 found: self.format_type_name(dst_type),
8898 })),
8899 span,
8900 ));
8901 }
8902 _ => {
8903 if !dst_type.is_error() && !dst_type.is_never() {
8904 return Err(CompileError::new(
8905 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8906 name: "ptr_copy".to_string(),
8907 expected: "ptr mut T".to_string(),
8908 found: self.format_type_name(dst_type),
8909 })),
8910 span,
8911 ));
8912 }
8913 Type::ERROR
8914 }
8915 };
8916
8917 let src_pointee = match src_type.kind() {
8919 TypeKind::PtrConst(ptr_id) => self.type_pool.ptr_const_def(ptr_id),
8920 TypeKind::PtrMut(ptr_id) => self.type_pool.ptr_mut_def(ptr_id),
8921 _ => {
8922 if !src_type.is_error() && !src_type.is_never() {
8923 return Err(CompileError::new(
8924 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8925 name: "ptr_copy".to_string(),
8926 expected: "ptr const T or ptr mut T".to_string(),
8927 found: self.format_type_name(src_type),
8928 })),
8929 span,
8930 ));
8931 }
8932 Type::ERROR
8933 }
8934 };
8935
8936 if dst_pointee != src_pointee
8938 && !dst_pointee.is_error()
8939 && !src_pointee.is_error()
8940 && !dst_pointee.is_never()
8941 && !src_pointee.is_never()
8942 {
8943 return Err(CompileError::new(
8944 ErrorKind::TypeMismatch {
8945 expected: self.format_type_name(dst_pointee),
8946 found: self.format_type_name(src_pointee),
8947 },
8948 span,
8949 ));
8950 }
8951
8952 if count_type != Type::U64 && !count_type.is_error() && !count_type.is_never() {
8954 return Err(CompileError::new(
8955 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
8956 name: "ptr_copy".to_string(),
8957 expected: "u64".to_string(),
8958 found: self.format_type_name(count_type),
8959 })),
8960 span,
8961 ));
8962 }
8963
8964 let args_start = air.add_extra(&[
8966 dst_result.air_ref.as_u32(),
8967 src_result.air_ref.as_u32(),
8968 count_result.air_ref.as_u32(),
8969 ]);
8970 let air_ref = air.add_inst(AirInst {
8971 data: AirInstData::Intrinsic {
8972 name,
8973 args_start,
8974 args_len: 3,
8975 },
8976 ty: Type::UNIT,
8977 span,
8978 });
8979 Ok(AnalysisResult::new(air_ref, Type::UNIT))
8980 }
8981
8982 fn analyze_addr_of_intrinsic(
8986 &mut self,
8987 air: &mut Air,
8988 args: &[RirCallArg],
8989 span: Span,
8990 ctx: &mut AnalysisContext,
8991 is_mut: bool,
8992 ) -> CompileResult<AnalysisResult> {
8993 let intrinsic_name = if is_mut { "addr_of_mut" } else { "addr_of" };
8994
8995 if args.len() != 1 {
8996 return Err(CompileError::new(
8997 ErrorKind::IntrinsicWrongArgCount {
8998 name: intrinsic_name.to_string(),
8999 expected: 1,
9000 found: args.len(),
9001 },
9002 span,
9003 ));
9004 }
9005
9006 let arg_result = self.analyze_inst(air, args[0].value, ctx)?;
9007 let pointee_type = arg_result.ty;
9008
9009 let result_type = if is_mut {
9014 let ptr_type_id = self.type_pool.intern_ptr_mut_from_type(pointee_type);
9015 Type::new_ptr_mut(ptr_type_id)
9016 } else {
9017 let ptr_type_id = self.type_pool.intern_ptr_const_from_type(pointee_type);
9018 Type::new_ptr_const(ptr_type_id)
9019 };
9020
9021 let name = if is_mut {
9023 self.known.raw_mut
9024 } else {
9025 self.known.raw
9026 };
9027 let args_start = air.add_extra(&[arg_result.air_ref.as_u32()]);
9028 let air_ref = air.add_inst(AirInst {
9029 data: AirInstData::Intrinsic {
9030 name,
9031 args_start,
9032 args_len: 1,
9033 },
9034 ty: result_type,
9035 span,
9036 });
9037 Ok(AnalysisResult::new(air_ref, result_type))
9038 }
9039
9040 fn analyze_syscall_intrinsic(
9047 &mut self,
9048 air: &mut Air,
9049 name: Spur,
9050 args: &[RirCallArg],
9051 span: Span,
9052 ctx: &mut AnalysisContext,
9053 ) -> CompileResult<AnalysisResult> {
9054 if args.is_empty() || args.len() > 7 {
9056 return Err(CompileError::new(
9057 ErrorKind::IntrinsicWrongArgCount {
9058 name: "syscall".to_string(),
9059 expected: 7, found: args.len(),
9061 },
9062 span,
9063 ));
9064 }
9065
9066 let mut arg_refs = Vec::with_capacity(args.len());
9068 for (i, arg) in args.iter().enumerate() {
9069 let arg_result = self.analyze_inst(air, arg.value, ctx)?;
9070 let arg_type = arg_result.ty;
9071
9072 if arg_type != Type::U64 && !arg_type.is_error() && !arg_type.is_never() {
9074 return Err(CompileError::new(
9075 ErrorKind::IntrinsicTypeMismatch(Box::new(IntrinsicTypeMismatchError {
9076 name: "syscall".to_string(),
9077 expected: format!("u64 for argument {}", i),
9078 found: self.format_type_name(arg_type),
9079 })),
9080 span,
9081 ));
9082 }
9083
9084 arg_refs.push(arg_result.air_ref.as_u32());
9085 }
9086
9087 let args_start = air.add_extra(&arg_refs);
9089 let air_ref = air.add_inst(AirInst {
9090 data: AirInstData::Intrinsic {
9091 name,
9092 args_start,
9093 args_len: args.len() as u32,
9094 },
9095 ty: Type::I64,
9096 span,
9097 });
9098 Ok(AnalysisResult::new(air_ref, Type::I64))
9099 }
9100
9101 fn analyze_target_arch_intrinsic(
9106 &self,
9107 air: &mut Air,
9108 args: &[RirCallArg],
9109 span: Span,
9110 ) -> CompileResult<AnalysisResult> {
9111 if !args.is_empty() {
9113 return Err(CompileError::new(
9114 ErrorKind::IntrinsicWrongArgCount {
9115 name: "target_arch".to_string(),
9116 expected: 0,
9117 found: args.len(),
9118 },
9119 span,
9120 ));
9121 }
9122
9123 let arch_enum_id = self
9124 .builtin_arch_id
9125 .expect("Arch enum not injected - internal compiler error");
9126
9127 let variant_index = match gruel_target::Target::host().arch() {
9130 Arch::X86_64 => 0,
9131 Arch::Aarch64 => 1,
9132 };
9133
9134 let result_type = Type::new_enum(arch_enum_id);
9135 let air_ref = air.add_inst(AirInst {
9136 data: AirInstData::EnumVariant {
9137 enum_id: arch_enum_id,
9138 variant_index,
9139 },
9140 ty: result_type,
9141 span,
9142 });
9143 Ok(AnalysisResult::new(air_ref, result_type))
9144 }
9145
9146 fn analyze_target_os_intrinsic(
9151 &self,
9152 air: &mut Air,
9153 args: &[RirCallArg],
9154 span: Span,
9155 ) -> CompileResult<AnalysisResult> {
9156 if !args.is_empty() {
9158 return Err(CompileError::new(
9159 ErrorKind::IntrinsicWrongArgCount {
9160 name: "target_os".to_string(),
9161 expected: 0,
9162 found: args.len(),
9163 },
9164 span,
9165 ));
9166 }
9167
9168 let os_enum_id = self
9169 .builtin_os_id
9170 .expect("Os enum not injected - internal compiler error");
9171
9172 let variant_index = match gruel_target::Target::host().os() {
9175 Os::Linux => 0,
9176 Os::Macos => 1,
9177 };
9178
9179 let result_type = Type::new_enum(os_enum_id);
9180 let air_ref = air.add_inst(AirInst {
9181 data: AirInstData::EnumVariant {
9182 enum_id: os_enum_id,
9183 variant_index,
9184 },
9185 ty: result_type,
9186 span,
9187 });
9188 Ok(AnalysisResult::new(air_ref, result_type))
9189 }
9190}