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
45pub 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 let impls = trait_impls.inner.get(&target).cloned().unwrap_or(vec![]);
74
75 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 Yes,
162 No,
163 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 (TypeSpec::Generic(_), _) => Overlap::Yes,
256 (_, 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 (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 (TypeSpec::TraitSelf(_), TypeVar::Known(_, _, _)) => {
292 unreachable!("Trait self in impl target")
293 }
294 (TypeSpec::Wildcard(_), _) => unreachable!("Wildcard during type spec overlap check"),
295 }
296}