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