1use std::collections::{HashMap, HashSet, VecDeque};
23
24use bock_air::{AIRNode, NodeKind};
25use bock_ast::TypePath;
26use bock_errors::{DiagnosticBag, DiagnosticCode};
27
28use crate::{GenericType, NamedType, PrimitiveType, Type};
29
30const E_COHERENCE_OVERLAP: DiagnosticCode = DiagnosticCode {
33 prefix: 'E',
34 number: 4010,
35};
36
37const E_SEALED_PRIMITIVE_IMPL: DiagnosticCode = DiagnosticCode {
40 prefix: 'E',
41 number: 4011,
42};
43
44const E_DUPLICATE_METHOD: DiagnosticCode = DiagnosticCode {
51 prefix: 'E',
52 number: 4012,
53};
54
55pub type ImplId = u32;
59
60#[derive(Debug, Clone, PartialEq)]
64pub struct TraitRef {
65 pub name: String,
67 pub args: Vec<Type>,
70}
71
72impl TraitRef {
73 #[must_use]
76 pub fn new(name: impl Into<String>) -> Self {
77 Self {
78 name: name.into(),
79 args: vec![],
80 }
81 }
82
83 #[must_use]
87 pub fn parameterized(name: impl Into<String>, args: Vec<Type>) -> Self {
88 Self {
89 name: name.into(),
90 args,
91 }
92 }
93
94 fn from_path(path: &TypePath) -> Self {
95 let name = path
96 .segments
97 .iter()
98 .map(|s| s.name.as_str())
99 .collect::<Vec<_>>()
100 .join(".");
101 Self { name, args: vec![] }
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct ResolvedMethod {
108 pub impl_id: ImplId,
110 pub trait_ref: Option<TraitRef>,
112 pub method: String,
114}
115
116#[derive(Debug, Clone)]
120pub struct ImplEntry {
121 pub id: ImplId,
123 pub trait_ref: Option<TraitRef>,
125 pub type_key: String,
127 pub methods: Vec<String>,
129 pub is_generic: bool,
133 pub is_canonical: bool,
139 pub trait_args: Vec<Type>,
142 pub is_derived: bool,
148 pub target_type: Option<Type>,
153}
154
155pub struct ImplTable {
171 entries: HashMap<ImplId, ImplEntry>,
173 trait_impl_index: HashMap<(String, String), ImplId>,
177 param_trait_impl_index: HashMap<(String, String, String), ImplId>,
183 inherent_impl_index: HashMap<String, ImplId>,
185 supertraits: HashMap<String, Vec<String>>,
187 assoc_types: HashMap<(ImplId, String), Type>,
189 next_id: u32,
191 pub diags: DiagnosticBag,
193 method_defs: HashMap<String, Vec<MethodDef>>,
201}
202
203struct MethodDef {
206 name: String,
208 sig_key: String,
212 span: bock_errors::Span,
214 origin: String,
217}
218
219impl ImplTable {
220 #[must_use]
222 pub fn new() -> Self {
223 Self {
224 entries: HashMap::new(),
225 trait_impl_index: HashMap::new(),
226 param_trait_impl_index: HashMap::new(),
227 inherent_impl_index: HashMap::new(),
228 supertraits: HashMap::new(),
229 assoc_types: HashMap::new(),
230 next_id: 0,
231 diags: DiagnosticBag::new(),
232 method_defs: HashMap::new(),
233 }
234 }
235
236 #[must_use]
243 pub fn build_from(module: &AIRNode) -> Self {
244 let mut table = Self::new();
245 if let NodeKind::Module { items, .. } = &module.kind {
246 for item in items {
247 table.visit_item(item);
248 }
249 }
250 table.check_method_namespace();
256 table.synthesize_blanket_into();
261 table
262 }
263
264 fn check_method_namespace(&mut self) {
285 let mut type_keys: Vec<&String> = self.method_defs.keys().collect();
287 type_keys.sort();
288 let type_keys: Vec<String> = type_keys.into_iter().cloned().collect();
289
290 for type_key in type_keys {
291 let defs = &self.method_defs[&type_key];
292 let mut by_name: HashMap<&str, Vec<usize>> = HashMap::new();
296 for (idx, def) in defs.iter().enumerate() {
297 by_name.entry(def.name.as_str()).or_default().push(idx);
298 }
299 let mut names: Vec<&str> = by_name.keys().copied().collect();
300 names.sort();
301
302 for name in names {
303 let indices = &by_name[name];
304 if indices.len() < 2 {
305 continue;
306 }
307 let first = &defs[indices[0]];
308 for &dup_idx in &indices[1..] {
309 let dup = &defs[dup_idx];
310 let same_sig = dup.sig_key == first.sig_key;
311 let detail = if same_sig {
312 format!(
313 "a method named `{name}` is already defined for type `{type_key}` \
314 in the {}; a type has one method namespace, so the same method \
315 may not be defined twice",
316 first.origin,
317 )
318 } else {
319 format!(
320 "method `{name}` is defined for type `{type_key}` with conflicting \
321 signatures (in the {} and the {}); a type has one method slot per \
322 name and cannot satisfy two requirements with incompatible signatures",
323 first.origin, dup.origin,
324 )
325 };
326 self.diags
327 .error(E_DUPLICATE_METHOD, detail, dup.span)
328 .note(format!(
329 "a trait requirement is satisfied by a matching method anywhere in \
330 the type's namespace; if `{name}` should satisfy a trait, define it \
331 once (as an inherent/class-body method or inside the trait impl) and \
332 leave the other block empty",
333 ));
334 }
335 }
336 }
337 }
338
339 fn record_method_def(&mut self, type_key: &str, method: &AIRNode, origin: &str) {
343 if let NodeKind::FnDecl { name, .. } = &method.kind {
344 self.method_defs
345 .entry(type_key.to_owned())
346 .or_default()
347 .push(MethodDef {
348 name: name.name.clone(),
349 sig_key: method_sig_key(method),
350 span: method.span,
351 origin: origin.to_owned(),
352 });
353 }
354 }
355
356 fn synthesize_blanket_into(&mut self) {
365 let froms: Vec<(Type, Type)> = self
367 .entries
368 .values()
369 .filter_map(|e| {
370 let tr = e.trait_ref.as_ref()?;
371 if tr.name != "From" || tr.args.len() != 1 || e.is_generic {
372 return None;
373 }
374 let source = tr.args[0].clone();
376 let target = e.target_type.clone()?;
377 Some((source, target))
378 })
379 .collect();
380
381 for (source, target) in froms {
382 let into_arg_key = trait_arg_key(std::slice::from_ref(&target));
384 let into_target_key = type_key(&source);
385 let occupied = self.param_trait_impl_index.contains_key(&(
386 "Into".to_owned(),
387 into_arg_key,
388 into_target_key,
389 ));
390 if occupied {
391 continue;
393 }
394 self.register_param_trait_impl("Into", std::slice::from_ref(&target), &source, true);
395 }
396 }
397
398 fn visit_item(&mut self, node: &AIRNode) {
399 match &node.kind {
400 NodeKind::ImplBlock {
401 trait_path,
402 trait_args,
403 target,
404 methods,
405 generic_params,
406 ..
407 } => {
408 let resolved_trait_args: Vec<Type> =
412 trait_args.iter().map(type_from_node).collect();
413 let trait_ref = trait_path.as_ref().map(|p| {
414 let mut tr = TraitRef::from_path(p);
415 tr.args = resolved_trait_args.clone();
416 tr
417 });
418 let type_key = type_key_from_node(target);
419 let is_generic = !generic_params.is_empty();
420
421 if let Some(tr) = &trait_ref {
430 if SEALED_CORE_TRAITS.contains(&tr.name.as_str())
431 && SEALED_PRIMITIVE_KEYS.contains(&type_key.as_str())
432 {
433 self.diags
434 .error(
435 E_SEALED_PRIMITIVE_IMPL,
436 format!(
437 "cannot implement core trait `{}` for primitive type `{}`: its conformance is provided by the compiler and is sealed",
438 tr.name, type_key,
439 ),
440 node.span,
441 )
442 .note(format!(
443 "wrap `{type_key}` in a newtype (e.g. `record My{type_key} {{ value: {type_key} }}`) and implement `{}` for that instead",
444 tr.name,
445 ));
446 return;
447 }
448 }
449
450 if !is_generic {
457 if let Some(tr) = &trait_ref {
458 if tr.args.is_empty() {
459 let index_key = (tr.name.clone(), type_key.clone());
460 if self.trait_impl_index.contains_key(&index_key) {
461 self.diags.error(
462 E_COHERENCE_OVERLAP,
463 format!(
464 "conflicting implementations of trait `{}` for type `{}`",
465 tr.name, type_key,
466 ),
467 node.span,
468 );
469 return;
470 }
471 } else {
472 let arg_key = trait_arg_key(&tr.args);
473 let index_key = (tr.name.clone(), arg_key.clone(), type_key.clone());
474 if self.param_trait_impl_index.contains_key(&index_key) {
475 self.diags.error(
476 E_COHERENCE_OVERLAP,
477 format!(
478 "conflicting implementations of trait `{}[{}]` for type `{}`",
479 tr.name, arg_key, type_key,
480 ),
481 node.span,
482 );
483 return;
484 }
485 }
486 }
487 }
488
489 let id = self.alloc_id();
490
491 let origin = match &trait_ref {
494 Some(tr) => format!("`impl {} for {}` block", tr.name, type_key),
495 None => format!("inherent `impl {type_key}` block"),
496 };
497
498 let trait_is_parameterized =
508 trait_ref.as_ref().is_some_and(|tr| !tr.args.is_empty());
509 let track_namespace = !is_generic && !trait_is_parameterized;
510
511 let mut method_names = Vec::new();
513 for m in methods {
514 match &m.kind {
515 NodeKind::FnDecl { name, .. } => {
516 method_names.push(name.name.clone());
517 if track_namespace {
518 self.record_method_def(&type_key, m, &origin);
519 }
520 }
521 NodeKind::TypeAlias { name, ty, .. } => {
522 let resolved = type_from_node(ty);
524 self.assoc_types.insert((id, name.name.clone()), resolved);
525 }
526 _ => {}
527 }
528 }
529
530 if let Some(tr) = &trait_ref {
532 if !is_generic {
533 if tr.args.is_empty() {
534 self.trait_impl_index
535 .insert((tr.name.clone(), type_key.clone()), id);
536 } else {
537 self.param_trait_impl_index.insert(
538 (tr.name.clone(), trait_arg_key(&tr.args), type_key.clone()),
539 id,
540 );
541 }
542 }
543 } else {
544 self.inherent_impl_index.insert(type_key.clone(), id);
546 }
547
548 let target_type = Some(type_from_node(target));
549 self.entries.insert(
550 id,
551 ImplEntry {
552 id,
553 trait_ref,
554 type_key,
555 methods: method_names,
556 is_generic,
557 is_canonical: false,
558 trait_args: resolved_trait_args,
559 is_derived: false,
560 target_type,
561 },
562 );
563 }
564
565 NodeKind::TraitDecl {
566 name,
567 generic_params,
568 ..
569 } => {
570 for param in generic_params {
573 if param.name.name == "Self" {
574 for bound in ¶m.bounds {
575 let supertrait = trait_name_from_path(bound);
576 self.register_supertrait(name.name.clone(), supertrait);
577 }
578 }
579 }
580 }
581
582 NodeKind::ClassDecl { name, methods, .. } => {
583 let class_key = name.name.clone();
590 let origin = format!("`class {class_key}` body");
591 for m in methods {
592 if matches!(m.kind, NodeKind::FnDecl { .. }) {
593 self.record_method_def(&class_key, m, &origin);
594 }
595 }
596 }
597
598 _ => {}
599 }
600 }
601
602 fn alloc_id(&mut self) -> ImplId {
603 let id = self.next_id;
604 self.next_id += 1;
605 id
606 }
607
608 pub fn register_supertrait(
611 &mut self,
612 sub_trait: impl Into<String>,
613 super_trait: impl Into<String>,
614 ) {
615 self.supertraits
616 .entry(sub_trait.into())
617 .or_default()
618 .push(super_trait.into());
619 }
620
621 pub fn register_trait_impl(&mut self, trait_name: impl Into<String>, ty: &Type) -> ImplId {
629 self.register_trait_impl_inner(trait_name, &[], ty, false, false)
630 }
631
632 pub fn register_param_trait_impl(
637 &mut self,
638 trait_name: impl Into<String>,
639 trait_args: &[Type],
640 ty: &Type,
641 is_derived: bool,
642 ) -> ImplId {
643 self.register_trait_impl_inner(trait_name, trait_args, ty, false, is_derived)
644 }
645
646 fn register_trait_impl_inner(
657 &mut self,
658 trait_name: impl Into<String>,
659 trait_args: &[Type],
660 ty: &Type,
661 is_canonical: bool,
662 is_derived: bool,
663 ) -> ImplId {
664 let id = self.alloc_id();
665 let trait_name = trait_name.into();
666 let key = type_key(ty);
667 let args_vec = trait_args.to_vec();
668 let trait_ref = if args_vec.is_empty() {
669 TraitRef::new(&trait_name)
670 } else {
671 TraitRef::parameterized(&trait_name, args_vec.clone())
672 };
673 self.entries.insert(
674 id,
675 ImplEntry {
676 id,
677 trait_ref: Some(trait_ref),
678 type_key: key.clone(),
679 methods: vec![],
680 is_generic: false,
681 is_canonical,
682 trait_args: args_vec.clone(),
683 is_derived,
684 target_type: Some(ty.clone()),
685 },
686 );
687 if args_vec.is_empty() {
688 self.trait_impl_index.insert((trait_name, key), id);
689 } else {
690 self.param_trait_impl_index
691 .insert((trait_name, trait_arg_key(&args_vec), key), id);
692 }
693 id
694 }
695
696 pub fn fold_imported_impls(&mut self, impls: &[(String, Vec<Type>, Type)]) {
710 for (trait_name, trait_args, target) in impls {
711 let key = type_key(target);
712 if trait_args.is_empty() {
713 if self
714 .trait_impl_index
715 .contains_key(&(trait_name.clone(), key.clone()))
716 {
717 continue; }
719 self.register_trait_impl(trait_name.clone(), target);
720 } else {
721 let arg_key = trait_arg_key(trait_args);
722 if self
723 .param_trait_impl_index
724 .contains_key(&(trait_name.clone(), arg_key, key))
725 {
726 continue;
727 }
728 self.register_param_trait_impl(trait_name.clone(), trait_args, target, false);
729 }
730 }
731 self.synthesize_blanket_into();
733 }
734
735 pub fn register_assoc_type(&mut self, impl_id: ImplId, name: impl Into<String>, ty: Type) {
739 self.assoc_types.insert((impl_id, name.into()), ty);
740 }
741
742 #[must_use]
746 pub fn resolve_assoc_type(&self, impl_id: ImplId, name: &str) -> Option<&Type> {
747 self.assoc_types.get(&(impl_id, name.to_owned()))
748 }
749
750 #[must_use]
756 pub fn all_supertraits(&self, trait_name: &str) -> Vec<String> {
757 let mut result = Vec::new();
758 let mut visited: HashSet<String> = HashSet::new();
759 let mut queue: VecDeque<String> = VecDeque::new();
760
761 if let Some(direct) = self.supertraits.get(trait_name) {
762 for st in direct {
763 if visited.insert(st.clone()) {
764 queue.push_back(st.clone());
765 }
766 }
767 }
768
769 while let Some(name) = queue.pop_front() {
770 result.push(name.clone());
771 if let Some(supers) = self.supertraits.get(&name) {
772 for st in supers {
773 if visited.insert(st.clone()) {
774 queue.push_back(st.clone());
775 }
776 }
777 }
778 }
779
780 result
781 }
782
783 #[must_use]
785 pub fn get_entry(&self, id: ImplId) -> Option<&ImplEntry> {
786 self.entries.get(&id)
787 }
788
789 pub fn entries(&self) -> impl Iterator<Item = &ImplEntry> {
791 self.entries.values()
792 }
793
794 #[must_use]
821 pub fn exportable_trait_impls(&self) -> Vec<(String, Vec<Type>, Type)> {
822 let mut out: Vec<(String, Vec<Type>, Type)> = Vec::new();
823 for entry in self.entries.values() {
824 if entry.is_canonical || entry.is_derived || entry.is_generic {
825 continue;
826 }
827 let Some(tr) = &entry.trait_ref else {
828 continue; };
830 let Some(target) = &entry.target_type else {
831 continue; };
833 if matches!(target, Type::Primitive(_)) {
834 continue; }
836 out.push((tr.name.clone(), entry.trait_args.clone(), target.clone()));
837 }
838 out
839 }
840
841 fn find_trait_impl(&self, trait_name: &str, type_key: &str) -> Option<ImplId> {
844 self.trait_impl_index
845 .get(&(trait_name.to_owned(), type_key.to_owned()))
846 .copied()
847 }
848
849 fn find_param_trait_impl(
850 &self,
851 trait_name: &str,
852 trait_arg_key: &str,
853 type_key: &str,
854 ) -> Option<ImplId> {
855 self.param_trait_impl_index
856 .get(&(
857 trait_name.to_owned(),
858 trait_arg_key.to_owned(),
859 type_key.to_owned(),
860 ))
861 .copied()
862 }
863
864 #[must_use]
874 pub fn has_any_param_trait_impl(&self, trait_name: &str, type_key: &str) -> bool {
875 self.param_trait_impl_index
876 .keys()
877 .any(|(t, _arg, ty)| t == trait_name && ty == type_key)
878 }
879
880 fn find_inherent_impl(&self, type_key: &str) -> Option<ImplId> {
881 self.inherent_impl_index.get(type_key).copied()
882 }
883}
884
885impl Default for ImplTable {
886 fn default() -> Self {
887 Self::new()
888 }
889}
890
891#[must_use]
908pub fn resolve_impl(trait_ref: &TraitRef, ty: &Type, impls: &ImplTable) -> Option<ImplId> {
909 let key = type_key(ty);
910 if trait_ref.args.is_empty() {
911 impls.find_trait_impl(&trait_ref.name, &key)
912 } else {
913 impls.find_param_trait_impl(&trait_ref.name, &trait_arg_key(&trait_ref.args), &key)
914 }
915}
916
917#[must_use]
923pub fn check_supertrait_obligations(trait_ref: &TraitRef, ty: &Type, impls: &ImplTable) -> bool {
924 let key = type_key(ty);
925 for supertrait in impls.all_supertraits(&trait_ref.name) {
926 if impls.find_trait_impl(&supertrait, &key).is_none() {
927 return false;
928 }
929 }
930 true
931}
932
933#[must_use]
941pub fn resolve_method(receiver: &Type, method: &str, impls: &ImplTable) -> Option<ResolvedMethod> {
942 let key = type_key(receiver);
943
944 if let Some(impl_id) = impls.find_inherent_impl(&key) {
946 if let Some(entry) = impls.get_entry(impl_id) {
947 if entry.methods.iter().any(|m| m == method) {
948 return Some(ResolvedMethod {
949 impl_id,
950 trait_ref: None,
951 method: method.to_owned(),
952 });
953 }
954 }
955 }
956
957 for entry in impls.entries() {
959 if entry.type_key == key
960 && entry.trait_ref.is_some()
961 && entry.methods.iter().any(|m| m == method)
962 {
963 return Some(ResolvedMethod {
964 impl_id: entry.id,
965 trait_ref: entry.trait_ref.clone(),
966 method: method.to_owned(),
967 });
968 }
969 }
970
971 None
972}
973
974#[must_use]
982pub fn type_key(ty: &Type) -> String {
983 match ty {
984 Type::Primitive(p) => format!("{p:?}"),
985 Type::Named(n) => n.name.clone(),
986 Type::Generic(g) => {
987 let args = g.args.iter().map(type_key).collect::<Vec<_>>().join(", ");
988 format!("{}[{}]", g.constructor, args)
989 }
990 Type::Tuple(elems) => {
991 let elems = elems.iter().map(type_key).collect::<Vec<_>>().join(", ");
992 format!("({})", elems)
993 }
994 Type::Function(f) => {
995 let params = f.params.iter().map(type_key).collect::<Vec<_>>().join(", ");
996 format!("Fn({}) -> {}", params, type_key(&f.ret))
997 }
998 Type::Optional(inner) => format!("{}?", type_key(inner)),
999 Type::Result(ok, err) => format!("Result[{}, {}]", type_key(ok), type_key(err)),
1000 Type::TypeVar(id) => format!("?{id}"),
1001 Type::Refined(base, _) => type_key(base),
1002 Type::Flexible(_) => "Flexible".to_string(),
1003 Type::Error => "Error".to_string(),
1004 }
1005}
1006
1007#[must_use]
1014pub fn trait_arg_key(args: &[Type]) -> String {
1015 args.iter().map(type_key).collect::<Vec<_>>().join(", ")
1016}
1017
1018fn method_sig_key(method: &AIRNode) -> String {
1029 let NodeKind::FnDecl {
1030 params,
1031 return_type,
1032 ..
1033 } = &method.kind
1034 else {
1035 return String::new();
1036 };
1037 let param_keys: Vec<String> = params
1038 .iter()
1039 .map(|p| match &p.kind {
1040 NodeKind::Param {
1041 pattern, ty: None, ..
1042 } => {
1043 if let NodeKind::BindPat { name, .. } = &pattern.kind {
1047 format!("@{}", name.name)
1048 } else {
1049 "@_".to_owned()
1050 }
1051 }
1052 NodeKind::Param {
1053 ty: Some(ty_node), ..
1054 } => type_key_from_node(ty_node),
1055 _ => "?".to_owned(),
1056 })
1057 .collect();
1058 let ret_key = return_type
1059 .as_deref()
1060 .map_or_else(|| "Void".to_owned(), type_key_from_node);
1061 format!("({}) -> {}", param_keys.join(", "), ret_key)
1062}
1063
1064fn trait_name_from_path(path: &TypePath) -> String {
1066 path.segments
1067 .iter()
1068 .map(|s| s.name.as_str())
1069 .collect::<Vec<_>>()
1070 .join(".")
1071}
1072
1073fn type_key_from_node(node: &AIRNode) -> String {
1077 match &node.kind {
1078 NodeKind::TypeNamed { path, args } => {
1079 let name = path
1080 .segments
1081 .iter()
1082 .map(|s| s.name.as_str())
1083 .collect::<Vec<_>>()
1084 .join(".");
1085 if args.is_empty() {
1086 name
1087 } else {
1088 let arg_keys: Vec<_> = args.iter().map(type_key_from_node).collect();
1089 format!("{}[{}]", name, arg_keys.join(", "))
1090 }
1091 }
1092 NodeKind::TypeTuple { elems } => {
1093 let elem_keys: Vec<_> = elems.iter().map(type_key_from_node).collect();
1094 format!("({})", elem_keys.join(", "))
1095 }
1096 NodeKind::TypeOptional { inner } => format!("{}?", type_key_from_node(inner)),
1097 NodeKind::TypeFunction { params, ret, .. } => {
1098 let param_keys: Vec<_> = params.iter().map(type_key_from_node).collect();
1099 format!(
1100 "Fn({}) -> {}",
1101 param_keys.join(", "),
1102 type_key_from_node(ret)
1103 )
1104 }
1105 NodeKind::TypeSelf => "Self".to_string(),
1106 _ => "Unknown".to_string(),
1107 }
1108}
1109
1110fn type_from_node(node: &AIRNode) -> Type {
1115 match &node.kind {
1116 NodeKind::TypeNamed { path, args } => {
1117 let name = path
1118 .segments
1119 .iter()
1120 .map(|s| s.name.as_str())
1121 .collect::<Vec<_>>()
1122 .join(".");
1123 if args.is_empty() {
1124 match name.as_str() {
1125 "Int" => Type::Primitive(PrimitiveType::Int),
1126 "Float" => Type::Primitive(PrimitiveType::Float),
1127 "Bool" => Type::Primitive(PrimitiveType::Bool),
1128 "String" => Type::Primitive(PrimitiveType::String),
1129 "Char" => Type::Primitive(PrimitiveType::Char),
1130 "Void" => Type::Primitive(PrimitiveType::Void),
1131 "Never" => Type::Primitive(PrimitiveType::Never),
1132 _ => Type::Named(NamedType { name }),
1133 }
1134 } else {
1135 let type_args: Vec<_> = args.iter().map(type_from_node).collect();
1136 Type::Generic(GenericType {
1137 constructor: name,
1138 args: type_args,
1139 })
1140 }
1141 }
1142 NodeKind::TypeOptional { inner } => Type::Optional(Box::new(type_from_node(inner))),
1143 NodeKind::TypeTuple { elems } => Type::Tuple(elems.iter().map(type_from_node).collect()),
1144 NodeKind::TypeSelf => Type::Named(NamedType {
1145 name: "Self".to_owned(),
1146 }),
1147 _ => Type::Error,
1148 }
1149}
1150
1151pub const SEALED_CORE_TRAITS: &[&str] = &["Equatable", "Comparable", "Displayable", "Hashable"];
1160
1161pub const SEALED_PRIMITIVE_KEYS: &[&str] = &[
1165 "Int", "Float", "String", "Bool", "Char", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8",
1166 "UInt16", "UInt32", "UInt64", "Float32", "Float64",
1167];
1168
1169const SIZED_INTS: &[PrimitiveType] = &[
1171 PrimitiveType::Int8,
1172 PrimitiveType::Int16,
1173 PrimitiveType::Int32,
1174 PrimitiveType::Int64,
1175 PrimitiveType::Int128,
1176 PrimitiveType::UInt8,
1177 PrimitiveType::UInt16,
1178 PrimitiveType::UInt32,
1179 PrimitiveType::UInt64,
1180];
1181
1182const SIZED_FLOATS: &[PrimitiveType] = &[PrimitiveType::Float32, PrimitiveType::Float64];
1184
1185pub fn register_canonical_conformances(table: &mut ImplTable) {
1207 table.register_supertrait("Comparable", "Equatable");
1209
1210 let register = |table: &mut ImplTable, trait_name: &str, prims: &[PrimitiveType]| {
1212 for p in prims {
1213 let ty = Type::Primitive(p.clone());
1214 table.register_trait_impl_inner(trait_name, &[], &ty, true, false);
1215 }
1216 };
1217
1218 const EQUATABLE_BASE: &[PrimitiveType] = &[
1220 PrimitiveType::Int,
1221 PrimitiveType::Float,
1222 PrimitiveType::String,
1223 PrimitiveType::Bool,
1224 PrimitiveType::Char,
1225 ];
1226 const COMPARABLE_BASE: &[PrimitiveType] = &[
1227 PrimitiveType::Int,
1228 PrimitiveType::Float,
1229 PrimitiveType::String,
1230 PrimitiveType::Char,
1231 ];
1232 const DISPLAYABLE_BASE: &[PrimitiveType] = &[
1233 PrimitiveType::Int,
1234 PrimitiveType::Float,
1235 PrimitiveType::String,
1236 PrimitiveType::Bool,
1237 PrimitiveType::Char,
1238 ];
1239 const HASHABLE_BASE: &[PrimitiveType] = &[
1240 PrimitiveType::Int,
1241 PrimitiveType::String,
1242 PrimitiveType::Bool,
1243 PrimitiveType::Char,
1244 ];
1245
1246 register(table, "Equatable", EQUATABLE_BASE);
1248 register(table, "Equatable", SIZED_INTS);
1249 register(table, "Equatable", SIZED_FLOATS);
1250
1251 register(table, "Comparable", COMPARABLE_BASE);
1253 register(table, "Comparable", SIZED_INTS);
1254 register(table, "Comparable", SIZED_FLOATS);
1255
1256 register(table, "Displayable", DISPLAYABLE_BASE);
1258 register(table, "Displayable", SIZED_INTS);
1259 register(table, "Displayable", SIZED_FLOATS);
1260
1261 register(table, "Hashable", HASHABLE_BASE);
1263 register(table, "Hashable", SIZED_INTS);
1264}
1265
1266pub fn register_canonical_conversions(table: &mut ImplTable) {
1290 let from = |table: &mut ImplTable, source: PrimitiveType, target: PrimitiveType| {
1294 let source_ty = Type::Primitive(source);
1295 let target_ty = Type::Primitive(target);
1296 table.register_param_trait_impl(
1297 "From",
1298 std::slice::from_ref(&source_ty),
1299 &target_ty,
1300 false,
1301 );
1302 table.register_param_trait_impl("Into", std::slice::from_ref(&target_ty), &source_ty, true);
1304 };
1305
1306 from(table, PrimitiveType::Int, PrimitiveType::Float);
1308
1309 from(table, PrimitiveType::Float32, PrimitiveType::Float);
1311
1312 from(table, PrimitiveType::Char, PrimitiveType::String);
1314
1315 const SIGNED_WIDENING: &[PrimitiveType] = &[
1318 PrimitiveType::Int8,
1319 PrimitiveType::Int16,
1320 PrimitiveType::Int32,
1321 PrimitiveType::Int64,
1322 PrimitiveType::Int128,
1323 ];
1324 for (i, narrow) in SIGNED_WIDENING.iter().enumerate() {
1325 for wide in &SIGNED_WIDENING[i + 1..] {
1326 from(table, narrow.clone(), wide.clone());
1327 }
1328 from(table, narrow.clone(), PrimitiveType::Int);
1330 }
1331
1332 let string_ty = Type::Primitive(PrimitiveType::String);
1335 table.register_param_trait_impl(
1336 "TryFrom",
1337 std::slice::from_ref(&string_ty),
1338 &Type::Primitive(PrimitiveType::Int),
1339 false,
1340 );
1341 table.register_param_trait_impl(
1342 "TryFrom",
1343 std::slice::from_ref(&string_ty),
1344 &Type::Primitive(PrimitiveType::Float),
1345 false,
1346 );
1347}
1348
1349#[cfg(test)]
1352mod tests {
1353 use super::*;
1354 use crate::{NamedType, PrimitiveType, Type};
1355
1356 fn named(name: &str) -> Type {
1359 Type::Named(NamedType {
1360 name: name.to_owned(),
1361 })
1362 }
1363
1364 fn int() -> Type {
1365 Type::Primitive(PrimitiveType::Int)
1366 }
1367
1368 fn bool_ty() -> Type {
1369 Type::Primitive(PrimitiveType::Bool)
1370 }
1371
1372 fn dummy_span() -> bock_errors::Span {
1373 use bock_errors::{FileId, Span};
1374 Span {
1375 file: FileId(0),
1376 start: 0,
1377 end: 0,
1378 }
1379 }
1380
1381 fn make_air_node(kind: NodeKind) -> AIRNode {
1382 AIRNode::new(0, dummy_span(), kind)
1383 }
1384
1385 fn make_module(items: Vec<AIRNode>) -> AIRNode {
1386 make_air_node(NodeKind::Module {
1387 path: None,
1388 annotations: vec![],
1389 imports: vec![],
1390 items,
1391 })
1392 }
1393
1394 fn make_type_named(name: &str) -> AIRNode {
1395 use bock_ast::{Ident, TypePath};
1396 let ident = Ident {
1397 name: name.to_owned(),
1398 span: dummy_span(),
1399 };
1400 make_air_node(NodeKind::TypeNamed {
1401 path: TypePath {
1402 segments: vec![ident],
1403 span: dummy_span(),
1404 },
1405 args: vec![],
1406 })
1407 }
1408
1409 fn make_fn_decl(name: &str) -> AIRNode {
1410 make_fn_decl_ret(name, None)
1411 }
1412
1413 fn make_fn_decl_ret(name: &str, ret: Option<&str>) -> AIRNode {
1417 use bock_ast::{Ident, Visibility};
1418 let body = make_air_node(NodeKind::Block {
1419 stmts: vec![],
1420 tail: None,
1421 });
1422 make_air_node(NodeKind::FnDecl {
1423 annotations: vec![],
1424 visibility: Visibility::Private,
1425 is_async: false,
1426 name: Ident {
1427 name: name.to_owned(),
1428 span: dummy_span(),
1429 },
1430 generic_params: vec![],
1431 params: vec![],
1432 return_type: ret.map(|r| Box::new(make_type_named(r))),
1433 effect_clause: vec![],
1434 where_clause: vec![],
1435 body: Box::new(body),
1436 })
1437 }
1438
1439 fn make_class_decl(name: &str, methods: Vec<AIRNode>) -> AIRNode {
1442 use bock_ast::{Ident, Visibility};
1443 make_air_node(NodeKind::ClassDecl {
1444 annotations: vec![],
1445 visibility: Visibility::Private,
1446 name: Ident {
1447 name: name.to_owned(),
1448 span: dummy_span(),
1449 },
1450 generic_params: vec![],
1451 base: None,
1452 traits: vec![],
1453 fields: vec![],
1454 methods,
1455 })
1456 }
1457
1458 fn make_impl_block(
1459 trait_name: Option<&str>,
1460 target_name: &str,
1461 methods: Vec<AIRNode>,
1462 ) -> AIRNode {
1463 use bock_ast::{Ident, TypePath};
1464 let trait_path = trait_name.map(|n| TypePath {
1465 segments: vec![Ident {
1466 name: n.to_owned(),
1467 span: dummy_span(),
1468 }],
1469 span: dummy_span(),
1470 });
1471 make_air_node(NodeKind::ImplBlock {
1472 annotations: vec![],
1473 generic_params: vec![],
1474 trait_path,
1475 trait_args: vec![],
1476 target: Box::new(make_type_named(target_name)),
1477 where_clause: vec![],
1478 methods,
1479 })
1480 }
1481
1482 fn make_param_impl_block(
1484 trait_name: &str,
1485 trait_arg_names: &[&str],
1486 target_name: &str,
1487 methods: Vec<AIRNode>,
1488 ) -> AIRNode {
1489 use bock_ast::{Ident, TypePath};
1490 let trait_path = Some(TypePath {
1491 segments: vec![Ident {
1492 name: trait_name.to_owned(),
1493 span: dummy_span(),
1494 }],
1495 span: dummy_span(),
1496 });
1497 let trait_args = trait_arg_names
1498 .iter()
1499 .map(|n| make_type_named(n))
1500 .collect::<Vec<_>>();
1501 make_air_node(NodeKind::ImplBlock {
1502 annotations: vec![],
1503 generic_params: vec![],
1504 trait_path,
1505 trait_args,
1506 target: Box::new(make_type_named(target_name)),
1507 where_clause: vec![],
1508 methods,
1509 })
1510 }
1511
1512 #[test]
1515 fn type_key_primitive() {
1516 assert_eq!(type_key(&int()), "Int");
1517 assert_eq!(type_key(&bool_ty()), "Bool");
1518 }
1519
1520 #[test]
1521 fn type_key_named() {
1522 assert_eq!(type_key(&named("User")), "User");
1523 }
1524
1525 #[test]
1526 fn type_key_generic() {
1527 use crate::GenericType;
1528 let ty = Type::Generic(GenericType {
1529 constructor: "List".to_owned(),
1530 args: vec![int()],
1531 });
1532 assert_eq!(type_key(&ty), "List[Int]");
1533 }
1534
1535 #[test]
1536 fn type_key_optional() {
1537 assert_eq!(type_key(&Type::Optional(Box::new(int()))), "Int?");
1538 }
1539
1540 #[test]
1541 fn type_key_result() {
1542 assert_eq!(
1543 type_key(&Type::Result(Box::new(int()), Box::new(named("Err")))),
1544 "Result[Int, Err]"
1545 );
1546 }
1547
1548 #[test]
1551 fn build_empty_module() {
1552 let module = make_module(vec![]);
1553 let table = ImplTable::build_from(&module);
1554 assert!(!table.diags.has_errors());
1555 assert_eq!(table.entries.len(), 0);
1556 }
1557
1558 #[test]
1559 fn build_registers_trait_impl() {
1560 let eq_method = make_fn_decl("equals");
1561 let impl_block = make_impl_block(Some("Equatable"), "User", vec![eq_method]);
1562 let module = make_module(vec![impl_block]);
1563 let table = ImplTable::build_from(&module);
1564 assert!(!table.diags.has_errors());
1565 let id = resolve_impl(&TraitRef::new("Equatable"), &named("User"), &table);
1566 assert!(id.is_some());
1567 }
1568
1569 #[test]
1570 fn build_registers_inherent_impl() {
1571 let method = make_fn_decl("greet");
1572 let impl_block = make_impl_block(None, "User", vec![method]);
1573 let module = make_module(vec![impl_block]);
1574 let table = ImplTable::build_from(&module);
1575 let result = resolve_method(&named("User"), "greet", &table);
1576 assert!(result.is_some());
1577 let r = result.unwrap();
1578 assert!(r.trait_ref.is_none());
1579 assert_eq!(r.method, "greet");
1580 }
1581
1582 #[test]
1585 fn resolve_impl_found() {
1586 let mut table = ImplTable::new();
1587 let id = table.alloc_id();
1588 table.entries.insert(
1589 id,
1590 ImplEntry {
1591 id,
1592 trait_ref: Some(TraitRef::new("Printable")),
1593 type_key: "Int".to_owned(),
1594 methods: vec!["print".to_owned()],
1595 is_generic: false,
1596 is_canonical: false,
1597 trait_args: vec![],
1598 is_derived: false,
1599 target_type: None,
1600 },
1601 );
1602 table
1603 .trait_impl_index
1604 .insert(("Printable".to_owned(), "Int".to_owned()), id);
1605
1606 assert_eq!(
1607 resolve_impl(&TraitRef::new("Printable"), &int(), &table),
1608 Some(id)
1609 );
1610 }
1611
1612 #[test]
1613 fn resolve_impl_not_found() {
1614 let table = ImplTable::new();
1615 assert_eq!(
1616 resolve_impl(&TraitRef::new("Printable"), &int(), &table),
1617 None
1618 );
1619 }
1620
1621 #[test]
1622 fn resolve_impl_wrong_type() {
1623 let mut table = ImplTable::new();
1624 let id = table.alloc_id();
1625 table.entries.insert(
1626 id,
1627 ImplEntry {
1628 id,
1629 trait_ref: Some(TraitRef::new("Printable")),
1630 type_key: "Int".to_owned(),
1631 methods: vec!["print".to_owned()],
1632 is_generic: false,
1633 is_canonical: false,
1634 trait_args: vec![],
1635 is_derived: false,
1636 target_type: None,
1637 },
1638 );
1639 table
1640 .trait_impl_index
1641 .insert(("Printable".to_owned(), "Int".to_owned()), id);
1642
1643 assert_eq!(
1645 resolve_impl(&TraitRef::new("Printable"), &bool_ty(), &table),
1646 None
1647 );
1648 }
1649
1650 #[test]
1653 fn resolve_method_inherent() {
1654 let method = make_fn_decl("to_string");
1655 let impl_block = make_impl_block(None, "User", vec![method]);
1656 let module = make_module(vec![impl_block]);
1657 let table = ImplTable::build_from(&module);
1658
1659 let r = resolve_method(&named("User"), "to_string", &table);
1660 assert!(r.is_some());
1661 let r = r.unwrap();
1662 assert!(r.trait_ref.is_none());
1663 assert_eq!(r.method, "to_string");
1664 }
1665
1666 #[test]
1667 fn resolve_method_from_trait_impl() {
1668 let method = make_fn_decl("serialize");
1669 let impl_block = make_impl_block(Some("Serializable"), "User", vec![method]);
1670 let module = make_module(vec![impl_block]);
1671 let table = ImplTable::build_from(&module);
1672
1673 let r = resolve_method(&named("User"), "serialize", &table);
1674 assert!(r.is_some());
1675 let r = r.unwrap();
1676 assert_eq!(
1677 r.trait_ref.as_ref().map(|t| t.name.as_str()),
1678 Some("Serializable")
1679 );
1680 assert_eq!(r.method, "serialize");
1681 }
1682
1683 #[test]
1684 fn resolve_method_not_found() {
1685 let table = ImplTable::new();
1686 assert!(resolve_method(&int(), "foo", &table).is_none());
1687 }
1688
1689 #[test]
1690 fn resolve_method_inherent_takes_priority_over_trait() {
1691 let inherent_method = make_fn_decl("display");
1692 let trait_method = make_fn_decl("display");
1693 let inherent_impl = make_impl_block(None, "User", vec![inherent_method]);
1694 let trait_impl = make_impl_block(Some("Display"), "User", vec![trait_method]);
1695 let module = make_module(vec![inherent_impl, trait_impl]);
1696 let table = ImplTable::build_from(&module);
1697
1698 let r = resolve_method(&named("User"), "display", &table).unwrap();
1699 assert!(r.trait_ref.is_none());
1701 }
1702
1703 #[test]
1706 fn coherence_detects_exact_overlap() {
1707 let impl1 = make_impl_block(Some("Equatable"), "Point", vec![make_fn_decl("equals")]);
1708 let impl2 = make_impl_block(Some("Equatable"), "Point", vec![make_fn_decl("equals")]);
1709 let module = make_module(vec![impl1, impl2]);
1710 let table = ImplTable::build_from(&module);
1711
1712 assert!(table.diags.has_errors());
1713 assert_eq!(table.diags.error_count(), 1);
1714 }
1715
1716 #[test]
1717 fn coherence_allows_different_types() {
1718 let impl1 = make_impl_block(Some("Equatable"), "Point", vec![make_fn_decl("equals")]);
1722 let impl2 = make_impl_block(Some("Equatable"), "Line", vec![make_fn_decl("equals")]);
1723 let module = make_module(vec![impl1, impl2]);
1724 let table = ImplTable::build_from(&module);
1725
1726 assert!(!table.diags.has_errors());
1727 }
1728
1729 #[test]
1730 fn coherence_allows_different_traits() {
1731 let impl1 = make_impl_block(Some("Equatable"), "Point", vec![make_fn_decl("equals")]);
1732 let impl2 = make_impl_block(Some("Comparable"), "Point", vec![make_fn_decl("compare")]);
1733 let module = make_module(vec![impl1, impl2]);
1734 let table = ImplTable::build_from(&module);
1735
1736 assert!(!table.diags.has_errors());
1737 }
1738
1739 fn count_e4012(table: &ImplTable) -> usize {
1743 table
1744 .diags
1745 .iter()
1746 .filter(|d| d.code == E_DUPLICATE_METHOD)
1747 .count()
1748 }
1749
1750 #[test]
1751 fn namespace_rejects_inherent_and_trait_same_method() {
1752 let inherent = make_impl_block(
1755 None,
1756 "Button",
1757 vec![make_fn_decl_ret("render", Some("String"))],
1758 );
1759 let trait_impl = make_impl_block(
1760 Some("Component"),
1761 "Button",
1762 vec![make_fn_decl_ret("render", Some("String"))],
1763 );
1764 let module = make_module(vec![inherent, trait_impl]);
1765 let table = ImplTable::build_from(&module);
1766
1767 assert!(table.diags.has_errors());
1768 assert_eq!(count_e4012(&table), 1);
1769 }
1770
1771 #[test]
1772 fn namespace_allows_empty_trait_impl_satisfied_by_inherent() {
1773 let inherent = make_impl_block(
1777 None,
1778 "Button",
1779 vec![make_fn_decl_ret("render", Some("String"))],
1780 );
1781 let trait_impl = make_impl_block(Some("Component"), "Button", vec![]);
1782 let module = make_module(vec![inherent, trait_impl]);
1783 let table = ImplTable::build_from(&module);
1784
1785 assert!(!table.diags.has_errors());
1786 assert_eq!(count_e4012(&table), 0);
1787 }
1788
1789 #[test]
1790 fn namespace_allows_distinct_method_names() {
1791 let inherent = make_impl_block(None, "Button", vec![make_fn_decl("click")]);
1793 let trait_impl = make_impl_block(
1794 Some("Component"),
1795 "Button",
1796 vec![make_fn_decl_ret("render", Some("String"))],
1797 );
1798 let module = make_module(vec![inherent, trait_impl]);
1799 let table = ImplTable::build_from(&module);
1800
1801 assert!(!table.diags.has_errors());
1802 assert_eq!(count_e4012(&table), 0);
1803 }
1804
1805 #[test]
1806 fn namespace_rejects_conflicting_signatures_across_traits() {
1807 let impl_a = make_impl_block(
1810 Some("TraitA"),
1811 "Widget",
1812 vec![make_fn_decl_ret("foo", Some("Int"))],
1813 );
1814 let impl_b = make_impl_block(
1815 Some("TraitB"),
1816 "Widget",
1817 vec![make_fn_decl_ret("foo", Some("String"))],
1818 );
1819 let module = make_module(vec![impl_a, impl_b]);
1820 let table = ImplTable::build_from(&module);
1821
1822 assert!(table.diags.has_errors());
1823 assert_eq!(count_e4012(&table), 1);
1824 }
1825
1826 #[test]
1827 fn namespace_rejects_class_body_and_trait_same_method() {
1828 let class = make_class_decl("Button", vec![make_fn_decl_ret("render", Some("String"))]);
1831 let trait_impl = make_impl_block(
1832 Some("Component"),
1833 "Button",
1834 vec![make_fn_decl_ret("render", Some("String"))],
1835 );
1836 let module = make_module(vec![class, trait_impl]);
1837 let table = ImplTable::build_from(&module);
1838
1839 assert!(table.diags.has_errors());
1840 assert_eq!(count_e4012(&table), 1);
1841 }
1842
1843 #[test]
1844 fn namespace_allows_class_body_satisfying_trait() {
1845 let class = make_class_decl("Button", vec![make_fn_decl_ret("render", Some("String"))]);
1848 let trait_impl = make_impl_block(Some("Component"), "Button", vec![]);
1849 let module = make_module(vec![class, trait_impl]);
1850 let table = ImplTable::build_from(&module);
1851
1852 assert!(!table.diags.has_errors());
1853 assert_eq!(count_e4012(&table), 0);
1854 }
1855
1856 #[test]
1857 fn coherence_skips_generic_impls() {
1858 use bock_ast::{GenericParam, Ident, TypePath};
1859
1860 let generic_param = GenericParam {
1861 id: 0,
1862 span: dummy_span(),
1863 name: Ident {
1864 name: "T".to_owned(),
1865 span: dummy_span(),
1866 },
1867 bounds: vec![],
1868 };
1869 let impl1 = make_air_node(NodeKind::ImplBlock {
1870 annotations: vec![],
1871 generic_params: vec![generic_param.clone()],
1872 trait_path: Some(TypePath {
1873 segments: vec![Ident {
1874 name: "Printable".to_owned(),
1875 span: dummy_span(),
1876 }],
1877 span: dummy_span(),
1878 }),
1879 trait_args: vec![],
1880 target: Box::new(make_type_named("T")),
1881 where_clause: vec![],
1882 methods: vec![],
1883 });
1884 let impl2 = make_air_node(NodeKind::ImplBlock {
1885 annotations: vec![],
1886 generic_params: vec![generic_param],
1887 trait_path: Some(TypePath {
1888 segments: vec![Ident {
1889 name: "Printable".to_owned(),
1890 span: dummy_span(),
1891 }],
1892 span: dummy_span(),
1893 }),
1894 trait_args: vec![],
1895 target: Box::new(make_type_named("T")),
1896 where_clause: vec![],
1897 methods: vec![],
1898 });
1899 let module = make_module(vec![impl1, impl2]);
1900 let table = ImplTable::build_from(&module);
1901
1902 assert!(!table.diags.has_errors());
1904 }
1905
1906 #[test]
1909 fn supertrait_registration_and_lookup() {
1910 let mut table = ImplTable::new();
1911 table.register_supertrait("Hashable", "Equatable");
1912 let supers = table.all_supertraits("Hashable");
1913 assert_eq!(supers, vec!["Equatable"]);
1914 }
1915
1916 #[test]
1917 fn supertrait_transitive() {
1918 let mut table = ImplTable::new();
1919 table.register_supertrait("C", "B");
1920 table.register_supertrait("B", "A");
1921 let supers = table.all_supertraits("C");
1922 assert_eq!(supers, vec!["B", "A"]);
1923 }
1924
1925 #[test]
1926 fn check_supertrait_obligations_satisfied() {
1927 let mut table = ImplTable::new();
1928 table.register_supertrait("Hashable", "Equatable");
1929
1930 let eq_method = make_fn_decl("equals");
1932 let hash_method = make_fn_decl("hash");
1933 let eq_impl = make_impl_block(Some("Equatable"), "User", vec![eq_method]);
1934 let hash_impl = make_impl_block(Some("Hashable"), "User", vec![hash_method]);
1935 let module = make_module(vec![eq_impl, hash_impl]);
1936 let table_built = ImplTable::build_from(&module);
1937
1938 let mut full_table = table_built;
1940 full_table.register_supertrait("Hashable", "Equatable");
1941
1942 assert!(check_supertrait_obligations(
1943 &TraitRef::new("Hashable"),
1944 &named("User"),
1945 &full_table
1946 ));
1947 }
1948
1949 #[test]
1950 fn check_supertrait_obligations_missing() {
1951 let mut table = ImplTable::new();
1952 table.register_supertrait("Hashable", "Equatable");
1953
1954 let hash_method = make_fn_decl("hash");
1956 let hash_impl = make_impl_block(Some("Hashable"), "User", vec![hash_method]);
1957 let module = make_module(vec![hash_impl]);
1958 let table_built = ImplTable::build_from(&module);
1959 let mut full_table = table_built;
1960 full_table.register_supertrait("Hashable", "Equatable");
1961
1962 assert!(!check_supertrait_obligations(
1964 &TraitRef::new("Hashable"),
1965 &named("User"),
1966 &full_table
1967 ));
1968 }
1969
1970 #[test]
1973 fn assoc_type_manual_registration() {
1974 let mut table = ImplTable::new();
1975 let impl_id = table.alloc_id();
1976 table.register_assoc_type(impl_id, "Item", int());
1977
1978 assert_eq!(table.resolve_assoc_type(impl_id, "Item"), Some(&int()));
1979 assert_eq!(table.resolve_assoc_type(impl_id, "Missing"), None);
1980 }
1981
1982 #[test]
1983 fn assoc_type_not_found_for_other_impl() {
1984 let mut table = ImplTable::new();
1985 let id1 = table.alloc_id();
1986 let id2 = table.alloc_id();
1987 table.register_assoc_type(id1, "Item", int());
1988
1989 assert_eq!(table.resolve_assoc_type(id2, "Item"), None);
1990 }
1991
1992 #[test]
1995 fn resolve_method_on_generic_type() {
1996 let method = make_fn_decl("push");
1997 let impl_block = make_impl_block(None, "List", vec![method]);
1998 let module = make_module(vec![impl_block]);
1999 let table = ImplTable::build_from(&module);
2000
2001 let receiver = Type::Named(NamedType {
2005 name: "List".to_owned(),
2006 });
2007 let r = resolve_method(&receiver, "push", &table);
2008 assert!(r.is_some());
2009 }
2010
2011 fn float() -> Type {
2014 Type::Primitive(PrimitiveType::Float)
2015 }
2016
2017 fn string() -> Type {
2018 Type::Primitive(PrimitiveType::String)
2019 }
2020
2021 fn char_ty() -> Type {
2022 Type::Primitive(PrimitiveType::Char)
2023 }
2024
2025 #[test]
2026 fn canonical_comparable_int_is_registered() {
2027 let mut table = ImplTable::new();
2028 register_canonical_conformances(&mut table);
2029 assert!(resolve_impl(&TraitRef::new("Comparable"), &int(), &table).is_some());
2030 }
2031
2032 #[test]
2033 fn canonical_equatable_covers_expected_primitives() {
2034 let mut table = ImplTable::new();
2035 register_canonical_conformances(&mut table);
2036 for ty in [int(), float(), string(), bool_ty(), char_ty()] {
2037 assert!(
2038 resolve_impl(&TraitRef::new("Equatable"), &ty, &table).is_some(),
2039 "Equatable should cover {ty:?}"
2040 );
2041 }
2042 }
2043
2044 #[test]
2045 fn canonical_comparable_excludes_bool() {
2046 let mut table = ImplTable::new();
2047 register_canonical_conformances(&mut table);
2048 assert!(resolve_impl(&TraitRef::new("Equatable"), &bool_ty(), &table).is_some());
2050 assert!(resolve_impl(&TraitRef::new("Comparable"), &bool_ty(), &table).is_none());
2051 }
2052
2053 #[test]
2054 fn canonical_hashable_excludes_float() {
2055 let mut table = ImplTable::new();
2056 register_canonical_conformances(&mut table);
2057 assert!(resolve_impl(&TraitRef::new("Equatable"), &float(), &table).is_some());
2059 assert!(resolve_impl(&TraitRef::new("Hashable"), &float(), &table).is_none());
2060 assert!(resolve_impl(&TraitRef::new("Hashable"), &int(), &table).is_some());
2062 }
2063
2064 #[test]
2065 fn canonical_covers_sized_numerics() {
2066 let mut table = ImplTable::new();
2067 register_canonical_conformances(&mut table);
2068 let i32_ty = Type::Primitive(PrimitiveType::Int32);
2069 let u64_ty = Type::Primitive(PrimitiveType::UInt64);
2070 let f32_ty = Type::Primitive(PrimitiveType::Float32);
2071 assert!(resolve_impl(&TraitRef::new("Comparable"), &i32_ty, &table).is_some());
2072 assert!(resolve_impl(&TraitRef::new("Equatable"), &u64_ty, &table).is_some());
2073 assert!(resolve_impl(&TraitRef::new("Comparable"), &f32_ty, &table).is_some());
2074 assert!(resolve_impl(&TraitRef::new("Hashable"), &f32_ty, &table).is_none());
2076 assert!(resolve_impl(&TraitRef::new("Hashable"), &i32_ty, &table).is_some());
2078 }
2079
2080 #[test]
2081 fn canonical_entries_are_marked_canonical() {
2082 let mut table = ImplTable::new();
2083 register_canonical_conformances(&mut table);
2084 let id = resolve_impl(&TraitRef::new("Comparable"), &int(), &table).unwrap();
2085 assert!(table.get_entry(id).unwrap().is_canonical);
2086 }
2087
2088 #[test]
2089 fn canonical_registers_comparable_equatable_supertrait() {
2090 let mut table = ImplTable::new();
2091 register_canonical_conformances(&mut table);
2092 assert_eq!(table.all_supertraits("Comparable"), vec!["Equatable"]);
2093 }
2094
2095 #[test]
2096 fn user_register_trait_impl_is_not_canonical() {
2097 let mut table = ImplTable::new();
2098 let id = table.register_trait_impl("MyTrait", &named("User"));
2099 assert!(!table.get_entry(id).unwrap().is_canonical);
2100 }
2101
2102 #[test]
2105 fn sealing_rejects_user_impl_core_trait_for_primitive() {
2106 let method = make_fn_decl("compare");
2108 let impl_block = make_impl_block(Some("Comparable"), "Int", vec![method]);
2109 let module = make_module(vec![impl_block]);
2110 let table = ImplTable::build_from(&module);
2111
2112 assert!(table.diags.has_errors());
2113 assert_eq!(table.diags.error_count(), 1);
2114 let diag = table.diags.iter().next().unwrap();
2115 assert_eq!(diag.code, E_SEALED_PRIMITIVE_IMPL);
2116 assert!(resolve_impl(&TraitRef::new("Comparable"), &int(), &table).is_none());
2118 assert!(diag.notes.iter().any(|n| n.contains("newtype")));
2120 }
2121
2122 #[test]
2123 fn sealing_rejects_each_sealed_core_trait_for_primitive() {
2124 for trait_name in ["Equatable", "Comparable", "Displayable", "Hashable"] {
2125 let impl_block = make_impl_block(Some(trait_name), "String", vec![make_fn_decl("m")]);
2126 let module = make_module(vec![impl_block]);
2127 let table = ImplTable::build_from(&module);
2128 assert!(
2129 table.diags.has_errors(),
2130 "{trait_name} for String should be sealed"
2131 );
2132 }
2133 }
2134
2135 #[test]
2136 fn sealing_allows_user_impl_core_trait_for_newtype() {
2137 let method = make_fn_decl("compare");
2139 let impl_block = make_impl_block(Some("Comparable"), "MyNewtype", vec![method]);
2140 let module = make_module(vec![impl_block]);
2141 let table = ImplTable::build_from(&module);
2142
2143 assert!(!table.diags.has_errors());
2144 assert!(resolve_impl(&TraitRef::new("Comparable"), &named("MyNewtype"), &table).is_some());
2145 }
2146
2147 #[test]
2148 fn sealing_allows_user_impl_noncore_trait_for_primitive() {
2149 let impl_block = make_impl_block(Some("MyTrait"), "Int", vec![make_fn_decl("m")]);
2152 let module = make_module(vec![impl_block]);
2153 let table = ImplTable::build_from(&module);
2154 assert!(!table.diags.has_errors());
2155 }
2156
2157 #[test]
2158 fn canonical_registration_bypasses_sealing() {
2159 let mut table = ImplTable::new();
2163 register_canonical_conformances(&mut table);
2164 assert!(!table.diags.has_errors());
2165 assert!(resolve_impl(&TraitRef::new("Comparable"), &int(), &table).is_some());
2166 }
2167
2168 #[test]
2171 fn param_impl_distinct_args_resolve_independently() {
2172 let from_int = make_param_impl_block("From", &["Int"], "Float", vec![make_fn_decl("from")]);
2175 let from_str =
2176 make_param_impl_block("From", &["String"], "Float", vec![make_fn_decl("from")]);
2177 let module = make_module(vec![from_int, from_str]);
2178 let table = ImplTable::build_from(&module);
2179
2180 assert!(
2181 !table.diags.has_errors(),
2182 "distinct trait args must not collide"
2183 );
2184 let float = Type::Primitive(PrimitiveType::Float);
2185 let id_int = resolve_impl(
2186 &TraitRef::parameterized("From", vec![int()]),
2187 &float,
2188 &table,
2189 );
2190 let id_str = resolve_impl(
2191 &TraitRef::parameterized("From", vec![Type::Primitive(PrimitiveType::String)]),
2192 &float,
2193 &table,
2194 );
2195 assert!(id_int.is_some(), "From[Int] for Float should resolve");
2196 assert!(id_str.is_some(), "From[String] for Float should resolve");
2197 assert_ne!(id_int, id_str, "the two impls must be distinct");
2198 }
2199
2200 #[test]
2201 fn param_impl_missing_arg_does_not_resolve() {
2202 let from_int = make_param_impl_block("From", &["Int"], "Float", vec![make_fn_decl("from")]);
2204 let module = make_module(vec![from_int]);
2205 let table = ImplTable::build_from(&module);
2206 let float = Type::Primitive(PrimitiveType::Float);
2207 assert!(resolve_impl(
2208 &TraitRef::parameterized("From", vec![bool_ty()]),
2209 &float,
2210 &table
2211 )
2212 .is_none());
2213 }
2214
2215 #[test]
2216 fn param_impl_duplicate_args_collide() {
2217 let a = make_param_impl_block("From", &["Int"], "Float", vec![make_fn_decl("from")]);
2219 let b = make_param_impl_block("From", &["Int"], "Float", vec![make_fn_decl("from")]);
2220 let module = make_module(vec![a, b]);
2221 let table = ImplTable::build_from(&module);
2222 assert!(
2223 table.diags.has_errors(),
2224 "duplicate From[Int] for Float must be a coherence error"
2225 );
2226 }
2227
2228 #[test]
2229 fn param_and_nonparam_indexes_are_independent() {
2230 let bare = make_impl_block(Some("From"), "Float", vec![make_fn_decl("from")]);
2234 let param = make_param_impl_block("From", &["Int"], "Float", vec![make_fn_decl("from")]);
2235 let module = make_module(vec![bare, param]);
2236 let table = ImplTable::build_from(&module);
2237 assert!(!table.diags.has_errors());
2238 let float = Type::Primitive(PrimitiveType::Float);
2239 assert!(resolve_impl(&TraitRef::new("From"), &float, &table).is_some());
2240 assert!(resolve_impl(
2241 &TraitRef::parameterized("From", vec![int()]),
2242 &float,
2243 &table
2244 )
2245 .is_some());
2246 }
2247
2248 #[test]
2251 fn blanket_into_derived_from_explicit_from() {
2252 let from = make_param_impl_block("From", &["Int"], "Float", vec![make_fn_decl("from")]);
2254 let module = make_module(vec![from]);
2255 let table = ImplTable::build_from(&module);
2256 assert!(!table.diags.has_errors());
2257
2258 let float = Type::Primitive(PrimitiveType::Float);
2259 let into_id = resolve_impl(
2261 &TraitRef::parameterized("Into", vec![float.clone()]),
2262 &int(),
2263 &table,
2264 );
2265 assert!(
2266 into_id.is_some(),
2267 "blanket Into[Float] for Int should resolve"
2268 );
2269 let entry = table.get_entry(into_id.unwrap()).unwrap();
2270 assert!(
2271 entry.is_derived,
2272 "synthesized Into entry must be is_derived"
2273 );
2274 }
2275
2276 #[test]
2277 fn blanket_into_does_not_clobber_explicit() {
2278 let explicit_into =
2281 make_param_impl_block("Into", &["Float"], "Apple", vec![make_fn_decl("into")]);
2282 let from = make_param_impl_block("From", &["Apple"], "Float", vec![make_fn_decl("from")]);
2283 let module = make_module(vec![explicit_into, from]);
2284 let table = ImplTable::build_from(&module);
2285
2286 assert!(
2287 !table.diags.has_errors(),
2288 "explicit Into + blanket From must not collide"
2289 );
2290 let float = Type::Primitive(PrimitiveType::Float);
2291 let apple = Type::Named(NamedType {
2292 name: "Apple".to_owned(),
2293 });
2294 let into_id = resolve_impl(
2295 &TraitRef::parameterized("Into", vec![float]),
2296 &apple,
2297 &table,
2298 )
2299 .expect("Into[Float] for Apple should resolve");
2300 let entry = table.get_entry(into_id).unwrap();
2301 assert!(
2302 !entry.is_derived,
2303 "explicit Into must win over the blanket-derived one"
2304 );
2305 }
2306
2307 #[test]
2310 fn canonical_conversions_register_from_int_for_float() {
2311 let mut table = ImplTable::new();
2312 register_canonical_conversions(&mut table);
2313 let float = Type::Primitive(PrimitiveType::Float);
2314 assert!(resolve_impl(
2316 &TraitRef::parameterized("From", vec![int()]),
2317 &float,
2318 &table
2319 )
2320 .is_some());
2321 assert!(resolve_impl(
2323 &TraitRef::parameterized("Into", vec![float]),
2324 &int(),
2325 &table
2326 )
2327 .is_some());
2328 }
2329
2330 #[test]
2331 fn canonical_conversions_widen_signed_ints() {
2332 let mut table = ImplTable::new();
2333 register_canonical_conversions(&mut table);
2334 let i8 = Type::Primitive(PrimitiveType::Int8);
2336 let i64 = Type::Primitive(PrimitiveType::Int64);
2337 assert!(resolve_impl(
2338 &TraitRef::parameterized("From", vec![i8.clone()]),
2339 &i64,
2340 &table
2341 )
2342 .is_some());
2343 assert!(resolve_impl(
2344 &TraitRef::parameterized("From", vec![i8.clone()]),
2345 &int(),
2346 &table
2347 )
2348 .is_some());
2349 assert!(resolve_impl(&TraitRef::parameterized("From", vec![i64]), &i8, &table).is_none());
2351 }
2352
2353 #[test]
2354 fn canonical_conversions_try_from_string() {
2355 let mut table = ImplTable::new();
2356 register_canonical_conversions(&mut table);
2357 let string = Type::Primitive(PrimitiveType::String);
2358 assert!(resolve_impl(
2359 &TraitRef::parameterized("TryFrom", vec![string.clone()]),
2360 &int(),
2361 &table
2362 )
2363 .is_some());
2364 assert!(resolve_impl(
2365 &TraitRef::parameterized("TryFrom", vec![string]),
2366 &Type::Primitive(PrimitiveType::Float),
2367 &table
2368 )
2369 .is_some());
2370 }
2371
2372 #[test]
2373 fn blanket_into_not_derived_for_try_from() {
2374 let tryfrom = make_param_impl_block(
2376 "TryFrom",
2377 &["String"],
2378 "Int",
2379 vec![make_fn_decl("try_from")],
2380 );
2381 let module = make_module(vec![tryfrom]);
2382 let table = ImplTable::build_from(&module);
2383 let string = Type::Primitive(PrimitiveType::String);
2384 assert!(
2385 resolve_impl(
2386 &TraitRef::parameterized("TryInto", vec![int()]),
2387 &string,
2388 &table
2389 )
2390 .is_none(),
2391 "no TryInto should be synthesized"
2392 );
2393 assert!(
2394 resolve_impl(
2395 &TraitRef::parameterized("Into", vec![int()]),
2396 &string,
2397 &table
2398 )
2399 .is_none(),
2400 "TryFrom must not synthesize a (lossless) Into"
2401 );
2402 }
2403}