prism_compiler/lang/
error.rs

1use crate::lang::UnionIndex;
2use crate::lang::{TcEnv, ValueOrigin};
3use ariadne::{Color, Label, Report, ReportKind, Source};
4use prism_parser::core::span::Span;
5use std::io;
6
7const SECONDARY_COLOR: Color = Color::Rgb(0xA0, 0xA0, 0xA0);
8
9#[derive(Debug)]
10pub enum TypeError {
11    ExpectType(UnionIndex),
12    ExpectFn(UnionIndex),
13    ExpectFnArg {
14        function_type: UnionIndex,
15        function_arg_type: UnionIndex,
16        arg_type: UnionIndex,
17    },
18    ExpectTypeAssert {
19        expr: UnionIndex,
20        expr_type: UnionIndex,
21        expected_type: UnionIndex,
22    },
23    IndexOutOfBound(UnionIndex),
24    InfiniteType(UnionIndex, UnionIndex),
25    BadInfer {
26        free_var: UnionIndex,
27        inferred_var: UnionIndex,
28    },
29    UnknownName(Span),
30}
31
32impl TcEnv {
33    pub fn report(&mut self, error: &TypeError) -> Option<Report<'static, Span>> {
34        let report = Report::build(ReportKind::Error, (), 0);
35        Some(match error {
36            TypeError::ExpectType(i) => {
37                let ValueOrigin::TypeOf(j) = self.value_origins[**i] else {
38                    unreachable!()
39                };
40                let ValueOrigin::SourceCode(span) = self.value_origins[*j] else {
41                    unreachable!()
42                };
43
44                report
45                    .with_message("Expected type")
46                    .with_label(Label::new(span).with_message(format!(
47                        "Expected a type, found value of type: {}",
48                        self.index_to_sm_string(self.value_types[&j])
49                    )))
50                    .finish()
51            }
52            TypeError::ExpectTypeAssert {
53                expr,
54                expr_type,
55                expected_type,
56            } => {
57                let ValueOrigin::SourceCode(span_expr) = self.value_origins[**expr] else {
58                    unreachable!()
59                };
60                let ValueOrigin::SourceCode(span_expected) = self.value_origins[**expected_type]
61                else {
62                    unreachable!()
63                };
64
65                report
66                    .with_message("Type assertion failed")
67                    .with_label(Label::new(span_expr).with_message(format!(
68                        "This value has type: {}",
69                        self.index_to_sm_string(*expr_type)
70                    )))
71                    .with_label(Label::new(span_expected).with_message(format!(
72                        "Expected value to have this type: {}",
73                        self.index_to_sm_string(*expected_type)
74                    )))
75                    .finish()
76            }
77            TypeError::IndexOutOfBound(i) => {
78                let ValueOrigin::SourceCode(span) = self.value_origins[**i] else {
79                    unreachable!()
80                };
81
82                report
83                    .with_message(format!("De Bruijn index `{}` out of bounds", *i))
84                    .with_label(Label::new(span).with_message("This index is out of bounds."))
85                    .finish()
86            }
87            TypeError::ExpectFn(i) => {
88                let ValueOrigin::TypeOf(j) = self.value_origins[**i] else {
89                    unreachable!()
90                };
91                let ValueOrigin::SourceCode(span) = self.value_origins[*j] else {
92                    unreachable!()
93                };
94                report
95                    .with_message("Expected function")
96                    .with_label(Label::new(span).with_message(format!(
97                        "Expected a function, found value of type: {}",
98                        self.index_to_sm_string(self.value_types[&j])
99                    )))
100                    .finish()
101            }
102            TypeError::ExpectFnArg {
103                function_type,
104                function_arg_type,
105                arg_type,
106            } => {
107                let ValueOrigin::TypeOf(j) = self.value_origins[**arg_type] else {
108                    unreachable!()
109                };
110                let ValueOrigin::SourceCode(span) = self.value_origins[*j] else {
111                    unreachable!()
112                };
113                let label_arg = Label::new(span).with_message(format!(
114                    "This argument has type: {}",
115                    self.index_to_sm_string(*arg_type)
116                ));
117
118                let ValueOrigin::TypeOf(j) = self.value_origins[**function_type] else {
119                    unreachable!()
120                };
121                let ValueOrigin::SourceCode(span) = self.value_origins[*j] else {
122                    unreachable!()
123                };
124                let label_fn = Label::new(span)
125                    .with_message(format!(
126                        "This function takes arguments of type: {}",
127                        self.index_to_sm_string(*function_arg_type)
128                    ))
129                    .with_order(1)
130                    .with_color(SECONDARY_COLOR);
131
132                report
133                    .with_message("Argument type mismatch in function application")
134                    .with_label(label_arg)
135                    .with_label(label_fn)
136                    .finish()
137            }
138            TypeError::InfiniteType(left, right) => {
139                let (left_span, left_description) = self.label_value(*left)?;
140                let (right_span, right_description) = self.label_value(*left)?;
141
142                let constraints = self
143                    .queued_beq_free
144                    .iter()
145                    .flat_map(|(i, cs)| cs.iter().map(move |c| format!("{:?} = {:?}", i, c.1 .0)))
146                    .collect::<Vec<_>>()
147                    .join(",");
148
149                report.with_message("Constraint creates an infinite type")
150                    .with_label(Label::new(left_span).with_message(format!("Left side of constraint from {left_description}: {}", self.index_to_sm_string(*left))))
151                    .with_label(Label::new(right_span).with_message(format!("Right side of constraint from {right_description}: {}", self.index_to_sm_string(*right))))
152                    .with_help(format!("If this doesn't obviously create an infinite type, I'm sorry. This is probably because of the following hidden constraints: [{constraints}]"))
153                    .finish()
154            }
155            TypeError::BadInfer { .. } => report.finish(),
156            TypeError::UnknownName(name) => report
157                .with_message("Undefined name within this scope.")
158                .with_label(Label::new(*name).with_message("This name is undefined."))
159                .finish(),
160        })
161    }
162
163    fn label_value(&self, mut value: UnionIndex) -> Option<(Span, &'static str)> {
164        let mut origin_description = "this value";
165        let span = loop {
166            match self.value_origins[*value] {
167                ValueOrigin::SourceCode(span) => break span,
168                ValueOrigin::TypeOf(sub_value) => {
169                    assert_eq!(origin_description, "this value");
170                    origin_description = "type of this value";
171                    value = sub_value;
172                }
173                ValueOrigin::FreeSub(v) => value = v,
174                ValueOrigin::Failure => return None,
175            }
176        };
177        Some((span, origin_description))
178    }
179}
180
181pub struct AggregatedTypeError {
182    pub errors: Vec<TypeError>,
183}
184
185impl AggregatedTypeError {
186    pub fn eprint(&self, env: &mut TcEnv, input: &str) -> io::Result<()> {
187        let mut input = Source::from(input);
188        for report in self.errors.iter().flat_map(|err| env.report(err)) {
189            report.eprint(&mut input)?;
190        }
191        Ok(())
192    }
193}
194
195pub trait TypeResultExt<T> {
196    fn unwrap_or_eprint(self, env: &mut TcEnv, input: &str) -> T;
197}
198
199impl<T> TypeResultExt<T> for Result<T, AggregatedTypeError> {
200    fn unwrap_or_eprint(self, env: &mut TcEnv, input: &str) -> T {
201        self.unwrap_or_else(|es| {
202            es.eprint(env, input).unwrap();
203            panic!("Failed to type check")
204        })
205    }
206}