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
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::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 (TypeSpec::Generic(_), _) => Overlap::Yes,
244 (_, 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 (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 (TypeSpec::TraitSelf(_), TypeVar::Known(_, _, _)) => {
280 unreachable!("Trait self in impl target")
281 }
282 (TypeSpec::Wildcard(_), _) => unreachable!("Wildcard during type spec overlap check"),
283 }
284}