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, PartialEq, Eq, PartialOrd, Ord)]
25pub struct FnArgType {
26 pub label: Option<(ArcStr, bool)>,
27 pub typ: Type,
28}
29
30#[derive(Debug, Clone)]
31pub struct FnType {
32 pub args: Arc<[FnArgType]>,
33 pub vargs: Option<Type>,
34 pub rtype: Type,
35 pub constraints: Arc<RwLock<LPooled<Vec<(TVar, Type)>>>>,
36 pub throws: Type,
37 pub explicit_throws: bool,
38 pub lambda_ids: Arc<RwLock<FxHashSet<LambdaId>>>,
40}
41
42impl PartialEq for FnType {
43 fn eq(&self, other: &Self) -> bool {
44 let Self {
45 args: args0,
46 vargs: vargs0,
47 rtype: rtype0,
48 constraints: constraints0,
49 throws: th0,
50 explicit_throws: _,
51 lambda_ids: _,
52 } = self;
53 let Self {
54 args: args1,
55 vargs: vargs1,
56 rtype: rtype1,
57 constraints: constraints1,
58 throws: th1,
59 explicit_throws: _,
60 lambda_ids: _,
61 } = other;
62 args0 == args1
63 && vargs0 == vargs1
64 && rtype0 == rtype1
65 && &*constraints0.read() == &*constraints1.read()
66 && th0 == th1
67 }
68}
69
70impl Eq for FnType {}
71
72impl PartialOrd for FnType {
73 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
74 use std::cmp::Ordering;
75 let Self {
76 args: args0,
77 vargs: vargs0,
78 rtype: rtype0,
79 constraints: constraints0,
80 throws: th0,
81 explicit_throws: _,
82 lambda_ids: _,
83 } = self;
84 let Self {
85 args: args1,
86 vargs: vargs1,
87 rtype: rtype1,
88 constraints: constraints1,
89 throws: th1,
90 explicit_throws: _,
91 lambda_ids: _,
92 } = other;
93 match args0.partial_cmp(&args1) {
94 Some(Ordering::Equal) => match vargs0.partial_cmp(vargs1) {
95 Some(Ordering::Equal) => match rtype0.partial_cmp(rtype1) {
96 Some(Ordering::Equal) => {
97 match constraints0.read().partial_cmp(&*constraints1.read()) {
98 Some(Ordering::Equal) => th0.partial_cmp(th1),
99 r => r,
100 }
101 }
102 r => r,
103 },
104 r => r,
105 },
106 r => r,
107 }
108 }
109}
110
111impl Ord for FnType {
112 fn cmp(&self, other: &Self) -> Ordering {
113 self.partial_cmp(other).unwrap()
114 }
115}
116
117impl Default for FnType {
118 fn default() -> Self {
119 Self {
120 args: Arc::from_iter([]),
121 vargs: None,
122 rtype: Default::default(),
123 constraints: Arc::new(RwLock::new(LPooled::take())),
124 throws: Default::default(),
125 explicit_throws: false,
126 lambda_ids: Arc::new(RwLock::new(FxHashSet::default())),
127 }
128 }
129}
130
131impl FnType {
132 pub(super) fn normalize(&self) -> Self {
133 let Self { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids } =
134 self;
135 let args = Arc::from_iter(
136 args.iter()
137 .map(|a| FnArgType { label: a.label.clone(), typ: a.typ.normalize() }),
138 );
139 let vargs = vargs.as_ref().map(|t| t.normalize());
140 let rtype = rtype.normalize();
141 let constraints = Arc::new(RwLock::new(
142 constraints
143 .read()
144 .iter()
145 .map(|(tv, t)| (tv.clone(), t.normalize()))
146 .collect(),
147 ));
148 let throws = throws.normalize();
149 let explicit_throws = *explicit_throws;
150 let lambda_ids = lambda_ids.clone();
151 FnType { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
152 }
153
154 pub fn resolve_tvars(&self) -> Self {
157 let Self {
158 args,
159 vargs,
160 rtype,
161 constraints: _,
162 throws,
163 explicit_throws,
164 lambda_ids,
165 } = self;
166 let args =
167 Arc::from_iter(args.iter().map(|a| FnArgType {
168 label: a.label.clone(),
169 typ: a.typ.resolve_tvars(),
170 }));
171 let vargs = vargs.as_ref().map(|t| t.resolve_tvars());
172 let rtype = rtype.resolve_tvars();
173 let constraints = Arc::new(RwLock::new(LPooled::take()));
174 let throws = throws.resolve_tvars();
175 let explicit_throws = *explicit_throws;
176 let lambda_ids = lambda_ids.clone();
177 FnType { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
178 }
179
180 pub fn unbind_tvars(&self) {
181 let FnType {
182 args,
183 vargs,
184 rtype,
185 constraints,
186 throws,
187 explicit_throws: _,
188 lambda_ids: _,
189 } = self;
190 for arg in args.iter() {
191 arg.typ.unbind_tvars()
192 }
193 if let Some(t) = vargs {
194 t.unbind_tvars()
195 }
196 rtype.unbind_tvars();
197 for (tv, _) in constraints.read().iter() {
198 tv.unbind();
199 }
200 throws.unbind_tvars();
201 }
202
203 pub fn constrain_known(&self) {
204 let mut known = LPooled::take();
205 self.collect_tvars(&mut known);
206 let mut constraints = self.constraints.write();
207 for (name, tv) in known.drain() {
208 if let Some(t) = tv.read().typ.read().as_ref()
209 && t != &Type::Bottom
210 && t != &Type::Any
211 {
212 if !constraints.iter().any(|(tv, _)| tv.name == name) {
213 t.bind_as(&Type::Any);
214 constraints.push((tv.clone(), t.normalize()));
215 }
216 }
217 }
218 }
219
220 pub fn reset_tvars(&self) -> Self {
221 let FnType {
222 args,
223 vargs,
224 rtype,
225 constraints,
226 throws,
227 explicit_throws,
228 lambda_ids,
229 } = self;
230 let args = Arc::from_iter(
231 args.iter()
232 .map(|a| FnArgType { label: a.label.clone(), typ: a.typ.reset_tvars() }),
233 );
234 let vargs = vargs.as_ref().map(|t| t.reset_tvars());
235 let rtype = rtype.reset_tvars();
236 let constraints = Arc::new(RwLock::new(
237 constraints
238 .read()
239 .iter()
240 .map(|(tv, tc)| (TVar::empty_named(tv.name.clone()), tc.reset_tvars()))
241 .collect(),
242 ));
243 let throws = throws.reset_tvars();
244 let explicit_throws = *explicit_throws;
245 let lambda_ids = lambda_ids.clone();
246 FnType { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
247 }
248
249 pub fn replace_tvars(&self, known: &FxHashMap<ArcStr, Type>) -> Self {
250 self.replace_tvars_int(known, &mut LPooled::take())
251 }
252
253 pub(super) fn replace_tvars_int(
254 &self,
255 known: &FxHashMap<ArcStr, Type>,
256 renamed: &mut FxHashMap<ArcStr, TVar>,
257 ) -> Self {
258 let FnType {
259 args,
260 vargs,
261 rtype,
262 constraints,
263 throws,
264 explicit_throws,
265 lambda_ids,
266 } = self;
267 let args = Arc::from_iter(args.iter().map(|a| FnArgType {
268 label: a.label.clone(),
269 typ: a.typ.replace_tvars_int(known, renamed),
270 }));
271 let vargs = vargs.as_ref().map(|t| t.replace_tvars_int(known, renamed));
272 let rtype = rtype.replace_tvars_int(known, renamed);
273 let constraints = constraints.clone();
274 let throws = throws.replace_tvars_int(known, renamed);
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 replace_auto_constrained(&self) -> Self {
284 let mut known: LPooled<FxHashMap<ArcStr, Type>> = LPooled::take();
285 let Self { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids } =
286 self;
287 let constraints: LPooled<Vec<(TVar, Type)>> = constraints
288 .read()
289 .iter()
290 .filter_map(|(tv, ct)| {
291 if tv.name.starts_with("_") {
292 known.insert(tv.name.clone(), ct.clone());
293 None
294 } else {
295 Some((tv.clone(), ct.clone()))
296 }
297 })
298 .collect();
299 let constraints = Arc::new(RwLock::new(constraints));
300 let args = Arc::from_iter(args.iter().map(|FnArgType { label, typ }| {
301 FnArgType { label: label.clone(), typ: typ.replace_tvars(&known) }
302 }));
303 let vargs = vargs.as_ref().map(|t| t.replace_tvars(&known));
304 let rtype = rtype.replace_tvars(&known);
305 let throws = throws.replace_tvars(&known);
306 let explicit_throws = *explicit_throws;
307 let lambda_ids = lambda_ids.clone();
308 Self { args, vargs, rtype, constraints, throws, explicit_throws, lambda_ids }
309 }
310
311 pub fn has_unbound(&self) -> bool {
312 let FnType {
313 args,
314 vargs,
315 rtype,
316 constraints,
317 throws,
318 explicit_throws: _,
319 lambda_ids: _,
320 } = self;
321 args.iter().any(|a| a.typ.has_unbound())
322 || vargs.as_ref().map(|t| t.has_unbound()).unwrap_or(false)
323 || rtype.has_unbound()
324 || constraints
325 .read()
326 .iter()
327 .any(|(tv, tc)| tv.read().typ.read().is_none() || tc.has_unbound())
328 || throws.has_unbound()
329 }
330
331 pub fn bind_as(&self, t: &Type) {
332 let FnType {
333 args,
334 vargs,
335 rtype,
336 constraints,
337 throws,
338 explicit_throws: _,
339 lambda_ids: _,
340 } = self;
341 for a in args.iter() {
342 a.typ.bind_as(t)
343 }
344 if let Some(va) = vargs.as_ref() {
345 va.bind_as(t)
346 }
347 rtype.bind_as(t);
348 for (tv, tc) in constraints.read().iter() {
349 let tv = tv.read();
350 let mut tv = tv.typ.write();
351 if tv.is_none() {
352 *tv = Some(t.clone())
353 }
354 tc.bind_as(t)
355 }
356 throws.bind_as(t);
357 }
358
359 pub fn alias_tvars(&self, known: &mut FxHashMap<ArcStr, TVar>) {
360 let FnType {
361 args,
362 vargs,
363 rtype,
364 constraints,
365 throws,
366 explicit_throws: _,
367 lambda_ids: _,
368 } = self;
369 for arg in args.iter() {
370 arg.typ.alias_tvars(known)
371 }
372 if let Some(vargs) = vargs {
373 vargs.alias_tvars(known)
374 }
375 rtype.alias_tvars(known);
376 for (tv, tc) in constraints.read().iter() {
377 Type::TVar(tv.clone()).alias_tvars(known);
378 tc.alias_tvars(known);
379 }
380 throws.alias_tvars(known);
381 }
382
383 pub fn unfreeze_tvars(&self) {
384 let FnType {
385 args,
386 vargs,
387 rtype,
388 constraints,
389 throws,
390 explicit_throws: _,
391 lambda_ids: _,
392 } = self;
393 for arg in args.iter() {
394 arg.typ.unfreeze_tvars()
395 }
396 if let Some(vargs) = vargs {
397 vargs.unfreeze_tvars()
398 }
399 rtype.unfreeze_tvars();
400 for (tv, tc) in constraints.read().iter() {
401 Type::TVar(tv.clone()).unfreeze_tvars();
402 tc.unfreeze_tvars();
403 }
404 throws.unfreeze_tvars();
405 }
406
407 pub fn collect_tvars(&self, known: &mut FxHashMap<ArcStr, TVar>) {
408 let FnType {
409 args,
410 vargs,
411 rtype,
412 constraints,
413 throws,
414 explicit_throws: _,
415 lambda_ids: _,
416 } = self;
417 for arg in args.iter() {
418 arg.typ.collect_tvars(known)
419 }
420 if let Some(vargs) = vargs {
421 vargs.collect_tvars(known)
422 }
423 rtype.collect_tvars(known);
424 for (tv, tc) in constraints.read().iter() {
425 Type::TVar(tv.clone()).collect_tvars(known);
426 tc.collect_tvars(known);
427 }
428 throws.collect_tvars(known);
429 }
430
431 pub fn contains(&self, env: &Env, t: &Self) -> Result<bool> {
432 self.contains_int(
433 ContainsFlags::AliasTVars | ContainsFlags::InitTVars,
434 env,
435 &mut RefHist::new(LPooled::take()),
436 t,
437 )
438 }
439
440 pub(super) fn contains_int(
441 &self,
442 flags: BitFlags<ContainsFlags>,
443 env: &Env,
444 hist: &mut RefHist<FxHashMap<(Option<usize>, Option<usize>), bool>>,
445 t: &Self,
446 ) -> Result<bool> {
447 let mut sul = 0;
448 let mut tul = 0;
449 for (i, a) in self.args.iter().enumerate() {
450 sul = i;
451 match &a.label {
452 None => {
453 break;
454 }
455 Some((l, _)) => match t
456 .args
457 .iter()
458 .find(|a| a.label.as_ref().map(|a| &a.0) == Some(l))
459 {
460 None => return Ok(false),
461 Some(o) => {
462 if !o.typ.contains_int(flags, env, hist, &a.typ)? {
463 return Ok(false);
464 }
465 }
466 },
467 }
468 }
469 for (i, a) in t.args.iter().enumerate() {
470 tul = i;
471 match &a.label {
472 None => {
473 break;
474 }
475 Some((l, opt)) => match self
476 .args
477 .iter()
478 .find(|a| a.label.as_ref().map(|a| &a.0) == Some(l))
479 {
480 Some(_) => (),
481 None => {
482 if !opt {
483 return Ok(false);
484 }
485 }
486 },
487 }
488 }
489 let slen = self.args.len() - sul;
490 let tlen = t.args.len() - tul;
491 Ok(slen == tlen
492 && t.args[tul..]
493 .iter()
494 .zip(self.args[sul..].iter())
495 .map(|(t, s)| t.typ.contains_int(flags, env, hist, &s.typ))
496 .collect::<Result<AndAc>>()?
497 .0
498 && match (&t.vargs, &self.vargs) {
499 (Some(tv), Some(sv)) => tv.contains_int(flags, env, hist, sv)?,
500 (None, None) => true,
501 (_, _) => false,
502 }
503 && self.rtype.contains_int(flags, env, hist, &t.rtype)?
504 && self
505 .constraints
506 .read()
507 .iter()
508 .map(|(tv, tc)| {
509 tc.contains_int(flags, env, hist, &Type::TVar(tv.clone()))
510 })
511 .collect::<Result<AndAc>>()?
512 .0
513 && t.constraints
514 .read()
515 .iter()
516 .map(|(tv, tc)| {
517 tc.contains_int(flags, env, hist, &Type::TVar(tv.clone()))
518 })
519 .collect::<Result<AndAc>>()?
520 .0
521 && self.throws.contains_int(flags, env, hist, &t.throws)?)
522 }
523
524 pub fn merge_lambda_ids(&self, other: &Self) {
527 if Arc::ptr_eq(&self.lambda_ids, &other.lambda_ids) {
528 return;
529 }
530 let mut self_ids = self.lambda_ids.write();
531 let mut other_ids = other.lambda_ids.write();
532 self_ids.extend(other_ids.iter().copied());
533 other_ids.extend(self_ids.iter().copied());
534 }
535
536 pub fn check_contains(&self, env: &Env, other: &Self) -> Result<()> {
537 if !self.contains(env, other)? {
538 bail!("Fn type mismatch {self} does not contain {other}")
539 }
540 Ok(())
541 }
542
543 pub fn sig_contains(&self, env: &Env, other: &Self) -> Result<bool> {
546 let Self {
547 args: args0,
548 vargs: vargs0,
549 rtype: rtype0,
550 constraints: constraints0,
551 throws: tr0,
552 explicit_throws: _,
553 lambda_ids: _,
554 } = self;
555 let Self {
556 args: args1,
557 vargs: vargs1,
558 rtype: rtype1,
559 constraints: constraints1,
560 throws: tr1,
561 explicit_throws: _,
562 lambda_ids: _,
563 } = other;
564 Ok(args0.len() == args1.len()
565 && args0
566 .iter()
567 .zip(args1.iter())
568 .map(
569 |(a0, a1)| Ok(a0.label == a1.label && a0.typ.contains(env, &a1.typ)?),
570 )
571 .collect::<Result<AndAc>>()?
572 .0
573 && match (vargs0, vargs1) {
574 (None, None) => true,
575 (None, _) | (_, None) => false,
576 (Some(t0), Some(t1)) => t0.contains(env, t1)?,
577 }
578 && rtype0.contains(env, rtype1)?
579 && constraints0
580 .read()
581 .iter()
582 .map(|(tv, tc)| tc.contains(env, &Type::TVar(tv.clone())))
583 .collect::<Result<AndAc>>()?
584 .0
585 && constraints1
586 .read()
587 .iter()
588 .map(|(tv, tc)| tc.contains(env, &Type::TVar(tv.clone())))
589 .collect::<Result<AndAc>>()?
590 .0
591 && tr0.contains(env, tr1)?)
592 }
593
594 pub fn check_sig_contains(&self, env: &Env, other: &Self) -> Result<()> {
595 if !self.sig_contains(env, other)? {
596 bail!("Fn signature {self} does not contain {other}")
597 }
598 Ok(())
599 }
600
601 pub fn sig_matches(
602 &self,
603 env: &Env,
604 impl_fn: &Self,
605 adts: &mut FxHashMap<AbstractId, Type>,
606 ) -> Result<()> {
607 self.sig_matches_int(
608 env,
609 impl_fn,
610 &mut LPooled::take(),
611 &mut RefHist::new(LPooled::take()),
612 adts,
613 )
614 }
615
616 pub(super) fn sig_matches_int(
617 &self,
618 env: &Env,
619 impl_fn: &Self,
620 tvar_map: &mut FxHashMap<usize, Type>,
621 hist: &mut RefHist<FxHashSet<(Option<usize>, Option<usize>)>>,
622 adts: &FxHashMap<AbstractId, Type>,
623 ) -> Result<()> {
624 let Self {
625 args: sig_args,
626 vargs: sig_vargs,
627 rtype: sig_rtype,
628 constraints: sig_constraints,
629 throws: sig_throws,
630 explicit_throws: _,
631 lambda_ids: _,
632 } = self;
633 let Self {
634 args: impl_args,
635 vargs: impl_vargs,
636 rtype: impl_rtype,
637 constraints: impl_constraints,
638 throws: impl_throws,
639 explicit_throws: _,
640 lambda_ids: _,
641 } = impl_fn;
642 if sig_args.len() != impl_args.len() {
643 bail!(
644 "argument count mismatch: signature has {}, implementation has {}",
645 sig_args.len(),
646 impl_args.len()
647 );
648 }
649 for (i, (sig_arg, impl_arg)) in sig_args.iter().zip(impl_args.iter()).enumerate()
650 {
651 if sig_arg.label != impl_arg.label {
652 bail!(
653 "argument {} label mismatch: signature has {:?}, implementation has {:?}",
654 i,
655 sig_arg.label,
656 impl_arg.label
657 );
658 }
659 sig_arg
660 .typ
661 .sig_matches_int(env, &impl_arg.typ, tvar_map, hist, adts)
662 .with_context(|| format!("in argument {i}"))?;
663 }
664 match (sig_vargs, impl_vargs) {
665 (None, None) => (),
666 (Some(sig_va), Some(impl_va)) => {
667 sig_va
668 .sig_matches_int(env, impl_va, tvar_map, hist, adts)
669 .context("in variadic argument")?;
670 }
671 (None, Some(_)) => {
672 bail!("signature has no variadic args but implementation does")
673 }
674 (Some(_), None) => {
675 bail!("signature has variadic args but implementation does not")
676 }
677 }
678 sig_rtype
679 .sig_matches_int(env, impl_rtype, tvar_map, hist, adts)
680 .context("in return type")?;
681 sig_throws
682 .sig_matches_int(env, impl_throws, tvar_map, hist, adts)
683 .context("in throws clause")?;
684 let sig_cons = sig_constraints.read();
685 let impl_cons = impl_constraints.read();
686 for (sig_tv, sig_tc) in sig_cons.iter() {
687 if !impl_cons
688 .iter()
689 .any(|(impl_tv, impl_tc)| sig_tv == impl_tv && sig_tc == impl_tc)
690 {
691 bail!("missing constraint {sig_tv}: {sig_tc} in implementation")
692 }
693 }
694 for (impl_tv, impl_tc) in impl_cons.iter() {
695 match tvar_map.get(&impl_tv.inner_addr()).cloned() {
696 None | Some(Type::TVar(_)) => (),
697 Some(sig_type) => {
698 sig_type.sig_matches_int(env, impl_tc, tvar_map, hist, adts).with_context(|| {
699 format!(
700 "signature has concrete type {sig_type}, implementation constraint is {impl_tc}"
701 )
702 })?;
703 }
704 }
705 }
706 Ok(())
707 }
708
709 pub fn map_argpos(
710 &self,
711 other: &Self,
712 ) -> LPooled<FxHashMap<ArcStr, (Option<usize>, Option<usize>)>> {
713 let mut tbl: LPooled<FxHashMap<ArcStr, (Option<usize>, Option<usize>)>> =
714 LPooled::take();
715 for (i, a) in self.args.iter().enumerate() {
716 match &a.label {
717 None => break,
718 Some((n, _)) => tbl.entry(n.clone()).or_default().0 = Some(i),
719 }
720 }
721 for (i, a) in other.args.iter().enumerate() {
722 match &a.label {
723 None => break,
724 Some((n, _)) => tbl.entry(n.clone()).or_default().1 = Some(i),
725 }
726 }
727 tbl
728 }
729
730 pub fn scope_refs(&self, scope: &ModPath) -> Self {
731 let vargs = self.vargs.as_ref().map(|t| t.scope_refs(scope));
732 let rtype = self.rtype.scope_refs(scope);
733 let args =
734 Arc::from_iter(self.args.iter().map(|a| FnArgType {
735 label: a.label.clone(),
736 typ: a.typ.scope_refs(scope),
737 }));
738 let mut cres: SmallVec<[(TVar, Type); 4]> = smallvec![];
739 for (tv, tc) in self.constraints.read().iter() {
740 let tv = tv.scope_refs(scope);
741 let tc = tc.scope_refs(scope);
742 cres.push((tv, tc));
743 }
744 let throws = self.throws.scope_refs(scope);
745 FnType {
746 args,
747 rtype,
748 constraints: Arc::new(RwLock::new(cres.into_iter().collect())),
749 vargs,
750 throws,
751 explicit_throws: self.explicit_throws,
752 lambda_ids: self.lambda_ids.clone(),
753 }
754 }
755}
756
757impl fmt::Display for FnType {
758 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
759 let constraints = self.constraints.read();
760 if constraints.len() == 0 {
761 write!(f, "fn(")?;
762 } else {
763 write!(f, "fn<")?;
764 for (i, (tv, t)) in constraints.iter().enumerate() {
765 write!(f, "{tv}: {t}")?;
766 if i < constraints.len() - 1 {
767 write!(f, ", ")?;
768 }
769 }
770 write!(f, ">(")?;
771 }
772 for (i, a) in self.args.iter().enumerate() {
773 match &a.label {
774 Some((l, true)) => write!(f, "?#{l}: ")?,
775 Some((l, false)) => write!(f, "#{l}: ")?,
776 None => (),
777 }
778 write!(f, "{}", a.typ)?;
779 if i < self.args.len() - 1 || self.vargs.is_some() {
780 write!(f, ", ")?;
781 }
782 }
783 if let Some(vargs) = &self.vargs {
784 write!(f, "@args: {}", vargs)?;
785 }
786 match &self.rtype {
787 Type::Fn(ft) => write!(f, ") -> ({ft})")?,
788 Type::ByRef(t) => match &**t {
789 Type::Fn(ft) => write!(f, ") -> &({ft})")?,
790 t => write!(f, ") -> &{t}")?,
791 },
792 t => write!(f, ") -> {t}")?,
793 }
794 match &self.throws {
795 Type::Bottom => Ok(()),
796 Type::TVar(tv) if *tv.read().typ.read() == Some(Type::Bottom) => Ok(()),
797 t => write!(f, " throws {t}"),
798 }
799 }
800}
801
802impl PrettyDisplay for FnType {
803 fn fmt_pretty_inner(&self, buf: &mut PrettyBuf) -> fmt::Result {
804 let constraints = self.constraints.read();
805 if constraints.is_empty() {
806 writeln!(buf, "fn(")?;
807 } else {
808 writeln!(buf, "fn<")?;
809 buf.with_indent(2, |buf| {
810 for (i, (tv, t)) in constraints.iter().enumerate() {
811 write!(buf, "{tv}: ")?;
812 buf.with_indent(2, |buf| t.fmt_pretty(buf))?;
813 if i < constraints.len() - 1 {
814 buf.kill_newline();
815 writeln!(buf, ",")?;
816 }
817 }
818 Ok(())
819 })?;
820 writeln!(buf, ">(")?;
821 }
822 buf.with_indent(2, |buf| {
823 for (i, a) in self.args.iter().enumerate() {
824 match &a.label {
825 Some((l, true)) => write!(buf, "?#{l}: ")?,
826 Some((l, false)) => write!(buf, "#{l}: ")?,
827 None => (),
828 }
829 buf.with_indent(2, |buf| a.typ.fmt_pretty(buf))?;
830 if i < self.args.len() - 1 || self.vargs.is_some() {
831 buf.kill_newline();
832 writeln!(buf, ",")?;
833 }
834 }
835 if let Some(vargs) = &self.vargs {
836 write!(buf, "@args: ")?;
837 buf.with_indent(2, |buf| vargs.fmt_pretty(buf))?;
838 }
839 Ok(())
840 })?;
841 match &self.rtype {
842 Type::Fn(ft) => {
843 write!(buf, ") -> (")?;
844 ft.fmt_pretty(buf)?;
845 buf.kill_newline();
846 writeln!(buf, ")")?;
847 }
848 Type::ByRef(t) => match &**t {
849 Type::Fn(ft) => {
850 write!(buf, ") -> &(")?;
851 ft.fmt_pretty(buf)?;
852 buf.kill_newline();
853 writeln!(buf, ")")?;
854 }
855 t => {
856 write!(buf, ") -> &")?;
857 t.fmt_pretty(buf)?;
858 }
859 },
860 t => {
861 write!(buf, ") -> ")?;
862 t.fmt_pretty(buf)?;
863 }
864 }
865 match &self.throws {
866 Type::Bottom if !self.explicit_throws => Ok(()),
867 Type::TVar(tv)
868 if *tv.read().typ.read() == Some(Type::Bottom)
869 && !self.explicit_throws =>
870 {
871 Ok(())
872 }
873 t => {
874 buf.kill_newline();
875 write!(buf, " throws ")?;
876 t.fmt_pretty(buf)
877 }
878 }
879 }
880}