spade_typeinference/
method_resolution.rs

1use itertools::Itertools;
2use spade_common::location_info::{Loc, WithLocation};
3use spade_common::name::{Identifier, NameID};
4
5use spade_diagnostics::Diagnostic;
6use spade_hir::{ImplTarget, TypeExpression, TypeSpec};
7use spade_types::KnownType;
8
9use crate::equation::{TypeVar, TypeVarID};
10use crate::traits::{TraitImpl, TraitImplList};
11use crate::TypeState;
12
13#[derive(Debug, PartialEq, Eq)]
14pub enum FunctionLikeName {
15    Method(Identifier),
16    Free(NameID),
17}
18impl std::fmt::Display for FunctionLikeName {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            FunctionLikeName::Method(n) => write!(f, "{n}"),
22            FunctionLikeName::Free(n) => write!(f, "{n}"),
23        }
24    }
25}
26
27pub trait IntoImplTarget {
28    fn into_impl_target(&self) -> Option<ImplTarget>;
29}
30
31impl IntoImplTarget for KnownType {
32    fn into_impl_target(&self) -> Option<ImplTarget> {
33        match self {
34            KnownType::Error => None,
35            KnownType::Named(name) => Some(ImplTarget::Named(name.clone())),
36            KnownType::Integer(_) | KnownType::Bool(_) => None,
37            KnownType::Tuple => None,
38            KnownType::Array => Some(ImplTarget::Array),
39            KnownType::Wire => Some(ImplTarget::Wire),
40            KnownType::Inverted => Some(ImplTarget::Inverted),
41        }
42    }
43}
44
45/// Attempts to look up which function to call when calling `method` on a var
46/// of type `self_type`.
47/// Returns the method to call if it is fully known and exists, an error if there is
48/// no such method, or None if the method is ambiguous
49pub fn select_method(
50    expr: Loc<()>,
51    self_type: &TypeVarID,
52    method: &Loc<Identifier>,
53    trait_impls: &TraitImplList,
54    type_state: &TypeState,
55) -> Result<Option<Loc<NameID>>, Diagnostic> {
56    let target = self_type
57        .resolve(type_state)
58        .expect_known::<_, _, _, Option<ImplTarget>>(
59            |ktype, _params| ktype.into_impl_target(),
60            || None,
61        )
62        .ok_or_else(|| {
63            Diagnostic::error(
64                expr,
65                format!(
66                    "{self_type} does not have any methods",
67                    self_type = self_type.display_with_meta(false, type_state)
68                ),
69            )
70        })?;
71
72    // Go to the item list to check if this name has any methods
73    let impls = trait_impls.inner.get(&target).cloned().unwrap_or(vec![]);
74
75    // Gather all the candidate methods which we may want to call.
76    let (matched_candidates, maybe_candidates, unmatched_candidates): (Vec<_>, Vec<_>, Vec<_>) =
77        impls
78            .iter()
79            .flat_map(
80                |TraitImpl {
81                     name: _,
82                     target_type_params: _,
83                     trait_type_params: _,
84                     impl_block: r#impl,
85                 }| {
86                    r#impl.fns.iter().map(move |(fn_name, actual_fn)| {
87                        if fn_name == &method.inner {
88                            let is_overlapping =
89                                spec_is_overlapping(&r#impl.target, self_type, type_state);
90                            let selected = actual_fn.0.clone().at_loc(&actual_fn.1);
91                            match is_overlapping {
92                                Overlap::Yes => (Some(selected), None, None),
93                                Overlap::Maybe => (None, Some(selected), None),
94                                Overlap::No => (None, None, Some(&r#impl.target)),
95                            }
96                        } else {
97                            (None, None, None)
98                        }
99                    })
100                },
101            )
102            .multiunzip();
103
104    let matched_candidates = matched_candidates
105        .into_iter()
106        .filter_map(|x| x)
107        .collect::<Vec<_>>();
108    let maybe_candidates = maybe_candidates
109        .into_iter()
110        .filter_map(|x| x)
111        .collect::<Vec<_>>();
112    let unmatched_candidates = unmatched_candidates
113        .into_iter()
114        .filter_map(|x| x)
115        .collect::<Vec<_>>();
116
117    if !maybe_candidates.is_empty() {
118        return Ok(None);
119    }
120
121    let final_method = match matched_candidates.as_slice() {
122        [name] => name,
123        [] => {
124            let self_type = self_type.display_with_meta(false, type_state);
125            let mut d =
126                Diagnostic::error(method, format!("`{self_type}` has no method `{method}`"))
127                    .primary_label("No such method")
128                    .secondary_label(expr, format!("This has type `{self_type}`"));
129
130            match unmatched_candidates.as_slice() {
131                [] => {}
132                [one] => {
133                    d.add_help(format!("The method exists for `{one}`"));
134                }
135                multiple => {
136                    d.add_help(format!(
137                        "The method exists for `{}`, and `{}`",
138                        multiple[0..multiple.len() - 1]
139                            .iter()
140                            .map(|t| format!("`{t}`"))
141                            .join(", "),
142                        multiple.last().unwrap()
143                    ));
144                }
145            };
146            return Err(d);
147        }
148        _ => {
149            return Err(Diagnostic::bug(
150                method,
151                "Multiple candidates satisfy this method",
152            ))
153        }
154    };
155
156    Ok(Some(final_method.clone()))
157}
158
159enum Overlap {
160    /// We know for sure if there is overlap
161    Yes,
162    No,
163    /// We Are not sure yet if there is overlap. This happens if we have X<_>
164    /// satisfies but X<T> where T is concrete
165    Maybe,
166}
167
168trait IterExt {
169    fn all_overlap(self) -> Overlap;
170}
171
172impl<T> IterExt for T
173where
174    T: Iterator<Item = Overlap>,
175{
176    fn all_overlap(self) -> Overlap {
177        for o in self {
178            match o {
179                Overlap::Yes => {}
180                Overlap::No => return Overlap::No,
181                Overlap::Maybe => return Overlap::Maybe,
182            }
183        }
184        Overlap::Yes
185    }
186}
187
188fn specs_are_overlapping(
189    specs: &[Loc<TypeSpec>],
190    vars: &[TypeVarID],
191    type_state: &TypeState,
192) -> Overlap {
193    if specs.len() == vars.len() {
194        specs
195            .iter()
196            .zip(vars)
197            .map(|(s, v)| spec_is_overlapping(s, v, type_state))
198            .all_overlap()
199    } else {
200        Overlap::No
201    }
202}
203
204fn expr_is_overlapping(expr: &TypeExpression, var: &TypeVarID, type_state: &TypeState) -> Overlap {
205    match (&expr, var.resolve(type_state)) {
206        (TypeExpression::Integer(eval), TypeVar::Known(_, KnownType::Integer(vval), _)) => {
207            if eval == vval {
208                Overlap::Yes
209            } else {
210                Overlap::No
211            }
212        }
213        (TypeExpression::Integer(_), TypeVar::Unknown(_, _, _, _)) => Overlap::Maybe,
214        (TypeExpression::Integer(_), _) => {
215            unreachable!("Non integer and non-generic type matched with integer")
216        }
217        (TypeExpression::TypeSpec(s), _v) => spec_is_overlapping(s, var, type_state),
218        (TypeExpression::ConstGeneric(_), _) => {
219            unreachable!("Const generic in expr_is_overlapping")
220        }
221    }
222}
223
224fn exprs_are_overlapping(
225    exprs: &[Loc<TypeExpression>],
226    vars: &[TypeVarID],
227    type_state: &TypeState,
228) -> Overlap {
229    if exprs.len() == vars.len() {
230        exprs
231            .iter()
232            .zip(vars)
233            .map(|(e, v)| expr_is_overlapping(e, v, type_state))
234            .all_overlap()
235    } else {
236        Overlap::No
237    }
238}
239
240fn spec_is_overlapping(spec: &TypeSpec, var: &TypeVarID, type_state: &TypeState) -> Overlap {
241    match (spec, var.resolve(type_state)) {
242        // Generics overlap with anything
243        (TypeSpec::Generic(_), _) => Overlap::Yes,
244        // For anything non-generic, we need a concrete type to know if there is overlap.
245        (_, TypeVar::Unknown(_, _, _, _)) => Overlap::Maybe,
246        (
247            TypeSpec::Declared(specname, specparams),
248            TypeVar::Known(_, KnownType::Named(varname), varparams),
249        ) => {
250            if &specname.inner == varname {
251                exprs_are_overlapping(specparams, varparams, type_state)
252            } else {
253                Overlap::No
254            }
255        }
256        (TypeSpec::Declared(_, _), _) => Overlap::No,
257
258        // NOTE: This block currently cannot be tested because we can't add methods to
259        // anything other than declared types
260        (TypeSpec::Tuple(sspecs), TypeVar::Known(_, KnownType::Tuple, vvars)) => {
261            specs_are_overlapping(sspecs, vvars, type_state)
262        }
263        (TypeSpec::Tuple(_), _) => Overlap::No,
264        (TypeSpec::Array { inner, size }, TypeVar::Known(_, KnownType::Array, vvars)) => [
265            spec_is_overlapping(inner, &vvars[0], type_state),
266            expr_is_overlapping(size, &vvars[1], type_state),
267        ]
268        .into_iter()
269        .all_overlap(),
270        (TypeSpec::Array { .. }, _) => Overlap::No,
271        (TypeSpec::Inverted(sinner), TypeVar::Known(_, KnownType::Inverted, vinner))
272        | (TypeSpec::Wire(sinner), TypeVar::Known(_, KnownType::Wire, vinner)) => {
273            spec_is_overlapping(sinner, &vinner[0], type_state)
274        }
275        (TypeSpec::Inverted(_), _) => Overlap::No,
276        (TypeSpec::Wire(_), _) => Overlap::No,
277
278        // TraitSelf cannot appear as the impl target, so what we do here is irrelevant
279        (TypeSpec::TraitSelf(_), TypeVar::Known(_, _, _)) => {
280            unreachable!("Trait self in impl target")
281        }
282        (TypeSpec::Wildcard(_), _) => unreachable!("Wildcard during type spec overlap check"),
283    }
284}