Skip to main content

lisette_semantics/
promotion.rs

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    /// `ty` already carries the effective receiver: value embeds keep the
18    /// declared receiver, promoted methods are re-pointed at the embedder.
19    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    /// A pointer edge was crossed on the path to this subobject.
79    indirect: bool,
80    /// Reached by more than one path at this depth, so its members collide.
81    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 &current {
99            // Seen at a shallower depth: shadows here, and breaks cycles.
100            if !seen.insert(type_key(&entry.ty)) {
101                continue;
102            }
103            visited.push(entry.clone());
104
105            // Interfaces contribute their method set but have no fields to descend.
106            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
144/// Resolve `name` to its shallowest candidate; a lone non-`multiples` hit wins,
145/// anything else is ambiguous.
146fn 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
186/// The field or method a type declares under `name`. A method shadows a
187/// same-named field, as gc checks attached methods first.
188fn 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            // Only promoted methods are re-pointed; a depth-0 receiver is already
233            // the outer type, and rewriting it would break generics. A promoted
234            // method stays pointer-only when its receiver is a pointer and no
235            // pointer edge was crossed.
236            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
257/// Every field and method name a type exposes.
258fn 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
280/// Build an entry for `target` if it resolves (through aliases) to a nominal type.
281fn 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
302/// gc's `consolidateMultiples`: dedup by type, flagging any reached by more than
303/// one path so its members resolve as ambiguous.
304fn 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
319/// Type identity for `seen`/`multiples`: qualified id plus any type arguments.
320fn 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
331/// Strip one `Ref`, reporting whether it was present (a pointer edge).
332fn 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    // Anything else is a specialized impl (`impl Container<int>`): it promotes
413    // only when its concrete receiver is exactly this instantiation, so a method
414    // declared for one instantiation never leaks onto another through promotion.
415    let receiver = method_receiver(method_ty)?;
416    (receiver.strip_refs() == *container).then(|| method_ty.clone())
417}
418
419/// The receiver (first parameter) of a method type, peeling any `Forall`.
420fn 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
431/// The method's receiver is `Container<v0, .., vk>` with the impl vars bare and
432/// in order, i.e. an unspecialized `impl<v..> Container<v..>`.
433fn 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    /// An interface method as stored after registration: receiver already stripped.
487    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        // struct param is `T`, but the method's impl var is `V` (`impl<V> Box<V>`).
959        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    /// A concrete `impl Box<int> { fn only_int(self: Box<int>) -> int }`, stored
1014    /// without a `Forall` because it binds no type variables.
1015    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}