1use ecow::EcoString;
2use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
3use std::collections::BTreeMap;
4
5use syntax::ast::{Generic, Visibility};
6use syntax::program::{DefinitionBody, MethodSignatures};
7use syntax::types::{CompoundKind, Symbol, Type, build_substitution_map, substitute};
8
9use crate::store::Store;
10
11#[derive(Clone, Debug)]
12pub enum MemberKind {
13 Field {
14 ty: Type,
15 visibility: Visibility,
16 },
17 Method {
20 ty: Type,
21 },
22}
23
24#[derive(Clone, Debug)]
25pub struct ResolvedMember {
26 pub name: EcoString,
27 pub depth: usize,
28 pub embed_path: Vec<EcoString>,
29 pub declaring_type: Symbol,
30 pub indirect: bool,
31 pub kind: MemberKind,
32}
33
34#[derive(Clone, Debug)]
35pub enum Resolution {
36 Found(ResolvedMember),
37 Ambiguous { sources: Vec<Symbol> },
38 NotFound,
39}
40
41pub fn has_direct_embed(store: &Store, ty: &Type) -> bool {
42 let Type::Nominal { id, .. } = store.deep_resolve_alias(&ty.strip_refs()) else {
43 return false;
44 };
45 store
46 .fields_of(id.as_str())
47 .is_some_and(|fields| fields.iter().any(|f| f.embedded))
48}
49
50pub fn resolve_selector(store: &Store, outer: &Type, name: &str) -> Resolution {
51 let entries = walk(store, outer);
52 resolve_in_entries(store, &entries, outer, name)
53}
54
55pub fn promoted_method_set(store: &Store, outer: &Type) -> MethodSignatures {
56 let entries = walk(store, outer);
57
58 let mut names: HashSet<EcoString> = HashSet::default();
59 for entry in &entries {
60 collect_member_names(store, &entry.ty, &mut names);
61 }
62
63 let mut result = MethodSignatures::default();
64 for name in names {
65 if let Resolution::Found(member) = resolve_in_entries(store, &entries, outer, &name)
66 && let MemberKind::Method { ty } = member.kind
67 {
68 result.insert(name, ty);
69 }
70 }
71 result
72}
73
74#[derive(Clone)]
75struct Entry {
76 ty: Type,
77 depth: usize,
78 indirect: bool,
80 multiples: bool,
82 embed_path: Vec<EcoString>,
83}
84
85fn walk(store: &Store, outer: &Type) -> Vec<Entry> {
86 let mut visited: Vec<Entry> = Vec::new();
87 let mut seen: HashSet<String> = HashSet::default();
88
89 let Some(root) = nominal_entry(store, outer.clone(), 0, false, false, Vec::new()) else {
90 return visited;
91 };
92 let mut current = vec![root];
93 let mut depth = 0;
94
95 while !current.is_empty() {
96 let mut next: Vec<Entry> = Vec::new();
97
98 for entry in ¤t {
99 if !seen.insert(type_key(&entry.ty)) {
101 continue;
102 }
103 visited.push(entry.clone());
104
105 let Some(id) = entry.ty.get_qualified_id() else {
107 continue;
108 };
109 if store.get_interface(id).is_some() {
110 continue;
111 }
112 let Some(fields) = store.fields_of(id) else {
113 continue;
114 };
115 for field in fields {
116 if !field.embedded {
117 continue;
118 }
119 let field_ty = instantiate_field(store, &entry.ty, &field.ty);
120 let resolved_field = store.deep_resolve_alias(&field_ty);
121 let (target, is_pointer) = deref_once(&resolved_field);
122 let mut path = entry.embed_path.clone();
123 path.push(field.name.clone());
124 if let Some(child) = nominal_entry(
125 store,
126 target,
127 depth + 1,
128 entry.indirect || is_pointer,
129 entry.multiples,
130 path,
131 ) {
132 next.push(child);
133 }
134 }
135 }
136
137 current = consolidate(next);
138 depth += 1;
139 }
140
141 visited
142}
143
144fn resolve_in_entries(store: &Store, entries: &[Entry], outer: &Type, name: &str) -> Resolution {
147 let mut by_depth: BTreeMap<usize, Vec<(&Entry, Candidate)>> = BTreeMap::new();
148 for entry in entries {
149 if let Some(candidate) = entry_candidate(store, &entry.ty, name) {
150 by_depth
151 .entry(entry.depth)
152 .or_default()
153 .push((entry, candidate));
154 }
155 }
156
157 let Some((_, candidates)) = by_depth.into_iter().next() else {
158 return Resolution::NotFound;
159 };
160
161 if let [(entry, candidate)] = candidates.as_slice()
162 && !entry.multiples
163 {
164 return Resolution::Found(build_member(outer, name, entry, candidate));
165 }
166
167 let mut sources: Vec<Symbol> = candidates
168 .iter()
169 .map(|(_, c)| c.declaring_type.clone())
170 .collect();
171 sources.sort();
172 sources.dedup();
173 Resolution::Ambiguous { sources }
174}
175
176struct Candidate {
177 declaring_type: Symbol,
178 detail: CandidateDetail,
179}
180
181enum CandidateDetail {
182 Field { ty: Type, visibility: Visibility },
183 Method { ty: Type },
184}
185
186fn entry_candidate(store: &Store, ty: &Type, name: &str) -> Option<Candidate> {
189 let id = ty.get_qualified_id()?;
190
191 if store.get_interface(id).is_some() {
192 let methods = store.get_all_methods(ty, &Default::default());
193 let method_ty = methods.get(name)?.clone();
194 return Some(Candidate {
195 declaring_type: Symbol::from_raw(id),
196 detail: CandidateDetail::Method { ty: method_ty },
197 });
198 }
199
200 if let Some(method_ty) = store.get_own_methods(id).and_then(|m| m.get(name)) {
201 return Some(Candidate {
202 declaring_type: Symbol::from_raw(id),
203 detail: CandidateDetail::Method {
204 ty: instantiate_method(store, ty, method_ty)?,
205 },
206 });
207 }
208
209 if let Some(field) = store
210 .fields_of(id)
211 .and_then(|fields| fields.iter().find(|f| f.name == name))
212 {
213 return Some(Candidate {
214 declaring_type: Symbol::from_raw(id),
215 detail: CandidateDetail::Field {
216 ty: instantiate_field(store, ty, &field.ty),
217 visibility: field.visibility,
218 },
219 });
220 }
221
222 None
223}
224
225fn build_member(outer: &Type, name: &str, entry: &Entry, candidate: &Candidate) -> ResolvedMember {
226 let kind = match &candidate.detail {
227 CandidateDetail::Field { ty, visibility } => MemberKind::Field {
228 ty: ty.clone(),
229 visibility: *visibility,
230 },
231 CandidateDetail::Method { ty } => {
232 let method_ty = if entry.depth == 0 {
237 ty.clone()
238 } else if !entry.indirect && method_has_pointer_receiver(ty) {
239 ty.with_replaced_first_param(&ref_of(outer))
240 } else {
241 ty.with_replaced_first_param(outer)
242 };
243 MemberKind::Method { ty: method_ty }
244 }
245 };
246
247 ResolvedMember {
248 name: name.into(),
249 depth: entry.depth,
250 embed_path: entry.embed_path.clone(),
251 declaring_type: candidate.declaring_type.clone(),
252 indirect: entry.indirect,
253 kind,
254 }
255}
256
257fn collect_member_names(store: &Store, ty: &Type, names: &mut HashSet<EcoString>) {
259 let Some(id) = ty.get_qualified_id() else {
260 return;
261 };
262 if store.get_interface(id).is_some() {
263 for key in store.get_all_methods(ty, &Default::default()).keys() {
264 names.insert(key.clone());
265 }
266 return;
267 }
268 if let Some(methods) = store.get_own_methods(id) {
269 for key in methods.keys() {
270 names.insert(key.clone());
271 }
272 }
273 if let Some(fields) = store.fields_of(id) {
274 for field in fields {
275 names.insert(field.name.clone());
276 }
277 }
278}
279
280fn nominal_entry(
282 store: &Store,
283 target: Type,
284 depth: usize,
285 indirect: bool,
286 multiples: bool,
287 embed_path: Vec<EcoString>,
288) -> Option<Entry> {
289 let resolved = store.deep_resolve_alias(&target);
290 if !matches!(resolved, Type::Nominal { .. }) {
291 return None;
292 }
293 Some(Entry {
294 ty: resolved,
295 depth,
296 indirect,
297 multiples,
298 embed_path,
299 })
300}
301
302fn consolidate(list: Vec<Entry>) -> Vec<Entry> {
305 let mut result: Vec<Entry> = Vec::with_capacity(list.len());
306 let mut index_of: HashMap<String, usize> = HashMap::default();
307 for entry in list {
308 let key = type_key(&entry.ty);
309 if let Some(&i) = index_of.get(&key) {
310 result[i].multiples = true;
311 } else {
312 index_of.insert(key, result.len());
313 result.push(entry);
314 }
315 }
316 result
317}
318
319fn type_key(ty: &Type) -> String {
321 match ty {
322 Type::Nominal { id, params, .. } if params.is_empty() => id.as_str().to_string(),
323 Type::Nominal { id, params, .. } => {
324 let args: Vec<String> = params.iter().map(type_key).collect();
325 format!("{}<{}>", id, args.join(","))
326 }
327 other => other.to_string(),
328 }
329}
330
331fn deref_once(ty: &Type) -> (Type, bool) {
333 if ty.is_ref() {
334 (ty.inner().unwrap_or(Type::Error), true)
335 } else {
336 (ty.clone(), false)
337 }
338}
339
340fn method_has_pointer_receiver(method_ty: &Type) -> bool {
341 let body = match method_ty {
342 Type::Forall { body, .. } => body.as_ref(),
343 other => other,
344 };
345 matches!(body, Type::Function(f) if f.params.first().is_some_and(Type::is_ref))
346}
347
348fn ref_of(ty: &Type) -> Type {
349 Type::Compound {
350 kind: CompoundKind::Ref,
351 args: vec![ty.clone()],
352 }
353}
354
355fn declaring_generics<'a>(store: &'a Store, id: &str) -> &'a [Generic] {
356 match store.get_definition(id).map(|d| &d.body) {
357 Some(
358 DefinitionBody::Struct { generics, .. }
359 | DefinitionBody::Enum { generics, .. }
360 | DefinitionBody::TypeAlias { generics, .. },
361 ) => generics,
362 Some(DefinitionBody::Interface { definition }) => &definition.generics,
363 _ => &[],
364 }
365}
366
367fn instantiate_field(store: &Store, container: &Type, member_ty: &Type) -> Type {
368 let Some(id) = container.get_qualified_id() else {
369 return member_ty.clone();
370 };
371 let args = container.get_type_params().unwrap_or_default();
372 if args.is_empty() {
373 return member_ty.clone();
374 }
375 substitute(
376 member_ty,
377 &build_substitution_map(declaring_generics(store, id), args),
378 )
379}
380
381fn instantiate_method(store: &Store, container: &Type, method_ty: &Type) -> Option<Type> {
382 let Some(id) = container.get_qualified_id() else {
383 return Some(method_ty.clone());
384 };
385 let args = container.get_type_params().unwrap_or_default();
386 let arity = declaring_generics(store, id).len();
387 if args.is_empty() || arity == 0 {
388 return Some(method_ty.clone());
389 }
390 if let Type::Forall { vars, body } = method_ty
391 && args.len() == arity
392 && vars.len() >= arity
393 {
394 let (impl_vars, method_vars) = vars.split_at(arity);
395 if receiver_is_simple(body, id, impl_vars) {
396 let map: HashMap<EcoString, Type> = impl_vars
397 .iter()
398 .cloned()
399 .zip(args.iter().cloned())
400 .collect();
401 let new_body = substitute(body, &map);
402 return Some(if method_vars.is_empty() {
403 new_body
404 } else {
405 Type::Forall {
406 vars: method_vars.to_vec(),
407 body: Box::new(new_body),
408 }
409 });
410 }
411 }
412 let receiver = method_receiver(method_ty)?;
416 (receiver.strip_refs() == *container).then(|| method_ty.clone())
417}
418
419fn method_receiver(method_ty: &Type) -> Option<&Type> {
421 let body = match method_ty {
422 Type::Forall { body, .. } => body.as_ref(),
423 other => other,
424 };
425 match body {
426 Type::Function(f) => f.params.first(),
427 _ => None,
428 }
429}
430
431fn receiver_is_simple(body: &Type, container_id: &str, impl_vars: &[EcoString]) -> bool {
434 let Type::Function(f) = body else {
435 return false;
436 };
437 let Some(receiver) = f.params.first() else {
438 return false;
439 };
440 let Type::Nominal { id, params, .. } = receiver.strip_refs() else {
441 return false;
442 };
443 id.as_str() == container_id
444 && params.len() == impl_vars.len()
445 && params
446 .iter()
447 .zip(impl_vars)
448 .all(|(p, v)| matches!(p, Type::Parameter(name) if name == v))
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454 use syntax::ast::{Annotation, Span, StructFieldDefinition, StructKind};
455 use syntax::program::Visibility as ProgVis;
456 use syntax::program::{Attributes, Definition, DefinitionBody, Interface};
457
458 const MODULE: &str = "m";
459
460 fn nominal(name: &str) -> Type {
461 Type::Nominal {
462 id: Symbol::from_parts(MODULE, name),
463 params: vec![],
464 underlying_ty: None,
465 }
466 }
467
468 fn value_method(owner: &str) -> Type {
469 Type::function(
470 vec![nominal(owner)],
471 vec![false],
472 vec![],
473 Box::new(Type::string()),
474 )
475 }
476
477 fn pointer_method(owner: &str) -> Type {
478 Type::function(
479 vec![ref_of(&nominal(owner))],
480 vec![false],
481 vec![],
482 Box::new(Type::string()),
483 )
484 }
485
486 fn interface_method() -> Type {
488 Type::function(vec![], vec![], vec![], Box::new(Type::string()))
489 }
490
491 fn field(name: &str, ty: Type, embedded: bool) -> StructFieldDefinition {
492 StructFieldDefinition {
493 doc: None,
494 attributes: vec![],
495 name: name.into(),
496 name_span: Span::dummy(),
497 annotation: Annotation::Unknown,
498 visibility: Visibility::Public,
499 ty,
500 embedded,
501 }
502 }
503
504 struct Builder {
505 store: Store,
506 }
507
508 impl Builder {
509 fn new() -> Self {
510 let mut store = Store::new();
511 store.add_module(MODULE);
512 Builder { store }
513 }
514
515 fn insert(&mut self, name: &str, body: DefinitionBody) -> &mut Self {
516 let def = Definition {
517 visibility: ProgVis::Public,
518 ty: nominal(name),
519 name: Some(name.into()),
520 name_span: None,
521 doc: None,
522 body,
523 };
524 self.store
525 .get_module_mut(MODULE)
526 .unwrap()
527 .definitions
528 .insert(Symbol::from_parts(MODULE, name), def);
529 self
530 }
531
532 fn struct_(
533 &mut self,
534 name: &str,
535 fields: Vec<StructFieldDefinition>,
536 methods: Vec<(&str, Type)>,
537 ) -> &mut Self {
538 let mut method_map = MethodSignatures::default();
539 for (n, t) in methods {
540 method_map.insert(n.into(), t);
541 }
542 self.insert(
543 name,
544 DefinitionBody::Struct {
545 generics: vec![],
546 fields,
547 kind: StructKind::Record,
548 methods: method_map,
549 constructor: None,
550 attributes: Attributes::default(),
551 },
552 )
553 }
554
555 fn generic_struct(
556 &mut self,
557 name: &str,
558 generics: Vec<&str>,
559 fields: Vec<StructFieldDefinition>,
560 methods: Vec<(&str, Type)>,
561 ) -> &mut Self {
562 let mut method_map = MethodSignatures::default();
563 for (n, t) in methods {
564 method_map.insert(n.into(), t);
565 }
566 self.insert(
567 name,
568 DefinitionBody::Struct {
569 generics: generics
570 .into_iter()
571 .map(|g| Generic {
572 name: g.into(),
573 bounds: vec![],
574 span: Span::dummy(),
575 })
576 .collect(),
577 fields,
578 kind: StructKind::Record,
579 methods: method_map,
580 constructor: None,
581 attributes: Attributes::default(),
582 },
583 )
584 }
585
586 fn interface(&mut self, name: &str, methods: Vec<&str>, parents: Vec<&str>) -> &mut Self {
587 let mut method_map = MethodSignatures::default();
588 for n in methods {
589 method_map.insert(n.into(), interface_method());
590 }
591 self.insert(
592 name,
593 DefinitionBody::Interface {
594 definition: Interface {
595 name: name.into(),
596 generics: vec![],
597 parents: parents.into_iter().map(nominal).collect(),
598 methods: method_map,
599 },
600 },
601 )
602 }
603 }
604
605 fn vembed(target: &str) -> StructFieldDefinition {
606 field(target, nominal(target), true)
607 }
608
609 fn pembed(target: &str) -> StructFieldDefinition {
610 field(target, ref_of(&nominal(target)), true)
611 }
612
613 fn param(name: &str) -> Type {
614 Type::Parameter(name.into())
615 }
616
617 fn generic_nominal(name: &str, args: Vec<Type>) -> Type {
618 Type::Nominal {
619 id: Symbol::from_parts(MODULE, name),
620 params: args,
621 underlying_ty: None,
622 }
623 }
624
625 fn generic_value_method(owner: &str, impl_var: &str, ret: Type) -> Type {
626 Type::Forall {
627 vars: vec![impl_var.into()],
628 body: Box::new(Type::function(
629 vec![generic_nominal(owner, vec![param(impl_var)])],
630 vec![false],
631 vec![],
632 Box::new(ret),
633 )),
634 }
635 }
636
637 fn found(resolution: Resolution) -> ResolvedMember {
638 match resolution {
639 Resolution::Found(member) => member,
640 other => panic!("expected Found, got {other:?}"),
641 }
642 }
643
644 fn is_pointer_receiver(member: &ResolvedMember) -> bool {
645 match &member.kind {
646 MemberKind::Method { ty } => ty.get_function_params().unwrap()[0].is_ref(),
647 other => panic!("expected a method, got {other:?}"),
648 }
649 }
650
651 #[test]
652 fn direct_method_at_depth_zero() {
653 let mut b = Builder::new();
654 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
655 let member = found(resolve_selector(&b.store, &nominal("N0"), "m"));
656 assert_eq!(member.depth, 0);
657 assert!(!is_pointer_receiver(&member));
658 }
659
660 #[test]
661 fn value_embed_promotes_value_method() {
662 let mut b = Builder::new();
663 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
664 b.struct_("N1", vec![vembed("N0")], vec![]);
665 let member = found(resolve_selector(&b.store, &nominal("N1"), "m"));
666 assert_eq!(member.depth, 1);
667 assert_eq!(member.embed_path, vec![EcoString::from("N0")]);
668 assert!(!member.indirect);
669 assert!(!is_pointer_receiver(&member));
670 }
671
672 #[test]
673 fn value_embed_of_pointer_method_is_pointer_only() {
674 let mut b = Builder::new();
675 b.struct_("N0", vec![], vec![("pm", pointer_method("N0"))]);
676 b.struct_("N1", vec![vembed("N0")], vec![]);
677 let member = found(resolve_selector(&b.store, &nominal("N1"), "pm"));
678 assert!(!member.indirect);
679 assert!(is_pointer_receiver(&member));
680 }
681
682 #[test]
683 fn pointer_embed_puts_pointer_method_in_value_set() {
684 let mut b = Builder::new();
685 b.struct_("N0", vec![], vec![("pm", pointer_method("N0"))]);
686 b.struct_("N1", vec![pembed("N0")], vec![]);
687 let member = found(resolve_selector(&b.store, &nominal("N1"), "pm"));
688 assert!(member.indirect);
689 assert!(!is_pointer_receiver(&member));
690 }
691
692 #[test]
693 fn pointer_embed_of_value_method_is_value() {
694 let mut b = Builder::new();
695 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
696 b.struct_("N1", vec![pembed("N0")], vec![]);
697 let member = found(resolve_selector(&b.store, &nominal("N1"), "m"));
698 assert!(member.indirect);
699 assert!(!is_pointer_receiver(&member));
700 }
701
702 #[test]
703 fn three_value_edges_keep_pointer_method_pointer_only() {
704 let mut b = Builder::new();
705 b.struct_("N0", vec![], vec![("pm", pointer_method("N0"))]);
706 b.struct_("N1", vec![vembed("N0")], vec![]);
707 b.struct_("N2", vec![vembed("N1")], vec![]);
708 b.struct_("N3", vec![vembed("N2")], vec![]);
709 let member = found(resolve_selector(&b.store, &nominal("N3"), "pm"));
710 assert_eq!(member.depth, 3);
711 assert!(!member.indirect);
712 assert!(is_pointer_receiver(&member));
713 }
714
715 #[test]
716 fn pointer_edge_mid_three_level_path_puts_pointer_method_in_value_set() {
717 let mut b = Builder::new();
718 b.struct_("N0", vec![], vec![("pm", pointer_method("N0"))]);
719 b.struct_("N1", vec![vembed("N0")], vec![]);
720 b.struct_("N2", vec![pembed("N1")], vec![]);
721 b.struct_("N3", vec![vembed("N2")], vec![]);
722 let member = found(resolve_selector(&b.store, &nominal("N3"), "pm"));
723 assert_eq!(member.depth, 3);
724 assert!(member.indirect);
725 assert!(!is_pointer_receiver(&member));
726 }
727
728 #[test]
729 fn value_method_promotes_through_three_value_edges() {
730 let mut b = Builder::new();
731 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
732 b.struct_("N1", vec![vembed("N0")], vec![]);
733 b.struct_("N2", vec![vembed("N1")], vec![]);
734 b.struct_("N3", vec![vembed("N2")], vec![]);
735 let member = found(resolve_selector(&b.store, &nominal("N3"), "m"));
736 assert_eq!(member.depth, 3);
737 assert!(!member.indirect);
738 assert!(!is_pointer_receiver(&member));
739 }
740
741 #[test]
742 fn diamond_is_ambiguous() {
743 let mut b = Builder::new();
744 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
745 b.struct_("N1", vec![vembed("N0")], vec![]);
746 b.struct_("N2", vec![vembed("N0")], vec![]);
747 b.struct_("N3", vec![vembed("N1"), vembed("N2")], vec![]);
748 assert!(matches!(
749 resolve_selector(&b.store, &nominal("N3"), "m"),
750 Resolution::Ambiguous { .. }
751 ));
752 }
753
754 #[test]
755 fn shallower_path_shadows_deeper_diamond() {
756 let mut b = Builder::new();
757 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
758 b.struct_("N1", vec![vembed("N0")], vec![]);
759 b.struct_("N3", vec![vembed("N0"), vembed("N1")], vec![]);
760 let member = found(resolve_selector(&b.store, &nominal("N3"), "m"));
761 assert_eq!(member.depth, 1);
762 }
763
764 #[test]
765 fn own_member_shadows_promoted() {
766 let mut b = Builder::new();
767 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
768 b.struct_("N1", vec![vembed("N0")], vec![("m", value_method("N1"))]);
769 let member = found(resolve_selector(&b.store, &nominal("N1"), "m"));
770 assert_eq!(member.depth, 0);
771 assert_eq!(member.declaring_type.as_str(), "m.N1");
772 }
773
774 #[test]
775 fn field_promotes() {
776 let mut b = Builder::new();
777 b.struct_("N0", vec![field("f", Type::int(), false)], vec![]);
778 b.struct_("N1", vec![vembed("N0")], vec![]);
779 let member = found(resolve_selector(&b.store, &nominal("N1"), "f"));
780 assert_eq!(member.depth, 1);
781 assert!(matches!(member.kind, MemberKind::Field { .. }));
782 }
783
784 #[test]
785 fn field_and_method_collide_across_embeds() {
786 let mut b = Builder::new();
787 b.struct_("A", vec![field("x", Type::int(), false)], vec![]);
788 b.struct_("B", vec![], vec![("x", value_method("B"))]);
789 b.struct_("N2", vec![vembed("A"), vembed("B")], vec![]);
790 assert!(matches!(
791 resolve_selector(&b.store, &nominal("N2"), "x"),
792 Resolution::Ambiguous { .. }
793 ));
794 }
795
796 #[test]
797 fn pointer_cycle_terminates_and_resolves() {
798 let mut b = Builder::new();
799 b.struct_("N0", vec![pembed("N1")], vec![("a", value_method("N0"))]);
800 b.struct_("N1", vec![vembed("N0")], vec![("bb", value_method("N1"))]);
801 assert_eq!(
802 found(resolve_selector(&b.store, &nominal("N0"), "a")).depth,
803 0
804 );
805 assert_eq!(
806 found(resolve_selector(&b.store, &nominal("N0"), "bb")).depth,
807 1
808 );
809 assert_eq!(
810 found(resolve_selector(&b.store, &nominal("N1"), "a")).depth,
811 1
812 );
813 assert!(matches!(
814 resolve_selector(&b.store, &nominal("N0"), "absent"),
815 Resolution::NotFound
816 ));
817 }
818
819 #[test]
820 fn embedded_interface_promotes_value_callable() {
821 let mut b = Builder::new();
822 b.interface("I", vec!["speak"], vec![]);
823 b.struct_("N2", vec![vembed("I")], vec![]);
824 let member = found(resolve_selector(&b.store, &nominal("N2"), "speak"));
825 assert_eq!(member.depth, 1);
826 assert!(!is_pointer_receiver(&member));
827 }
828
829 #[test]
830 fn struct_embedding_interface_and_struct_with_same_method_is_ambiguous() {
831 let mut b = Builder::new();
832 b.interface("I", vec!["speak"], vec![]);
833 b.struct_("S", vec![], vec![("speak", value_method("S"))]);
834 b.struct_("N2", vec![vembed("I"), vembed("S")], vec![]);
835 assert!(matches!(
836 resolve_selector(&b.store, &nominal("N2"), "speak"),
837 Resolution::Ambiguous { .. }
838 ));
839 assert!(!promoted_method_set(&b.store, &nominal("N2")).contains_key("speak"));
840 }
841
842 #[test]
843 fn method_set_includes_promoted_excludes_ambiguous() {
844 let mut b = Builder::new();
845 b.struct_(
846 "N0",
847 vec![],
848 vec![("m", value_method("N0")), ("pm", pointer_method("N0"))],
849 );
850 b.struct_("N1", vec![vembed("N0")], vec![("o", value_method("N1"))]);
851 let set = promoted_method_set(&b.store, &nominal("N1"));
852 assert!(set.contains_key("o"));
853 assert!(set.contains_key("m"));
854 assert!(set.contains_key("pm"));
855 assert!(!set.get("m").unwrap().get_function_params().unwrap()[0].is_ref());
856 assert!(set.get("pm").unwrap().get_function_params().unwrap()[0].is_ref());
857 }
858
859 #[test]
860 fn method_set_drops_ambiguous_diamond_member() {
861 let mut b = Builder::new();
862 b.struct_("N0", vec![], vec![("m", value_method("N0"))]);
863 b.struct_("N1", vec![vembed("N0")], vec![]);
864 b.struct_("N2", vec![vembed("N0")], vec![]);
865 b.struct_("N3", vec![vembed("N1"), vembed("N2")], vec![]);
866 assert!(!promoted_method_set(&b.store, &nominal("N3")).contains_key("m"));
867 }
868
869 #[test]
870 fn has_direct_embed_detects_embeds() {
871 let mut b = Builder::new();
872 b.struct_("N0", vec![field("f", Type::int(), false)], vec![]);
873 b.struct_("N1", vec![vembed("N0")], vec![]);
874 assert!(!has_direct_embed(&b.store, &nominal("N0")));
875 assert!(has_direct_embed(&b.store, &nominal("N1")));
876 assert!(has_direct_embed(&b.store, &ref_of(&nominal("N1"))));
877 }
878
879 fn method_return(member: &ResolvedMember) -> Type {
880 match &member.kind {
881 MemberKind::Method { ty } => ty.get_function_ret().unwrap().clone(),
882 other => panic!("expected a method, got {other:?}"),
883 }
884 }
885
886 #[test]
887 fn generic_embed_promotes_field_at_instantiation() {
888 let mut b = Builder::new();
889 b.generic_struct(
890 "Box",
891 vec!["T"],
892 vec![field("value", param("T"), false)],
893 vec![],
894 );
895 b.struct_(
896 "Outer",
897 vec![field(
898 "Box",
899 generic_nominal("Box", vec![Type::int()]),
900 true,
901 )],
902 vec![],
903 );
904 let member = found(resolve_selector(&b.store, &nominal("Outer"), "value"));
905 match member.kind {
906 MemberKind::Field { ty, .. } => assert_eq!(ty, Type::int()),
907 other => panic!("expected a field, got {other:?}"),
908 }
909 }
910
911 #[test]
912 fn generic_embed_promotes_method_at_instantiation() {
913 let mut b = Builder::new();
914 b.generic_struct(
915 "Box",
916 vec!["T"],
917 vec![],
918 vec![("get", generic_value_method("Box", "T", param("T")))],
919 );
920 b.struct_(
921 "Outer",
922 vec![field(
923 "Box",
924 generic_nominal("Box", vec![Type::string()]),
925 true,
926 )],
927 vec![],
928 );
929 let member = found(resolve_selector(&b.store, &nominal("Outer"), "get"));
930 assert_eq!(method_return(&member), Type::string());
931 }
932
933 #[test]
934 fn generic_embedder_flows_its_param_into_the_target() {
935 let mut b = Builder::new();
936 b.generic_struct(
937 "Box",
938 vec!["T"],
939 vec![],
940 vec![("get", generic_value_method("Box", "T", param("T")))],
941 );
942 b.generic_struct(
943 "Outer",
944 vec!["U"],
945 vec![field("Box", generic_nominal("Box", vec![param("U")]), true)],
946 vec![],
947 );
948 let member = found(resolve_selector(
949 &b.store,
950 &generic_nominal("Outer", vec![Type::int()]),
951 "get",
952 ));
953 assert_eq!(method_return(&member), Type::int());
954 }
955
956 #[test]
957 fn renamed_impl_param_is_captured() {
958 let mut b = Builder::new();
960 b.generic_struct(
961 "Box",
962 vec!["T"],
963 vec![],
964 vec![("get", generic_value_method("Box", "V", param("V")))],
965 );
966 b.struct_(
967 "Outer",
968 vec![field(
969 "Box",
970 generic_nominal("Box", vec![Type::int()]),
971 true,
972 )],
973 vec![],
974 );
975 let member = found(resolve_selector(&b.store, &nominal("Outer"), "get"));
976 assert_eq!(method_return(&member), Type::int());
977 }
978
979 #[test]
980 fn specialized_impl_method_is_skipped() {
981 let specialized = Type::Forall {
982 vars: vec!["V".into()],
983 body: Box::new(Type::function(
984 vec![generic_nominal(
985 "Box",
986 vec![Type::Compound {
987 kind: CompoundKind::Slice,
988 args: vec![param("V")],
989 }],
990 )],
991 vec![false],
992 vec![],
993 Box::new(param("V")),
994 )),
995 };
996 let mut b = Builder::new();
997 b.generic_struct("Box", vec!["T"], vec![], vec![("weird", specialized)]);
998 b.struct_(
999 "Outer",
1000 vec![field(
1001 "Box",
1002 generic_nominal("Box", vec![Type::int()]),
1003 true,
1004 )],
1005 vec![],
1006 );
1007 assert!(matches!(
1008 resolve_selector(&b.store, &nominal("Outer"), "weird"),
1009 Resolution::NotFound
1010 ));
1011 }
1012
1013 fn concrete_int_method() -> Type {
1016 Type::function(
1017 vec![generic_nominal("Box", vec![Type::int()])],
1018 vec![false],
1019 vec![],
1020 Box::new(Type::int()),
1021 )
1022 }
1023
1024 #[test]
1025 fn specialized_impl_does_not_promote_onto_other_instantiation() {
1026 let mut b = Builder::new();
1027 b.generic_struct(
1028 "Box",
1029 vec!["T"],
1030 vec![],
1031 vec![("only_int", concrete_int_method())],
1032 );
1033 b.struct_(
1034 "Outer",
1035 vec![field(
1036 "Box",
1037 generic_nominal("Box", vec![Type::string()]),
1038 true,
1039 )],
1040 vec![],
1041 );
1042 assert!(matches!(
1043 resolve_selector(&b.store, &nominal("Outer"), "only_int"),
1044 Resolution::NotFound
1045 ));
1046 }
1047
1048 #[test]
1049 fn specialized_impl_promotes_onto_matching_instantiation() {
1050 let mut b = Builder::new();
1051 b.generic_struct(
1052 "Box",
1053 vec!["T"],
1054 vec![],
1055 vec![("only_int", concrete_int_method())],
1056 );
1057 b.struct_(
1058 "Outer",
1059 vec![field(
1060 "Box",
1061 generic_nominal("Box", vec![Type::int()]),
1062 true,
1063 )],
1064 vec![],
1065 );
1066 let member = found(resolve_selector(&b.store, &nominal("Outer"), "only_int"));
1067 assert_eq!(member.depth, 1);
1068 assert_eq!(method_return(&member), Type::int());
1069 }
1070}