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