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;
10use crate::traits::{TraitImpl, TraitImplList};
11
12#[derive(Debug, PartialEq, Eq)]
13pub enum FunctionLikeName {
14 Method(Identifier),
15 Free(NameID),
16}
17impl std::fmt::Display for FunctionLikeName {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 FunctionLikeName::Method(n) => write!(f, "{n}"),
21 FunctionLikeName::Free(n) => write!(f, "{n}"),
22 }
23 }
24}
25
26pub trait IntoImplTarget {
27 fn into_impl_target(&self) -> Option<ImplTarget>;
28}
29
30impl IntoImplTarget for KnownType {
31 fn into_impl_target(&self) -> Option<ImplTarget> {
32 match self {
33 KnownType::Named(name) => Some(ImplTarget::Named(name.clone())),
34 KnownType::Integer(_) | KnownType::Bool(_) => None,
35 KnownType::Tuple => None,
36 KnownType::Array => Some(ImplTarget::Array),
37 KnownType::Wire => Some(ImplTarget::Wire),
38 KnownType::Inverted => Some(ImplTarget::Inverted),
39 }
40 }
41}
42
43pub fn select_method(
48 expr: Loc<()>,
49 self_type: &TypeVar,
50 method: &Loc<Identifier>,
51 trait_impls: &TraitImplList,
52) -> Result<Option<Loc<NameID>>, Diagnostic> {
53 let target = self_type
54 .expect_known::<_, _, _, Option<ImplTarget>>(
55 |ktype, _params| ktype.into_impl_target(),
56 || None,
57 )
58 .ok_or_else(|| Diagnostic::error(expr, format!("{self_type} does not have any methods")))?;
59
60 let impls = trait_impls.inner.get(&target).cloned().unwrap_or(vec![]);
62
63 let (matched_candidates, maybe_candidates, unmatched_candidates): (Vec<_>, Vec<_>, Vec<_>) =
65 impls
66 .iter()
67 .flat_map(
68 |TraitImpl {
69 name: _,
70 type_params: _,
71 impl_block: r#impl,
72 }| {
73 r#impl.fns.iter().map(move |(fn_name, actual_fn)| {
74 if fn_name == &method.inner {
75 let is_overlapping = spec_is_overlapping(&r#impl.target, self_type);
76 let selected = actual_fn.0.clone().at_loc(&actual_fn.1);
77 match is_overlapping {
78 Overlap::Yes => (Some(selected), None, None),
79 Overlap::Maybe => (None, Some(selected), None),
80 Overlap::No => (None, None, Some(&r#impl.target)),
81 }
82 } else {
83 (None, None, None)
84 }
85 })
86 },
87 )
88 .multiunzip();
89
90 let matched_candidates = matched_candidates
91 .into_iter()
92 .filter_map(|x| x)
93 .collect::<Vec<_>>();
94 let maybe_candidates = maybe_candidates
95 .into_iter()
96 .filter_map(|x| x)
97 .collect::<Vec<_>>();
98 let unmatched_candidates = unmatched_candidates
99 .into_iter()
100 .filter_map(|x| x)
101 .collect::<Vec<_>>();
102
103 if !maybe_candidates.is_empty() {
104 return Ok(None);
105 }
106
107 let final_method = match matched_candidates.as_slice() {
108 [name] => name,
109 [] => {
110 let mut d =
111 Diagnostic::error(method, format!("`{self_type}` has no method `{method}`"))
112 .primary_label("No such method")
113 .secondary_label(expr, format!("This has type `{self_type}`"));
114
115 match unmatched_candidates.as_slice() {
116 [] => {}
117 [one] => {
118 d.add_help(format!("The method exists for `{one}`"));
119 }
120 multiple => {
121 d.add_help(format!(
122 "The method exists for `{}`, and `{}`",
123 multiple[0..multiple.len() - 1]
124 .iter()
125 .map(|t| format!("`{t}`"))
126 .join(", "),
127 multiple.last().unwrap()
128 ));
129 }
130 };
131 return Err(d);
132 }
133 _ => {
134 return Err(Diagnostic::bug(
135 method,
136 "Multiple candidates satisfy this method",
137 ))
138 }
139 };
140
141 Ok(Some(final_method.clone()))
142}
143
144enum Overlap {
145 Yes,
147 No,
148 Maybe,
151}
152
153trait IterExt {
154 fn all_overlap(self) -> Overlap;
155}
156
157impl<T> IterExt for T
158where
159 T: Iterator<Item = Overlap>,
160{
161 fn all_overlap(self) -> Overlap {
162 for o in self {
163 match o {
164 Overlap::Yes => {}
165 Overlap::No => return Overlap::No,
166 Overlap::Maybe => return Overlap::Maybe,
167 }
168 }
169 Overlap::Yes
170 }
171}
172
173fn specs_are_overlapping(specs: &[Loc<TypeSpec>], vars: &[TypeVar]) -> Overlap {
174 if specs.len() == vars.len() {
175 specs
176 .iter()
177 .zip(vars)
178 .map(|(s, v)| spec_is_overlapping(s, v))
179 .all_overlap()
180 } else {
181 Overlap::No
182 }
183}
184
185fn expr_is_overlapping(expr: &TypeExpression, var: &TypeVar) -> Overlap {
186 match (&expr, var) {
187 (TypeExpression::Integer(eval), TypeVar::Known(_, KnownType::Integer(vval), _)) => {
188 if eval == vval {
189 Overlap::Yes
190 } else {
191 Overlap::No
192 }
193 }
194 (TypeExpression::Integer(_), TypeVar::Unknown(_, _, _, _)) => Overlap::Maybe,
195 (TypeExpression::Integer(_), _) => {
196 unreachable!("Non integer and non-generic type matched with integer")
197 }
198 (TypeExpression::TypeSpec(s), v) => spec_is_overlapping(s, v),
199 (TypeExpression::ConstGeneric(_), _) => {
200 unreachable!("Const generic in expr_is_overlapping")
201 }
202 }
203}
204
205fn exprs_are_overlapping(exprs: &[Loc<TypeExpression>], vars: &[TypeVar]) -> Overlap {
206 if exprs.len() == vars.len() {
207 exprs
208 .iter()
209 .zip(vars)
210 .map(|(e, v)| expr_is_overlapping(e, v))
211 .all_overlap()
212 } else {
213 Overlap::No
214 }
215}
216
217fn spec_is_overlapping(spec: &TypeSpec, var: &TypeVar) -> Overlap {
218 match (spec, var) {
219 (TypeSpec::Generic(_), _) => Overlap::Yes,
221 (_, TypeVar::Unknown(_, _, _, _)) => Overlap::Maybe,
223 (
224 TypeSpec::Declared(specname, specparams),
225 TypeVar::Known(_, KnownType::Named(varname), varparams),
226 ) => {
227 if &specname.inner == varname {
228 exprs_are_overlapping(specparams, varparams)
229 } else {
230 Overlap::No
231 }
232 }
233 (TypeSpec::Declared(_, _), _) => Overlap::No,
234
235 (TypeSpec::Tuple(sspecs), TypeVar::Known(_, KnownType::Tuple, vvars)) => {
238 specs_are_overlapping(sspecs, vvars)
239 }
240 (TypeSpec::Tuple(_), _) => Overlap::No,
241 (TypeSpec::Array { inner, size }, TypeVar::Known(_, KnownType::Array, vvars)) => [
242 spec_is_overlapping(inner, &vvars[0]),
243 expr_is_overlapping(size, &vvars[1]),
244 ]
245 .into_iter()
246 .all_overlap(),
247 (TypeSpec::Array { .. }, _) => Overlap::No,
248 (TypeSpec::Inverted(sinner), TypeVar::Known(_, KnownType::Inverted, vinner))
249 | (TypeSpec::Wire(sinner), TypeVar::Known(_, KnownType::Wire, vinner)) => {
250 spec_is_overlapping(sinner, &vinner[0])
251 }
252 (TypeSpec::Inverted(_), _) => Overlap::No,
253 (TypeSpec::Wire(_), _) => Overlap::No,
254
255 (TypeSpec::TraitSelf(_), TypeVar::Known(_, _, _)) => {
257 unreachable!("Trait self in impl target")
258 }
259 (TypeSpec::Wildcard(_), _) => unreachable!("Wildcard during type spec overlap check"),
260 }
261}