1use dada_ir_ast::{
2 ast::{AstGenericTerm, AstPath, AstPathKind, AstPerm, AstPermKind, AstTy, AstTyKind},
3 diagnostic::{Diagnostic, Err, Level, ordinal},
4 span::{Span, Spanned},
5};
6use dada_util::{boxed_async_fn, indirect};
7
8use crate::{
9 check::{
10 env::Env,
11 exprs::ExprResultKind,
12 scope::{NameResolution, NameResolutionSym, Resolve},
13 },
14 ir::{
15 types::{
16 AnonymousPermSymbol, HasKind, SymGenericKind, SymGenericTerm, SymPerm, SymPermKind,
17 SymPlace, SymTy,
18 },
19 variables::FromVar,
20 },
21 prelude::Symbol,
22};
23
24use super::{CheckTyInEnv, exprs::ExprResult, member_lookup::MemberLookup};
25
26impl<'db> CheckTyInEnv<'db> for AstTy<'db> {
27 type Output = SymTy<'db>;
28
29 async fn check_in_env(&self, env: &mut Env<'db>) -> Self::Output {
30 let db = env.db();
31 indirect(async || match self.kind(db) {
32 AstTyKind::Perm(ast_perm, ast_ty) => {
33 let sym_perm = ast_perm.check_in_env(env).await;
34 let sym_ty = ast_ty.check_in_env(env).await;
35 SymTy::perm(db, sym_perm, sym_ty)
36 }
37
38 AstTyKind::Named(ast_path, ref opt_ast_generics) => {
39 let mut generics = vec![];
40 if let Some(ast_generics) = opt_ast_generics {
41 for g in ast_generics {
42 let span = g.span(db);
43 let checked = g.check_in_env(env).await;
44 generics.push((span, checked));
45 }
46 }
47 match ast_path.resolve_in(env).await {
48 Ok(r) => name_resolution_to_sym_ty(db, r, ast_path, generics),
49 Err(r) => SymTy::err(db, r),
50 }
51 }
52
53 AstTyKind::GenericDecl(decl) => {
54 let symbol = decl.symbol(db);
55 SymTy::var(db, symbol)
56 }
57 })
58 .await
59 }
60}
61
62fn name_resolution_to_sym_ty<'db>(
63 db: &'db dyn crate::Db,
64 name_resolution: NameResolution<'db>,
65 source: impl Spanned<'db>,
66 generics: Vec<(Span<'db>, SymGenericTerm<'db>)>,
67) -> SymTy<'db> {
68 match name_resolution.sym {
69 NameResolutionSym::SymPrimitive(sym_primitive) => {
70 if !generics.is_empty() {
71 return SymTy::err(
72 db,
73 Diagnostic::error(
74 db,
75 source.span(db),
76 format!(
77 "`{}` does not expect generic arguments",
78 sym_primitive.name(db)
79 ),
80 )
81 .label(
82 db,
83 Level::Error,
84 source.span(db),
85 format!(
86 "the primitive type `{}` does not expect generic arguments",
87 sym_primitive.name(db)
88 ),
89 )
90 .report(db),
91 );
92 }
93
94 SymTy::named(db, sym_primitive.into(), vec![])
95 }
96
97 NameResolutionSym::SymAggregate(sym_class) => {
98 let expected = sym_class.len_generics(db);
99 let found = generics.len();
100 if found != expected {
101 let name = sym_class.name(db);
102 return SymTy::err(
103 db,
104 Diagnostic::error(
105 db,
106 source.span(db),
107 format!("expected {expected} generic arguments, found {found}"),
108 )
109 .label(
110 db,
111 Level::Error,
112 source.span(db),
113 format!(
114 "`{name}` expects {expected} generic arguments, but I found {found}"
115 ),
116 )
117 .label(
118 db,
119 Level::Info,
120 sym_class.generics_span(db),
121 format!("generic arguments for `{name}` are declared here"),
122 )
123 .report(db),
124 );
125 }
126
127 let generics = sym_class
128 .generic_kinds(db)
129 .zip(&generics)
130 .zip(0..)
131 .map(|((expected_kind, &(span, generic)), index)| {
132 if generic.has_kind(db, expected_kind) {
133 generic
134 } else {
135 let found_kind = generic.kind().unwrap();
136 let name = sym_class.name(db);
137 SymGenericTerm::Error(
138 Diagnostic::error(
139 db,
140 span,
141 format!("expected a `{expected_kind}`, found a `{found_kind}`"),
142 )
143 .label(
144 db,
145 Level::Error,
146 span,
147 format!(
148 "`{name}` expects a `{expected_kind}` for its {ith} generic argument, but I found a `{found_kind}`",
149 ith = ordinal(index + 1),
150 ),
151 )
152 .label(
153 db,
154 Level::Info,
155 sym_class.generic_span(db, index),
156 format!(
157 "{ith} generic argument for `{name}` is declared here",
158 ith = ordinal(index + 1),
159 ),
160 )
161 .report(db)
162 )
163 }
164 })
165 .collect();
166
167 SymTy::named(db, sym_class.into(), generics)
168 }
169
170 NameResolutionSym::SymVariable(var) => {
171 if !generics.is_empty() {
172 return SymTy::err(
173 db,
174 Diagnostic::error(db, source.span(db), "generic types do not expect generic arguments")
175 .label(
176 db,
177 Level::Error,
178 source.span(db),
179 "this is the name of a generic type, but I also found a list of generic arguments",
180 )
181 .report(db),
182 );
183 }
184
185 let generic_kind = var.kind(db);
186 if generic_kind != SymGenericKind::Type {
187 return SymTy::err(
188 db,
189 Diagnostic::error(
190 db,
191 source.span(db),
192 format!("expected `type`, found `{generic_kind}`"),
193 )
194 .label(
195 db,
196 Level::Error,
197 source.span(db),
198 format!("I expected a type here, but I found a `{generic_kind}`"),
199 )
200 .report(db),
201 );
202 }
203
204 SymTy::var(db, var)
205 }
206 NameResolutionSym::SymModule(sym_module) => SymTy::err(
207 db,
208 Diagnostic::error(db, source.span(db), "modules are not valid types")
209 .label(
210 db,
211 Level::Error,
212 source.span(db),
213 format!(
214 "I expected a type here, but `{}` is a module",
215 sym_module.name(db)
216 ),
217 )
218 .report(db),
219 ),
220 NameResolutionSym::SymFunction(sym_function) => SymTy::err(
221 db,
222 Diagnostic::error(db, source.span(db), "modules are not valid types")
223 .label(
224 db,
225 Level::Error,
226 source.span(db),
227 format!(
228 "I expected a type here, but `{}` is a function",
229 sym_function.name(db)
230 ),
231 )
232 .report(db),
233 ),
234 }
235}
236
237impl<'db> CheckTyInEnv<'db> for AstGenericTerm<'db> {
238 type Output = SymGenericTerm<'db>;
239
240 async fn check_in_env(&self, env: &mut Env<'db>) -> Self::Output {
241 match *self {
242 AstGenericTerm::Ty(ast_ty) => ast_ty.check_in_env(env).await.into(),
243 AstGenericTerm::Perm(ast_perm) => ast_perm.check_in_env(env).await.into(),
244 AstGenericTerm::Id(id) => match id.resolve_in(env).await {
245 Ok(r) => name_resolution_to_generic_term(env.db(), r, id),
246 Err(r) => r.into(),
247 },
248 }
249 }
250}
251
252fn name_resolution_to_generic_term<'db>(
253 db: &'db dyn crate::Db,
254 name_resolution: NameResolution<'db>,
255 source: impl Spanned<'db>,
256) -> SymGenericTerm<'db> {
257 if let NameResolutionSym::SymVariable(var) = name_resolution.sym {
258 match var.kind(db) {
259 SymGenericKind::Type => SymGenericTerm::Type(SymTy::var(db, var)),
260 SymGenericKind::Perm => SymGenericTerm::Perm(SymPerm::var(db, var)),
261 SymGenericKind::Place => SymGenericTerm::Place(SymPlace::var(db, var)),
262 }
263 } else {
264 name_resolution_to_sym_ty(db, name_resolution, source, vec![]).into()
265 }
266}
267
268impl<'db> CheckTyInEnv<'db> for AstPerm<'db> {
269 type Output = SymPerm<'db>;
270
271 async fn check_in_env(&self, env: &mut Env<'db>) -> Self::Output {
272 let db = env.db();
273 match *self.kind(db) {
274 AstPermKind::Referenced(Some(ref paths)) => {
275 let places = paths_to_sym_places(env, paths).await;
276 SymPerm::new(db, SymPermKind::Referenced(places))
277 }
278 AstPermKind::Mutable(Some(ref paths)) => {
279 let places = paths_to_sym_places(env, paths).await;
280 SymPerm::new(db, SymPermKind::Mutable(places))
281 }
282 AstPermKind::Given(Some(ref _span_vec)) => todo!(),
283 AstPermKind::Referenced(None)
284 | AstPermKind::Mutable(None)
285 | AstPermKind::Given(None) => {
286 let sym_var = self.anonymous_perm_symbol(db);
287 SymPerm::var(db, sym_var)
288 }
289 AstPermKind::My => SymPerm::my(db),
290 AstPermKind::Our => SymPerm::our(db),
291 AstPermKind::Variable(id) => match id.resolve_in(env).await {
292 Ok(r) => name_resolution_to_sym_perm(db, r, id),
293 Err(r) => SymPerm::err(db, r),
294 },
295 AstPermKind::GenericDecl(ast_generic_decl) => {
296 let symbol = ast_generic_decl.symbol(db);
297 SymPerm::var(db, symbol)
298 }
299 }
300 }
301}
302
303fn name_resolution_to_sym_perm<'db>(
304 db: &'db dyn crate::Db,
305 name_resolution: NameResolution<'db>,
306 source: impl Spanned<'db>,
307) -> SymPerm<'db> {
308 match name_resolution.sym {
309 NameResolutionSym::SymVariable(sym_variable)
310 if sym_variable.has_kind(db, SymGenericKind::Perm) =>
311 {
312 SymPerm::var(db, sym_variable)
313 }
314
315 NameResolutionSym::SymModule(_)
316 | NameResolutionSym::SymAggregate(_)
317 | NameResolutionSym::SymFunction(_)
318 | NameResolutionSym::SymVariable(_)
319 | NameResolutionSym::SymPrimitive(_) => SymPerm::err(
320 db,
321 Diagnostic::error(
322 db,
323 source.span(db),
324 format!(
325 "expected permission, found {}",
326 name_resolution.sym.categorize(db)
327 ),
328 )
329 .label(
330 db,
331 Level::Error,
332 source.span(db),
333 format!(
334 "I expected a permission, but I found {}",
335 name_resolution.sym.describe(db)
336 ),
337 )
338 .report(db),
339 ),
340 }
341}
342
343async fn paths_to_sym_places<'db>(
344 env: &mut Env<'db>,
345 paths: &[AstPath<'db>],
346) -> Vec<SymPlace<'db>> {
347 let mut places = vec![];
348 for &path in paths {
349 places.push(path_to_sym_place(env, path).await);
350 }
351 places
352}
353
354async fn path_to_sym_place<'db>(env: &mut Env<'db>, path: AstPath<'db>) -> SymPlace<'db> {
355 let db = env.db();
356 let ExprResult {
357 temporaries,
358 span,
359 kind,
360 } = path_to_expr_result(env, path).await;
361
362 assert!(temporaries.is_empty());
363
364 match kind {
365 ExprResultKind::PlaceExpr(expr) => expr.into_sym_place(db),
366 _ => SymPlace::err(
367 db,
368 Diagnostic::error(db, span, "expected a place, found something else")
369 .label(
370 db,
371 Level::Error,
372 span,
373 "I expected a place, but I found something else",
374 )
375 .report(db),
376 ),
377 }
378}
379
380#[boxed_async_fn]
381async fn path_to_expr_result<'db>(env: &mut Env<'db>, path: AstPath<'db>) -> ExprResult<'db> {
382 let db = env.db();
383 match *path.kind(env.db()) {
384 AstPathKind::Identifier(id) => {
385 let nr = match id.resolve_in(env).await {
386 Ok(nr) => nr,
387 Err(r) => return ExprResult::err(db, r),
388 };
389 ExprResult::from_name_resolution(env, nr, id.span(db)).await
390 }
391 AstPathKind::GenericArgs { .. } => ExprResult::err(
392 db,
393 Diagnostic::error(db, path.span(db), "generic arguments are not valid places")
394 .label(
395 db,
396 Level::Error,
397 path.span(db),
398 "I expected a place, but I found generic arguments",
399 )
400 .report(db),
401 ),
402 AstPathKind::Member { path, id } => {
403 let owner = path_to_expr_result(env, path).await;
404 MemberLookup::new(env).lookup_member(owner, id).await
405 }
406 }
407}