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