Skip to main content

dada_check/
lib.rs

1//! Type checking orchestration for Dada programs.
2#![doc = include_str!("../docs/overview.md")]
3
4use dada_ir_ast::{
5    ast::Identifier,
6    diagnostic::{Diagnostic, Level},
7    inputs::SourceFile,
8    span::Spanned,
9};
10use dada_ir_sym::{
11    ir::{
12        binder::{Binder, BoundTerm},
13        classes::{SymAggregate, SymClassMember, SymField},
14        functions::{SignatureSymbols, SymFunction, SymFunctionSignature, SymInputOutput},
15        generics::{SymWhereClause, SymWhereClauseKind},
16        module::{SymItem, SymModule},
17        types::{SymGenericKind, SymGenericTerm, SymPerm, SymPlace, SymTy},
18        variables::SymVariable,
19    },
20    prelude::*,
21};
22
23pub use dada_ir_sym::Db;
24use dada_util::Map;
25
26pub mod prelude {
27    pub use crate::Check;
28}
29
30/// The main "check" routine. This defines what it means for a dada program to successfully compile.
31pub trait Check<'db> {
32    fn check(&self, db: &'db dyn crate::Db);
33}
34
35impl<'db, T: Check<'db>> Check<'db> for Option<T> {
36    fn check(&self, db: &'db dyn crate::Db) {
37        if let Some(t) = self {
38            t.check(db);
39        }
40    }
41}
42
43impl<'db> Check<'db> for SourceFile {
44    fn check(&self, db: &'db dyn crate::Db) {
45        self.symbol(db).check(db);
46    }
47}
48
49impl<'db> Check<'db> for SymModule<'db> {
50    fn check(&self, db: &'db dyn crate::Db) {
51        self.items(db).for_each(|item| item.check(db));
52        self.check_use_items(db);
53    }
54}
55
56impl<'db> Check<'db> for SymItem<'db> {
57    fn check(&self, db: &'db dyn crate::Db) {
58        match self {
59            SymItem::SymClass(sym_class) => sym_class.check(db),
60            SymItem::SymFunction(sym_function) => sym_function.check(db),
61            SymItem::SymPrimitive(_sym_primtive) => (),
62        }
63    }
64}
65
66impl<'db> Check<'db> for SymAggregate<'db> {
67    fn check(&self, db: &'db dyn crate::Db) {
68        self.members(db).iter().for_each(|member| member.check(db));
69    }
70}
71
72impl<'db> Check<'db> for SymClassMember<'db> {
73    fn check(&self, db: &'db dyn crate::Db) {
74        match self {
75            SymClassMember::SymField(sym_field) => sym_field.check(db),
76            SymClassMember::SymFunction(sym_function) => sym_function.check(db),
77        }
78    }
79}
80
81impl<'db> Check<'db> for SymField<'db> {
82    fn check(&self, db: &'db dyn crate::Db) {
83        self.checked_field_ty(db);
84    }
85}
86
87impl<'db> Check<'db> for SymFunction<'db> {
88    fn check(&self, db: &'db dyn crate::Db) {
89        let _ = self.checked_signature(db);
90        self.checked_body(db);
91    }
92}
93
94impl<'db> Check<'db> for SymFunctionSignature<'db> {
95    fn check(&self, db: &'db dyn crate::Db) {
96        self.symbols(db).check(db);
97
98        self.input_output(db).check(db);
99    }
100}
101
102impl<'db> Check<'db> for SignatureSymbols<'db> {
103    fn check(&self, db: &'db dyn crate::Db) {
104        let mut variable_names = Map::default();
105        for &variable in self.generic_variables.iter().chain(&self.input_variables) {
106            variable.check(db);
107
108            if let Some(id) = variable.name(db) {
109                check_for_duplicates(db, &mut variable_names, id, variable);
110            }
111        }
112    }
113}
114
115fn check_for_duplicates<'db, S: Spanned<'db>>(
116    db: &'db dyn crate::Db,
117    map: &mut Map<Identifier<'db>, S>,
118    id: Identifier<'db>,
119    value: S,
120) {
121    if let Some(other_input) = map.get(&id) {
122        Diagnostic::error(
123            db,
124            value.span(db),
125            format!("duplicate parameter name `{id}`"),
126        )
127        .label(
128            db,
129            Level::Error,
130            value.span(db),
131            "all parameters must have unique names, but this parameter has the same name as another parameter",
132        )
133        .label(
134            db,
135            Level::Info,
136            other_input.span(db),
137            "the previous parameter is here",
138        )
139        .report(db);
140    }
141
142    map.insert(id, value);
143}
144
145impl<'db> Check<'db> for SymInputOutput<'db> {
146    fn check(&self, db: &'db dyn crate::Db) {
147        let SymInputOutput {
148            input_tys,
149            output_ty,
150            where_clauses,
151        } = self;
152        input_tys.check(db);
153        output_ty.check(db);
154        where_clauses.check(db);
155    }
156}
157
158impl<'db, C: Check<'db>> Check<'db> for Vec<C> {
159    fn check(&self, db: &'db dyn crate::Db) {
160        for item in self {
161            item.check(db);
162        }
163    }
164}
165
166impl<'db> Check<'db> for SymTy<'db> {
167    fn check(&self, _db: &'db dyn crate::Db) {
168        // There *are* validity checks that need to be done on types,
169        // but they are done as part of the checking the item in which
170        // the type appears.
171    }
172}
173
174impl<'db, C: Check<'db> + BoundTerm<'db>> Check<'db> for Binder<'db, C> {
175    fn check(&self, db: &'db dyn crate::Db) {
176        for sym in &self.variables {
177            sym.check(db);
178        }
179        self.bound_value.check(db);
180    }
181}
182
183impl<'db> Check<'db> for SymGenericKind {
184    fn check(&self, _db: &'db dyn crate::Db) {}
185}
186
187impl<'db> Check<'db> for SymVariable<'db> {
188    fn check(&self, _db: &'db dyn crate::Db) {
189        // There *are* validity checks that need to be done on types,
190        // but they are done as part of the checking the item in which
191        // the type appears.
192    }
193}
194
195impl<'db> Check<'db> for SymWhereClause<'db> {
196    fn check(&self, db: &'db dyn crate::Db) {
197        self.subject(db).check(db);
198        self.kind(db).check(db);
199    }
200}
201
202impl<'db> Check<'db> for SymWhereClauseKind {
203    fn check(&self, _db: &'db dyn crate::Db) {
204        match self {
205            SymWhereClauseKind::Unique => (),
206            SymWhereClauseKind::Shared => (),
207            SymWhereClauseKind::Owned => (),
208            SymWhereClauseKind::Lent => (),
209        }
210    }
211}
212
213impl<'db> Check<'db> for SymGenericTerm<'db> {
214    fn check(&self, db: &'db dyn crate::Db) {
215        match self {
216            SymGenericTerm::Type(sym_ty) => sym_ty.check(db),
217            SymGenericTerm::Perm(sym_perm) => sym_perm.check(db),
218            SymGenericTerm::Place(sym_place) => sym_place.check(db),
219            SymGenericTerm::Error(_) => (),
220        }
221    }
222}
223
224impl<'db> Check<'db> for SymPerm<'db> {
225    fn check(&self, _db: &'db dyn crate::Db) {
226        // There *are* validity checks that need to be done on permissions,
227        // but they are done as part of the checking the item in which
228        // the permission appears.
229    }
230}
231
232impl<'db> Check<'db> for SymPlace<'db> {
233    fn check(&self, _db: &'db dyn crate::Db) {
234        // There *are* validity checks that need to be done on places,
235        // but they are done as part of the checking the item in which
236        // the place appears.
237    }
238}