1use crate::{
2 err,
3 etc::util::IntoPathSegments,
4 semantic::{
5 entry::GlobalCx,
6 logic::{term, util::var_name, ImplLogic},
7 },
8 ExprIn, TermIn, TriResult,
9};
10use logic_eval::{Expr, Name, Term, VAR_PREFIX};
11
12pub(crate) trait Host<'gcx> {
13 fn is_visible(&mut self, parent_path: &str, fn_ident: &str) -> TriResult<bool, ()>;
14}
15
16#[macro_export]
17macro_rules! impl_empty_method_host {
18 ($ty:ty) => {
19 impl<'gcx> $crate::semantic::logic::find_method::Host<'gcx> for $ty {
20 fn is_visible(
21 &mut self,
22 _parent_path: &str,
23 _fn_ident: &str,
24 ) -> $crate::TriResult<bool, ()> {
25 Ok(true)
26 }
27 }
28 };
29}
30
31pub(crate) struct MethodFinder<'a, 'gcx, H> {
32 gcx: &'gcx GlobalCx<'gcx>,
33 impl_logic: &'a mut ImplLogic<'gcx>,
34 host: &'a mut H,
35}
36
37impl<'a, 'gcx, H: Host<'gcx>> MethodFinder<'a, 'gcx, H>
38where
39 'gcx: 'a,
40 H: 'a,
41{
42 const CONCRETE_INTS: [&'static str; 12] = [
43 "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize",
44 ];
45 const CONCRETE_FLOATS: [&'static str; 2] = ["f32", "f64"];
46
47 pub(crate) fn new(
48 gcx: &'gcx GlobalCx<'gcx>,
49 impl_logic: &'a mut ImplLogic<'gcx>,
50 host: &'a mut H,
51 ) -> Self {
52 Self {
53 gcx,
54 impl_logic,
55 host,
56 }
57 }
58
59 pub(crate) fn find_method_sig(
65 &mut self,
66 parent_path: &str,
67 fn_ident: &str,
68 args: &mut [TermIn<'gcx>],
69 ) -> TriResult<(), ()> {
70 const PARENT_VAR: &str = "$P";
71 const FN_NAME_VAR: &str = "$F";
72 const _: () = assert!(VAR_PREFIX == '$');
73
74 let parent_var = Term {
76 functor: Name::with_intern(PARENT_VAR, self.gcx),
77 args: [].into(),
78 };
79 let n = parent_path.segments().count();
80 let parent = match n as u32 {
81 0 => unreachable!(),
82 1 => Term {
83 functor: Name::with_intern(parent_path, self.gcx),
84 args: [parent_var].into(),
85 },
86 2.. => {
87 let empty_arg = || term::arg_n([].into(), self.gcx);
88 let mut segments: Vec<TermIn<'gcx>> = parent_path
89 .segments()
90 .map(|segment| Term {
91 functor: Name::with_intern(segment, self.gcx),
92 args: [empty_arg()].into(),
93 })
94 .collect();
95 segments[n - 1].args = [parent_var].into();
96 term::list_n(segments, self.gcx)
97 }
98 };
99
100 let fn_name_var = Term {
102 functor: Name::with_intern(FN_NAME_VAR, self.gcx),
103 args: [].into(),
104 };
105 let fn_name = Term {
106 functor: Name::with_intern(fn_ident, self.gcx),
107 args: [fn_name_var].into(),
108 };
109
110 let num_args = args.len();
113 let var_args: Vec<TermIn<'gcx>> = (0..num_args)
114 .map(|i| {
115 if i == 1 {
117 args[1].clone()
118 } else {
119 Term {
120 functor: var_name(&i, self.gcx),
121 args: [].into(),
122 }
123 }
124 })
125 .collect();
126 let mut var_sig = term::sig_n(var_args, self.gcx);
127
128 if !self.host.is_visible(parent_path, fn_ident)? {
129 return err!(hard, "{parent_path}::{fn_ident} must be visible");
130 }
131
132 enum ReceiverState {
133 T, RefT, RefMutT, }
137
138 let mut recv_state = ReceiverState::T;
139
140 loop {
141 if self.resolve(parent.clone(), fn_name.clone(), var_sig.clone(), args) {
142 break;
143 }
144
145 match recv_state {
151 ReceiverState::T => {
153 let receiver = &mut var_sig.args[1];
154 *receiver = term::ref_1(receiver.clone(), self.gcx);
155 recv_state = ReceiverState::RefT;
156 }
157 ReceiverState::RefT => {
159 let receiver = &mut var_sig.args[1]; let t = &mut receiver.args[0]; *t = term::mut_1(t.clone(), self.gcx); recv_state = ReceiverState::RefMutT;
163 }
164
165 ReceiverState::RefMutT => {
177 return err!(
178 hard,
179 "could not find a method for {parent_path}::{fn_ident}"
180 );
181 }
182 }
183 }
184 Ok(())
185 }
186
187 fn resolve(
196 &mut self,
197 parent: TermIn<'gcx>,
198 fn_name: TermIn<'gcx>,
199 var_sig: TermIn<'gcx>,
200 args: &mut [TermIn<'gcx>],
201 ) -> bool {
202 let inher_fn = term::inher_fn_3(parent.clone(), fn_name.clone(), var_sig.clone(), self.gcx);
204 if Self::_resolve(self.impl_logic, Expr::Term(inher_fn), args, self.gcx) {
205 return true;
206 }
207
208 let assoc_fn = term::assoc_fn_3(parent, fn_name, var_sig.clone(), self.gcx);
210 Self::_resolve(self.impl_logic, Expr::Term(assoc_fn), args, self.gcx)
211 }
212
213 fn _resolve(
219 impl_logic: &mut ImplLogic<'gcx>,
220 query: ExprIn<'gcx>,
221 args: &mut [TermIn<'gcx>],
222 gcx: &'gcx GlobalCx<'gcx>,
223 ) -> bool {
224 let mut candidates = Vec::new();
226
227 let mut cx = impl_logic.query(query);
228 while let Some(eval) = cx.prove_next() {
229 let old_num_candidates = candidates.len();
230
231 for assign in eval {
232 let Ok(nth) = assign.get_lhs_variable()[1..].parse::<usize>() else {
235 continue;
236 };
237
238 let mut rhs = assign.rhs();
241 if nth != 1 && !Self::is_effective_same(&args[nth], &rhs) {
242 candidates.truncate(old_num_candidates);
243 break;
244 }
245
246 if nth != 1 {
249 if let Some(dst) =
250 Self::find_corresponding_to_var_with_coercion(&args[nth], &rhs)
251 {
252 rhs = dst.clone();
253 }
254 }
255
256 let pair = (nth, rhs);
257 if !candidates.contains(&pair) {
258 candidates.push(pair);
259 }
260 }
261 }
262
263 if candidates.is_empty() {
264 return false;
265 }
266
267 while let Some((nth, mut rhs)) = candidates.pop() {
268 let mut ambiguous_int = false;
269 let mut ambiguous_float = false;
270
271 for j in (0..candidates.len()).rev() {
272 if candidates[j].0 == nth {
273 if Self::is_similar_int(&rhs, &candidates[j].1) {
274 ambiguous_int = true;
275 } else if Self::is_similar_float(&rhs, &candidates[j].1) {
276 ambiguous_float = true;
277 } else {
278 panic!("something went wrong: {}, {}", rhs, candidates[j].1);
279 }
280 candidates.swap_remove(j);
281 }
282 }
283
284 assert!(!(ambiguous_int && ambiguous_float));
285
286 if ambiguous_int {
287 Self::replace_concrete_int(&mut rhs, nth, gcx);
288 } else if ambiguous_float {
289 Self::replace_concrete_float(&mut rhs, nth, gcx);
290 }
291
292 debug_assert!(Self::num_containing_vars(&args[nth]) <= 1);
293
294 args[nth].replace_all(&mut |term| {
295 if term.functor.starts_with(VAR_PREFIX)
296 && term.functor[1..].parse::<usize>() == Ok(nth)
297 {
298 Some(rhs.clone())
299 } else {
300 None
301 }
302 });
303 }
304 true
305 }
306
307 fn find_corresponding_to_var_with_coercion<'r>(
317 l: &TermIn<'gcx>,
318 r: &'r TermIn<'gcx>,
319 ) -> Option<&'r TermIn<'gcx>> {
320 if let Some(matching_term) = Self::find_var_corresponding(l, r) {
321 return Some(matching_term);
322 }
323
324 if l.functor.as_ref() == term::FUNCTOR_REF {
328 if l.args[0].functor.as_ref() == term::FUNCTOR_MUT {
329 let mut cloned = l.clone(); let mut_ = &cloned.args[0]; cloned.args[0] = mut_.args[0].clone(); Self::find_corresponding_to_var_with_coercion(&cloned, r)
334 } else {
335 None
337 }
338 } else {
339 None
341 }
342 }
343
344 fn find_var_corresponding<'r>(
351 l: &TermIn<'gcx>,
352 r: &'r TermIn<'gcx>,
353 ) -> Option<&'r TermIn<'gcx>> {
354 if !Self::is_effective_same(l, r) {
355 None
356 } else if l.is_variable() {
357 Some(r)
358 } else {
359 l.args
360 .iter()
361 .zip(&r.args)
362 .find_map(|(la, ra)| Self::find_var_corresponding(la, ra))
363 }
364 }
365
366 fn is_effective_same(l: &TermIn<'gcx>, r: &TermIn<'gcx>) -> bool {
374 if l.is_variable() {
375 return true;
376 }
377 l.functor == r.functor
378 && l.args.len() == r.args.len()
379 && l.args
380 .iter()
381 .zip(&r.args)
382 .all(|(la, ra)| Self::is_effective_same(la, ra))
383 }
384
385 fn is_similar_int(l: &TermIn<'gcx>, r: &TermIn<'gcx>) -> bool {
390 if Self::CONCRETE_INTS.contains(&l.functor.as_ref())
391 && Self::CONCRETE_INTS.contains(&r.functor.as_ref())
392 {
393 return true;
394 }
395
396 l.functor == r.functor
397 && l.args.len() == r.args.len()
398 && l.args
399 .iter()
400 .zip(&r.args)
401 .any(|(la, ra)| Self::is_similar_int(la, ra))
402 }
403
404 fn is_similar_float(l: &TermIn<'gcx>, r: &TermIn<'gcx>) -> bool {
409 if Self::CONCRETE_FLOATS.contains(&l.functor.as_ref())
410 && Self::CONCRETE_FLOATS.contains(&r.functor.as_ref())
411 {
412 return true;
413 }
414
415 l.functor == r.functor
416 && l.args.len() == r.args.len()
417 && l.args
418 .iter()
419 .zip(&r.args)
420 .any(|(la, ra)| Self::is_similar_float(la, ra))
421 }
422
423 fn replace_concrete_int(term: &mut TermIn<'gcx>, var_ident: usize, gcx: &'gcx GlobalCx<'gcx>) {
427 if Self::CONCRETE_INTS.contains(&term.functor.as_ref()) {
428 term.functor = var_name(&var_ident, gcx);
429 }
430
431 for arg in &mut term.args {
432 Self::replace_concrete_int(arg, var_ident, gcx);
433 }
434 }
435
436 fn replace_concrete_float(
440 term: &mut TermIn<'gcx>,
441 var_ident: usize,
442 gcx: &'gcx GlobalCx<'gcx>,
443 ) {
444 if Self::CONCRETE_FLOATS.contains(&term.functor.as_ref()) {
445 term.functor = var_name(&var_ident, gcx);
446 }
447
448 for arg in &mut term.args {
449 Self::replace_concrete_float(arg, var_ident, gcx);
450 }
451 }
452
453 fn num_containing_vars(term: &TermIn<'gcx>) -> usize {
455 if term.is_variable() {
456 return 1;
457 }
458 term.args
459 .iter()
460 .map(Self::num_containing_vars)
461 .sum::<usize>()
462 }
463}