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(_) | KnownType::String(_) => 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::String(eval), TypeVar::Known(_, KnownType::String(vval), _)) => {
214            if eval == &vval {
215                Overlap::Yes
216            } else {
217                Overlap::No
218            }
219        }
220        (TypeExpression::Integer(_) | TypeExpression::String(_), TypeVar::Unknown(_, _, _, _)) => {
221            Overlap::Maybe
222        }
223        (TypeExpression::Integer(_), _) => {
224            unreachable!("Non integer and non-generic type matched with integer")
225        }
226        (TypeExpression::String(_), _) => {
227            unreachable!("Non string and non-generic type matched with string")
228        }
229        (TypeExpression::TypeSpec(s), _v) => spec_is_overlapping(s, var, type_state),
230        (TypeExpression::ConstGeneric(_), _) => {
231            unreachable!("Const generic in expr_is_overlapping")
232        }
233    }
234}
235
236fn exprs_are_overlapping(
237    exprs: &[Loc<TypeExpression>],
238    vars: &[TypeVarID],
239    type_state: &TypeState,
240) -> Overlap {
241    if exprs.len() == vars.len() {
242        exprs
243            .iter()
244            .zip(vars)
245            .map(|(e, v)| expr_is_overlapping(e, v, type_state))
246            .all_overlap()
247    } else {
248        Overlap::No
249    }
250}
251
252fn spec_is_overlapping(spec: &TypeSpec, var: &TypeVarID, type_state: &TypeState) -> Overlap {
253    match (spec, var.resolve(type_state)) {
254        // Generics overlap with anything
255        (TypeSpec::Generic(_), _) => Overlap::Yes,
256        // For anything non-generic, we need a concrete type to know if there is overlap.
257        (_, TypeVar::Unknown(_, _, _, _)) => Overlap::Maybe,
258        (
259            TypeSpec::Declared(specname, specparams),
260            TypeVar::Known(_, KnownType::Named(varname), varparams),
261        ) => {
262            if specname.inner == varname {
263                exprs_are_overlapping(specparams, &varparams, type_state)
264            } else {
265                Overlap::No
266            }
267        }
268        (TypeSpec::Declared(_, _), _) => Overlap::No,
269
270        // NOTE: This block currently cannot be tested because we can't add methods to
271        // anything other than declared types
272        (TypeSpec::Tuple(sspecs), TypeVar::Known(_, KnownType::Tuple, vvars)) => {
273            specs_are_overlapping(sspecs, &vvars, type_state)
274        }
275        (TypeSpec::Tuple(_), _) => Overlap::No,
276        (TypeSpec::Array { inner, size }, TypeVar::Known(_, KnownType::Array, vvars)) => [
277            spec_is_overlapping(inner, &vvars[0], type_state),
278            expr_is_overlapping(size, &vvars[1], type_state),
279        ]
280        .into_iter()
281        .all_overlap(),
282        (TypeSpec::Array { .. }, _) => Overlap::No,
283        (TypeSpec::Inverted(sinner), TypeVar::Known(_, KnownType::Inverted, vinner))
284        | (TypeSpec::Wire(sinner), TypeVar::Known(_, KnownType::Wire, vinner)) => {
285            spec_is_overlapping(sinner, &vinner[0], type_state)
286        }
287        (TypeSpec::Inverted(_), _) => Overlap::No,
288        (TypeSpec::Wire(_), _) => Overlap::No,
289
290        // TraitSelf cannot appear as the impl target, so what we do here is irrelevant
291        (TypeSpec::TraitSelf(_), TypeVar::Known(_, _, _)) => {
292            unreachable!("Trait self in impl target")
293        }
294        (TypeSpec::Wildcard(_), _) => unreachable!("Wildcard during type spec overlap check"),
295    }
296}