prism_compiler/lang/
error.rs1use 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}