1use super::AndAc;
2use crate::{
3 env::Env,
4 expr::{
5 print::{PrettyBuf, PrettyDisplay},
6 ModPath,
7 },
8 typ::{contains::ContainsFlags, AbstractId, RefHist, TVar, Type},
9 LambdaId,
10};
11use ahash::{AHashMap, AHashSet};
12use anyhow::{bail, Context, Result};
13use arcstr::ArcStr;
14use enumflags2::BitFlags;
15use nohash::{IntMap, IntSet};
16use parking_lot::RwLock;
17use poolshark::local::LPooled;
18use smallvec::{smallvec, SmallVec};
19use std::{
20 cmp::{Eq, Ordering, PartialEq},
21 fmt::{self, Debug, Write},
22};
23use triomphe::Arc;
24
25#[derive(Debug, Clone)]
33pub enum FnArgKind {
34 Positional { name: Option<ArcStr> },
35 Labeled { name: ArcStr, has_default: bool },
36}
37
38impl FnArgKind {
39 pub fn name(&self) -> Option<&ArcStr> {
40 match self {
41 FnArgKind::Positional { name } => name.as_ref(),
42 FnArgKind::Labeled { name, .. } => Some(name),
43 }
44 }
45
46 pub fn label(&self) -> Option<&ArcStr> {
47 match self {
48 FnArgKind::Labeled { name, .. } => Some(name),
49 FnArgKind::Positional { .. } => None,
50 }
51 }
52
53 pub fn is_labeled(&self) -> bool {
54 matches!(self, FnArgKind::Labeled { .. })
55 }
56
57 pub fn is_positional(&self) -> bool {
58 matches!(self, FnArgKind::Positional { .. })
59 }
60
61 pub fn has_default(&self) -> bool {
62 matches!(self, FnArgKind::Labeled { has_default: true, .. })
63 }
64}
65
66impl PartialEq for FnArgKind {
71 fn eq(&self, other: &Self) -> bool {
72 match (self, other) {
73 (FnArgKind::Positional { .. }, FnArgKind::Positional { .. }) => true,
74 (
75 FnArgKind::Labeled { name: n0, has_default: d0 },
76 FnArgKind::Labeled { name: n1, has_default: d1 },
77 ) => n0 == n1 && d0 == d1,
78 _ => false,
79 }
80 }
81}
82
83impl Eq for FnArgKind {}
84
85impl PartialOrd for FnArgKind {
86 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
87 Some(self.cmp(other))
88 }
89}
90
91impl Ord for FnArgKind {
92 fn cmp(&self, other: &Self) -> Ordering {
93 match (self, other) {
94 (FnArgKind::Positional { .. }, FnArgKind::Positional { .. }) => {
95 Ordering::Equal
96 }
97 (FnArgKind::Positional { .. }, FnArgKind::Labeled { .. }) => Ordering::Less,
98 (FnArgKind::Labeled { .. }, FnArgKind::Positional { .. }) => {
99 Ordering::Greater
100 }
101 (
102 FnArgKind::Labeled { name: n0, has_default: d0 },
103 FnArgKind::Labeled { name: n1, has_default: d1 },
104 ) => n0.cmp(n1).then_with(|| d0.cmp(d1)),
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
110pub struct FnArgType {
111 pub kind: FnArgKind,
112 pub typ: Type,
113}
114
115impl FnArgType {
116 pub fn name(&self) -> Option<&ArcStr> {
117 self.kind.name()
118 }
119
120 pub fn label(&self) -> Option<&ArcStr> {
121 self.kind.label()
122 }
123
124 pub fn is_labeled(&self) -> bool {
125 self.kind.is_labeled()
126 }
127
128 pub fn is_positional(&self) -> bool {
129 self.kind.is_positional()
130 }
131
132 pub fn has_default(&self) -> bool {
133 self.kind.has_default()
134 }
135}
136
137impl PartialEq for FnArgType {
138 fn eq(&self, other: &Self) -> bool {
139 self.kind == other.kind && self.typ == other.typ
140 }
141}
142
143impl Eq for FnArgType {}
144
145impl PartialOrd for FnArgType {
146 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
147 Some(self.cmp(other))
148 }
149}
150
151impl Ord for FnArgType {
152 fn cmp(&self, other: &Self) -> Ordering {
153 self.kind.cmp(&other.kind).then_with(|| self.typ.cmp(&other.typ))
154 }
155}
156
157#[derive(Debug, Clone)]
158pub struct FnType {
159 pub args: Arc<[FnArgType]>,
160 pub vargs: Option<Type>,
161 pub rtype: Type,
162 pub constraints: Arc<RwLock<LPooled<Vec<(TVar, Type)>>>>,
163 pub throws: Type,
164 pub explicit_throws: bool,
165 pub lambda_ids: Arc<RwLock<IntSet<LambdaId>>>,
167}
168
169impl PartialEq for FnType {
170 fn eq(&self, other: &Self) -> bool {
171 let Self {
172 args: args0,
173 vargs: vargs0,
174 rtype: rtype0,
175 constraints: constraints0,
176 throws: th0,
177 explicit_throws: _,
178 lambda_ids: _,
179 } = self;
180 let Self {
181 args: args1,
182 vargs: vargs1,
183 rtype: rtype1,
184 constraints: constraints1,
185 throws: th1,
186 explicit_throws: _,
187 lambda_ids: _,
188 } = other;
189 args0 == args1
190 && vargs0 == vargs1
191 && rtype0 == rtype1
192 && &*constraints0.read() == &*constraints1.read()
193 && th0 == th1
194 }
195}
196
197impl Eq for FnType {}
198
199impl PartialOrd for FnType {
200 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
201 use std::cmp::Ordering;
202 let Self {
203 args: args0,
204 vargs: vargs0,
205 rtype: rtype0,
206 constraints: constraints0,
207 throws: th0,
208 explicit_throws: _,
209 lambda_ids: _,
210 } = self;
211 let Self {
212 args: args1,
213 vargs: vargs1,
214 rtype: rtype1,
215 constraints: constraints1,
216 throws: th1,
217 explicit_throws: _,
218 lambda_ids: _,
219 } = other;
220 match args0.partial_cmp(&args1) {
221 Some(Ordering::Equal) => match vargs0.partial_cmp(vargs1) {
222 Some(Ordering::Equal) => match rtype0.partial_cmp(rtype1) {
223 Some(Ordering::Equal) => {
224 match constraints0.read().partial_cmp(&*constraints1.read()) {
225 Some(Ordering::Equal) => th0.partial_cmp(th1),
226 r => r,
227 }
228 }
229 r => r,
230 },
231 r => r,
232 },
233 r => r,
234 }
235 }
236}
237
238impl Ord for FnType {
239 fn cmp(&self, other: &Self) -> Ordering {
240 self.partial_cmp(other).unwrap()
241 }
242}
243
244impl Default for FnType {
245 fn default() -> Self {
246 Self {
247 args: Arc::from_iter([]),
248 vargs: None,
249 rtype: Default::default(),
250 constraints: Arc::new(RwLock::new(LPooled::take())),
251 throws: Default::default(),
252 explicit_throws: false,
253 lambda_ids: Arc::new(RwLock::new(IntSet::default())),
254 }
255 }
256}
257
258impl FnType {
259 pub(super) fn normalize(&self) -> Self {
260 let Self { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids } =
261 self;
262 let args = Arc::from_iter(
263 args.iter()
264 .map(|a| FnArgType { kind: a.kind.clone(), typ: a.typ.normalize() }),
265 );
266 let vargs = vargs.as_ref().map(|t| t.normalize());
267 let rtype = rtype.normalize();
268 let constraints = Arc::new(RwLock::new(
269 constraints
270 .read()
271 .iter()
272 .map(|(tv, t)| (tv.clone(), t.normalize()))
273 .collect(),
274 ));
275 let throws = throws.normalize();
276 let explicit_throws = *explicit_throws;
277 let lambda_ids = lambda_ids.clone();
278 FnType { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
279 }
280
281 pub fn resolve_tvars(&self) -> Self {
284 let Self {
285 args,
286 vargs,
287 rtype,
288 constraints: _,
289 throws,
290 explicit_throws,
291 lambda_ids,
292 } = self;
293 let args = Arc::from_iter(
294 args.iter()
295 .map(|a| FnArgType { kind: a.kind.clone(), typ: a.typ.resolve_tvars() }),
296 );
297 let vargs = vargs.as_ref().map(|t| t.resolve_tvars());
298 let rtype = rtype.resolve_tvars();
299 let constraints = Arc::new(RwLock::new(LPooled::take()));
300 let throws = throws.resolve_tvars();
301 let explicit_throws = *explicit_throws;
302 let lambda_ids = lambda_ids.clone();
303 FnType { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
304 }
305
306 pub fn unbind_tvars(&self) {
307 let FnType {
308 args,
309 vargs,
310 rtype,
311 constraints,
312 throws,
313 explicit_throws: _,
314 lambda_ids: _,
315 } = self;
316 for arg in args.iter() {
317 arg.typ.unbind_tvars()
318 }
319 if let Some(t) = vargs {
320 t.unbind_tvars()
321 }
322 rtype.unbind_tvars();
323 for (tv, _) in constraints.read().iter() {
324 tv.unbind();
325 }
326 throws.unbind_tvars();
327 }
328
329 pub fn constrain_known(&self) {
330 let mut known = LPooled::take();
331 self.collect_tvars(&mut known);
332 let mut constraints = self.constraints.write();
333 for (name, tv) in known.drain() {
334 if let Some(t) = tv.read().typ.read().as_ref()
335 && t != &Type::Bottom
336 && t != &Type::Any
337 {
338 if !constraints.iter().any(|(tv, _)| tv.name == name) {
339 t.bind_as(&Type::Any);
340 constraints.push((tv.clone(), t.normalize()));
341 }
342 }
343 }
344 }
345
346 pub fn reset_tvars(&self) -> Self {
347 let FnType {
348 args,
349 vargs,
350 rtype,
351 constraints,
352 throws,
353 explicit_throws,
354 lambda_ids,
355 } = self;
356 let args = Arc::from_iter(
357 args.iter()
358 .map(|a| FnArgType { kind: a.kind.clone(), typ: a.typ.reset_tvars() }),
359 );
360 let vargs = vargs.as_ref().map(|t| t.reset_tvars());
361 let rtype = rtype.reset_tvars();
362 let constraints = Arc::new(RwLock::new(
363 constraints
364 .read()
365 .iter()
366 .map(|(tv, tc)| (TVar::empty_named(tv.name.clone()), tc.reset_tvars()))
367 .collect(),
368 ));
369 let throws = throws.reset_tvars();
370 let explicit_throws = *explicit_throws;
371 let lambda_ids = lambda_ids.clone();
372 FnType { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
373 }
374
375 pub fn replace_tvars(&self, known: &AHashMap<ArcStr, Type>) -> Self {
376 self.replace_tvars_int(known, &mut LPooled::take())
377 }
378
379 pub(super) fn replace_tvars_int(
380 &self,
381 known: &AHashMap<ArcStr, Type>,
382 renamed: &mut AHashMap<ArcStr, TVar>,
383 ) -> Self {
384 let FnType {
385 args,
386 vargs,
387 rtype,
388 constraints,
389 throws,
390 explicit_throws,
391 lambda_ids,
392 } = self;
393 let args = Arc::from_iter(args.iter().map(|a| FnArgType {
394 kind: a.kind.clone(),
395 typ: a.typ.replace_tvars_int(known, renamed),
396 }));
397 let vargs = vargs.as_ref().map(|t| t.replace_tvars_int(known, renamed));
398 let rtype = rtype.replace_tvars_int(known, renamed);
399 let constraints = constraints.clone();
400 let throws = throws.replace_tvars_int(known, renamed);
401 let explicit_throws = *explicit_throws;
402 let lambda_ids = lambda_ids.clone();
403 FnType { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
404 }
405
406 pub fn replace_auto_constrained(&self) -> Self {
417 let mut known: LPooled<AHashMap<ArcStr, Type>> = LPooled::take();
418 let Self { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids } =
419 self;
420 let constraints: LPooled<Vec<(TVar, Type)>> = constraints
421 .read()
422 .iter()
423 .filter_map(|(tv, ct)| {
424 if tv.name.starts_with("_") {
425 known.insert(tv.name.clone(), ct.clone());
426 None
427 } else {
428 Some((tv.clone(), ct.clone()))
429 }
430 })
431 .collect();
432 let constraints = Arc::new(RwLock::new(constraints));
433 let mut all_tvars: LPooled<AHashMap<ArcStr, TVar>> = LPooled::take();
434 self.collect_tvars(&mut all_tvars);
435 for (name, tv) in all_tvars.drain() {
436 if !known.contains_key(&name) {
437 known.insert(name, Type::TVar(tv));
438 }
439 }
440 let args = Arc::from_iter(args.iter().map(|FnArgType { kind, typ }| FnArgType {
441 kind: kind.clone(),
442 typ: typ.replace_tvars(&known),
443 }));
444 let vargs = vargs.as_ref().map(|t| t.replace_tvars(&known));
445 let rtype = rtype.replace_tvars(&known);
446 let throws = throws.replace_tvars(&known);
447 let explicit_throws = *explicit_throws;
448 let lambda_ids = lambda_ids.clone();
449 Self { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
450 }
451
452 pub fn has_unbound(&self) -> bool {
453 let FnType {
454 args,
455 vargs,
456 rtype,
457 constraints,
458 throws,
459 explicit_throws: _,
460 lambda_ids: _,
461 } = self;
462 args.iter().any(|a| a.typ.has_unbound())
463 || vargs.as_ref().map(|t| t.has_unbound()).unwrap_or(false)
464 || rtype.has_unbound()
465 || constraints
466 .read()
467 .iter()
468 .any(|(tv, tc)| tv.read().typ.read().is_none() || tc.has_unbound())
469 || throws.has_unbound()
470 }
471
472 pub fn bind_as(&self, t: &Type) {
473 let FnType {
474 args,
475 vargs,
476 rtype,
477 constraints,
478 throws,
479 explicit_throws: _,
480 lambda_ids: _,
481 } = self;
482 for a in args.iter() {
483 a.typ.bind_as(t)
484 }
485 if let Some(va) = vargs.as_ref() {
486 va.bind_as(t)
487 }
488 rtype.bind_as(t);
489 for (tv, tc) in constraints.read().iter() {
490 let tv = tv.read();
491 let mut tv = tv.typ.write();
492 if tv.is_none() {
493 *tv = Some(t.clone())
494 }
495 tc.bind_as(t)
496 }
497 throws.bind_as(t);
498 }
499
500 pub fn alias_tvars(&self, known: &mut AHashMap<ArcStr, TVar>) {
501 let FnType {
502 args,
503 vargs,
504 rtype,
505 constraints,
506 throws,
507 explicit_throws: _,
508 lambda_ids: _,
509 } = self;
510 for arg in args.iter() {
511 arg.typ.alias_tvars(known)
512 }
513 if let Some(vargs) = vargs {
514 vargs.alias_tvars(known)
515 }
516 rtype.alias_tvars(known);
517 for (tv, tc) in constraints.read().iter() {
518 Type::TVar(tv.clone()).alias_tvars(known);
519 tc.alias_tvars(known);
520 }
521 throws.alias_tvars(known);
522 }
523
524 pub fn unfreeze_tvars(&self) {
525 let FnType {
526 args,
527 vargs,
528 rtype,
529 constraints,
530 throws,
531 explicit_throws: _,
532 lambda_ids: _,
533 } = self;
534 for arg in args.iter() {
535 arg.typ.unfreeze_tvars()
536 }
537 if let Some(vargs) = vargs {
538 vargs.unfreeze_tvars()
539 }
540 rtype.unfreeze_tvars();
541 for (tv, tc) in constraints.read().iter() {
542 Type::TVar(tv.clone()).unfreeze_tvars();
543 tc.unfreeze_tvars();
544 }
545 throws.unfreeze_tvars();
546 }
547
548 pub fn collect_tvars(&self, known: &mut AHashMap<ArcStr, TVar>) {
549 let FnType {
550 args,
551 vargs,
552 rtype,
553 constraints,
554 throws,
555 explicit_throws: _,
556 lambda_ids: _,
557 } = self;
558 for arg in args.iter() {
559 arg.typ.collect_tvars(known)
560 }
561 if let Some(vargs) = vargs {
562 vargs.collect_tvars(known)
563 }
564 rtype.collect_tvars(known);
565 for (tv, tc) in constraints.read().iter() {
566 Type::TVar(tv.clone()).collect_tvars(known);
567 tc.collect_tvars(known);
568 }
569 throws.collect_tvars(known);
570 }
571
572 pub fn contains(&self, env: &Env, t: &Self) -> Result<bool> {
573 self.contains_int(
574 ContainsFlags::AliasTVars | ContainsFlags::InitTVars,
575 env,
576 &mut RefHist::new(LPooled::take()),
577 t,
578 )
579 }
580
581 pub(super) fn contains_int(
582 &self,
583 flags: BitFlags<ContainsFlags>,
584 env: &Env,
585 hist: &mut RefHist<AHashMap<(Option<usize>, Option<usize>), bool>>,
586 t: &Self,
587 ) -> Result<bool> {
588 let mut sul = 0;
589 let mut tul = 0;
590 for (i, a) in self.args.iter().enumerate() {
591 sul = i;
592 match &a.kind {
593 FnArgKind::Positional { .. } => {
594 break;
595 }
596 FnArgKind::Labeled { name: l, .. } => {
597 match t.args.iter().find(|a| a.label() == Some(l)) {
598 None => return Ok(false),
599 Some(o) => {
600 if !o.typ.contains_int(flags, env, hist, &a.typ)? {
601 return Ok(false);
602 }
603 }
604 }
605 }
606 }
607 }
608 for (i, a) in t.args.iter().enumerate() {
609 tul = i;
610 match &a.kind {
611 FnArgKind::Positional { .. } => {
612 break;
613 }
614 FnArgKind::Labeled { name: l, has_default } => {
615 match self.args.iter().find(|a| a.label() == Some(l)) {
616 Some(_) => (),
617 None => {
618 if !*has_default {
619 return Ok(false);
620 }
621 }
622 }
623 }
624 }
625 }
626 let slen = self.args.len() - sul;
627 let tlen = t.args.len() - tul;
628 Ok(slen == tlen
629 && t.args[tul..]
630 .iter()
631 .zip(self.args[sul..].iter())
632 .map(|(t, s)| t.typ.contains_int(flags, env, hist, &s.typ))
633 .collect::<Result<AndAc>>()?
634 .0
635 && match (&t.vargs, &self.vargs) {
636 (Some(tv), Some(sv)) => tv.contains_int(flags, env, hist, sv)?,
637 (None, None) => true,
638 (_, _) => false,
639 }
640 && self.rtype.contains_int(flags, env, hist, &t.rtype)?
641 && self
642 .constraints
643 .read()
644 .iter()
645 .map(|(tv, tc)| {
646 tc.contains_int(flags, env, hist, &Type::TVar(tv.clone()))
647 })
648 .collect::<Result<AndAc>>()?
649 .0
650 && t.constraints
651 .read()
652 .iter()
653 .map(|(tv, tc)| {
654 tc.contains_int(flags, env, hist, &Type::TVar(tv.clone()))
655 })
656 .collect::<Result<AndAc>>()?
657 .0
658 && self.throws.contains_int(flags, env, hist, &t.throws)?)
659 }
660
661 pub fn merge_lambda_ids(&self, other: &Self) {
664 if Arc::ptr_eq(&self.lambda_ids, &other.lambda_ids) {
665 return;
666 }
667 let mut self_ids = self.lambda_ids.write();
668 let mut other_ids = other.lambda_ids.write();
669 self_ids.extend(other_ids.iter().copied());
670 other_ids.extend(self_ids.iter().copied());
671 }
672
673 pub fn check_contains(&self, env: &Env, other: &Self) -> Result<()> {
674 if !self.contains(env, other)? {
675 bail!("Fn type mismatch {self} does not contain {other}")
676 }
677 Ok(())
678 }
679
680 pub fn sig_contains(&self, env: &Env, other: &Self) -> Result<bool> {
683 let Self {
684 args: args0,
685 vargs: vargs0,
686 rtype: rtype0,
687 constraints: constraints0,
688 throws: tr0,
689 explicit_throws: _,
690 lambda_ids: _,
691 } = self;
692 let Self {
693 args: args1,
694 vargs: vargs1,
695 rtype: rtype1,
696 constraints: constraints1,
697 throws: tr1,
698 explicit_throws: _,
699 lambda_ids: _,
700 } = other;
701 Ok(args0.len() == args1.len()
702 && args0
703 .iter()
704 .zip(args1.iter())
705 .map(|(a0, a1)| Ok(a0.kind == a1.kind && a0.typ.contains(env, &a1.typ)?))
706 .collect::<Result<AndAc>>()?
707 .0
708 && match (vargs0, vargs1) {
709 (None, None) => true,
710 (None, _) | (_, None) => false,
711 (Some(t0), Some(t1)) => t0.contains(env, t1)?,
712 }
713 && rtype0.contains(env, rtype1)?
714 && constraints0
715 .read()
716 .iter()
717 .map(|(tv, tc)| tc.contains(env, &Type::TVar(tv.clone())))
718 .collect::<Result<AndAc>>()?
719 .0
720 && constraints1
721 .read()
722 .iter()
723 .map(|(tv, tc)| tc.contains(env, &Type::TVar(tv.clone())))
724 .collect::<Result<AndAc>>()?
725 .0
726 && tr0.contains(env, tr1)?)
727 }
728
729 pub fn check_sig_contains(&self, env: &Env, other: &Self) -> Result<()> {
730 if !self.sig_contains(env, other)? {
731 bail!("Fn signature {self} does not contain {other}")
732 }
733 Ok(())
734 }
735
736 pub fn sig_matches(
737 &self,
738 env: &Env,
739 impl_fn: &Self,
740 adts: &mut IntMap<AbstractId, Type>,
741 ) -> Result<()> {
742 self.sig_matches_int(
743 env,
744 impl_fn,
745 &mut LPooled::take(),
746 &mut RefHist::new(LPooled::take()),
747 adts,
748 )
749 }
750
751 pub(super) fn sig_matches_int(
752 &self,
753 env: &Env,
754 impl_fn: &Self,
755 tvar_map: &mut IntMap<usize, Type>,
756 hist: &mut RefHist<AHashSet<(Option<usize>, Option<usize>)>>,
757 adts: &IntMap<AbstractId, Type>,
758 ) -> Result<()> {
759 let Self {
760 args: sig_args,
761 vargs: sig_vargs,
762 rtype: sig_rtype,
763 constraints: sig_constraints,
764 throws: sig_throws,
765 explicit_throws: _,
766 lambda_ids: _,
767 } = self;
768 let Self {
769 args: impl_args,
770 vargs: impl_vargs,
771 rtype: impl_rtype,
772 constraints: impl_constraints,
773 throws: impl_throws,
774 explicit_throws: _,
775 lambda_ids: _,
776 } = impl_fn;
777 if sig_args.len() != impl_args.len() {
778 bail!(
779 "argument count mismatch: signature has {}, implementation has {}",
780 sig_args.len(),
781 impl_args.len()
782 );
783 }
784 for (i, (sig_arg, impl_arg)) in sig_args.iter().zip(impl_args.iter()).enumerate()
785 {
786 if sig_arg.kind != impl_arg.kind {
787 bail!(
788 "argument {} kind mismatch: signature has {:?}, implementation has {:?}",
789 i,
790 sig_arg.kind,
791 impl_arg.kind
792 );
793 }
794 sig_arg
795 .typ
796 .sig_matches_int(env, &impl_arg.typ, tvar_map, hist, adts)
797 .with_context(|| format!("in argument {i}"))?;
798 }
799 match (sig_vargs, impl_vargs) {
800 (None, None) => (),
801 (Some(sig_va), Some(impl_va)) => {
802 sig_va
803 .sig_matches_int(env, impl_va, tvar_map, hist, adts)
804 .context("in variadic argument")?;
805 }
806 (None, Some(_)) => {
807 bail!("signature has no variadic args but implementation does")
808 }
809 (Some(_), None) => {
810 bail!("signature has variadic args but implementation does not")
811 }
812 }
813 sig_rtype
814 .sig_matches_int(env, impl_rtype, tvar_map, hist, adts)
815 .context("in return type")?;
816 sig_throws
817 .sig_matches_int(env, impl_throws, tvar_map, hist, adts)
818 .context("in throws clause")?;
819 let sig_cons = sig_constraints.read();
820 let impl_cons = impl_constraints.read();
821 for (sig_tv, sig_tc) in sig_cons.iter() {
822 if !impl_cons
823 .iter()
824 .any(|(impl_tv, impl_tc)| sig_tv == impl_tv && sig_tc == impl_tc)
825 {
826 bail!("missing constraint {sig_tv}: {sig_tc} in implementation")
827 }
828 }
829 for (impl_tv, impl_tc) in impl_cons.iter() {
830 match tvar_map.get(&impl_tv.inner_addr()).cloned() {
831 None | Some(Type::TVar(_)) => (),
832 Some(sig_type) => {
833 sig_type.sig_matches_int(env, impl_tc, tvar_map, hist, adts).with_context(|| {
834 format!(
835 "signature has concrete type {sig_type}, implementation constraint is {impl_tc}"
836 )
837 })?;
838 }
839 }
840 }
841 Ok(())
842 }
843
844 pub fn map_argpos(
845 &self,
846 other: &Self,
847 ) -> LPooled<AHashMap<ArcStr, (Option<usize>, Option<usize>)>> {
848 let mut tbl: LPooled<AHashMap<ArcStr, (Option<usize>, Option<usize>)>> =
849 LPooled::take();
850 for (i, a) in self.args.iter().enumerate() {
851 match &a.kind {
852 FnArgKind::Positional { .. } => break,
853 FnArgKind::Labeled { name, .. } => {
854 tbl.entry(name.clone()).or_default().0 = Some(i)
855 }
856 }
857 }
858 for (i, a) in other.args.iter().enumerate() {
859 match &a.kind {
860 FnArgKind::Positional { .. } => break,
861 FnArgKind::Labeled { name, .. } => {
862 tbl.entry(name.clone()).or_default().1 = Some(i)
863 }
864 }
865 }
866 tbl
867 }
868
869 pub fn scope_refs(&self, scope: &ModPath) -> Self {
870 let vargs = self.vargs.as_ref().map(|t| t.scope_refs(scope));
871 let rtype = self.rtype.scope_refs(scope);
872 let args =
873 Arc::from_iter(self.args.iter().map(|a| FnArgType {
874 kind: a.kind.clone(),
875 typ: a.typ.scope_refs(scope),
876 }));
877 let mut cres: SmallVec<[(TVar, Type); 4]> = smallvec![];
878 for (tv, tc) in self.constraints.read().iter() {
879 let tv = tv.scope_refs(scope);
880 let tc = tc.scope_refs(scope);
881 cres.push((tv, tc));
882 }
883 let throws = self.throws.scope_refs(scope);
884 FnType {
885 args,
886 rtype,
887 constraints: Arc::new(RwLock::new(cres.into_iter().collect())),
888 vargs,
889 throws,
890 explicit_throws: self.explicit_throws,
891 lambda_ids: self.lambda_ids.clone(),
892 }
893 }
894}
895
896impl fmt::Display for FnType {
897 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
898 let constraints = self.constraints.read();
899 if constraints.len() == 0 {
900 write!(f, "fn(")?;
901 } else {
902 write!(f, "fn<")?;
903 for (i, (tv, t)) in constraints.iter().enumerate() {
904 write!(f, "{tv}: {t}")?;
905 if i < constraints.len() - 1 {
906 write!(f, ", ")?;
907 }
908 }
909 write!(f, ">(")?;
910 }
911 for (i, a) in self.args.iter().enumerate() {
912 match &a.kind {
913 FnArgKind::Labeled { name, has_default: true } => {
914 write!(f, "?#{name}: ")?
915 }
916 FnArgKind::Labeled { name, has_default: false } => {
917 write!(f, "#{name}: ")?
918 }
919 FnArgKind::Positional { name: Some(n) } => write!(f, "{n}: ")?,
920 FnArgKind::Positional { name: None } => (),
921 }
922 write!(f, "{}", a.typ)?;
923 if i < self.args.len() - 1 || self.vargs.is_some() {
924 write!(f, ", ")?;
925 }
926 }
927 if let Some(vargs) = &self.vargs {
928 write!(f, "@args: {}", vargs)?;
929 }
930 match &self.rtype {
931 Type::Fn(ft) => write!(f, ") -> ({ft})")?,
932 Type::ByRef(t) => match &**t {
933 Type::Fn(ft) => write!(f, ") -> &({ft})")?,
934 t => write!(f, ") -> &{t}")?,
935 },
936 t => write!(f, ") -> {t}")?,
937 }
938 match &self.throws {
939 Type::Bottom => Ok(()),
940 Type::TVar(tv) if *tv.read().typ.read() == Some(Type::Bottom) => Ok(()),
941 Type::TVar(tv)
942 if tv.name.starts_with('_') && tv.read().typ.read().is_none() =>
943 {
944 Ok(())
945 }
946 t => write!(f, " throws {t}"),
947 }
948 }
949}
950
951impl PrettyDisplay for FnType {
952 fn fmt_pretty_inner(&self, buf: &mut PrettyBuf) -> fmt::Result {
953 let constraints = self.constraints.read();
954 if constraints.is_empty() {
955 writeln!(buf, "fn(")?;
956 } else {
957 writeln!(buf, "fn<")?;
958 buf.with_indent(2, |buf| {
959 for (i, (tv, t)) in constraints.iter().enumerate() {
960 write!(buf, "{tv}: ")?;
961 buf.with_indent(2, |buf| t.fmt_pretty(buf))?;
962 if i < constraints.len() - 1 {
963 buf.kill_newline();
964 writeln!(buf, ",")?;
965 }
966 }
967 Ok(())
968 })?;
969 writeln!(buf, ">(")?;
970 }
971 buf.with_indent(2, |buf| {
972 for (i, a) in self.args.iter().enumerate() {
973 match &a.kind {
974 FnArgKind::Labeled { name, has_default: true } => {
975 write!(buf, "?#{name}: ")?
976 }
977 FnArgKind::Labeled { name, has_default: false } => {
978 write!(buf, "#{name}: ")?
979 }
980 FnArgKind::Positional { name: Some(n) } => write!(buf, "{n}: ")?,
981 FnArgKind::Positional { name: None } => (),
982 }
983 buf.with_indent(2, |buf| a.typ.fmt_pretty(buf))?;
984 if i < self.args.len() - 1 || self.vargs.is_some() {
985 buf.kill_newline();
986 writeln!(buf, ",")?;
987 }
988 }
989 if let Some(vargs) = &self.vargs {
990 write!(buf, "@args: ")?;
991 buf.with_indent(2, |buf| vargs.fmt_pretty(buf))?;
992 }
993 Ok(())
994 })?;
995 match &self.rtype {
996 Type::Fn(ft) => {
997 write!(buf, ") -> (")?;
998 ft.fmt_pretty(buf)?;
999 buf.kill_newline();
1000 writeln!(buf, ")")?;
1001 }
1002 Type::ByRef(t) => match &**t {
1003 Type::Fn(ft) => {
1004 write!(buf, ") -> &(")?;
1005 ft.fmt_pretty(buf)?;
1006 buf.kill_newline();
1007 writeln!(buf, ")")?;
1008 }
1009 t => {
1010 write!(buf, ") -> &")?;
1011 t.fmt_pretty(buf)?;
1012 }
1013 },
1014 t => {
1015 write!(buf, ") -> ")?;
1016 t.fmt_pretty(buf)?;
1017 }
1018 }
1019 match &self.throws {
1020 Type::Bottom if !self.explicit_throws => Ok(()),
1021 Type::TVar(tv)
1022 if *tv.read().typ.read() == Some(Type::Bottom)
1023 && !self.explicit_throws =>
1024 {
1025 Ok(())
1026 }
1027 Type::TVar(tv)
1028 if tv.name.starts_with('_')
1029 && tv.read().typ.read().is_none()
1030 && !self.explicit_throws =>
1031 {
1032 Ok(())
1033 }
1034 t => {
1035 buf.kill_newline();
1036 write!(buf, " throws ")?;
1037 t.fmt_pretty(buf)
1038 }
1039 }
1040 }
1041}
1042
1043#[cfg(test)]
1044mod tests {
1045 use crate::expr::parser::parse_fn_type;
1046 use poolshark::local::LPooled;
1047
1048 #[test]
1056 fn polymorphic_sig_preserves_tvar_names() {
1057 let ft = parse_fn_type("fn(a: Array<'a>, @args: 'a) -> Array<'a>").unwrap();
1058 ft.alias_tvars(&mut LPooled::take());
1059 let folded = ft.replace_auto_constrained();
1060 let resolved = folded.resolve_tvars();
1061 let s = format!("{}", crate::typ::Type::Fn(triomphe::Arc::new(resolved)));
1062 assert!(
1063 s.contains("'a"),
1064 "named tvar 'a should survive pretty-printing, got: {s}"
1065 );
1066 assert!(
1067 !s.contains("'_"),
1068 "auto tvars should not leak into pretty output, got: {s}"
1069 );
1070 }
1071
1072 #[test]
1079 fn unbound_auto_throws_is_hidden() {
1080 let ft = parse_fn_type("fn(a: Array<'a>) -> i64").unwrap();
1081 ft.alias_tvars(&mut LPooled::take());
1082 let folded = ft.replace_auto_constrained();
1083 let resolved = folded.resolve_tvars();
1084 let s = format!("{}", crate::typ::Type::Fn(triomphe::Arc::new(resolved)));
1085 assert!(!s.contains("throws"), "unbound auto throws should not appear, got: {s}");
1086 }
1087
1088 #[test]
1093 fn explicit_throws_always_shown() {
1094 let ft = parse_fn_type("fn(x: 'a) -> 'a throws `Boom").unwrap();
1095 ft.alias_tvars(&mut LPooled::take());
1096 let s = format!("{}", crate::typ::Type::Fn(triomphe::Arc::new(ft)));
1097 assert!(s.contains("throws"), "explicit throws should be shown, got: {s}");
1098 assert!(s.contains("`Boom"), "throws variant should be printed, got: {s}");
1099 }
1100}