Skip to main content

dada_ir_sym/check/
types.rs

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}