Skip to content

Commit 3e99d03

Browse files
committed
Initial instance member checking
This also implements several subsystems required to get instance member checking working, including but not limited to: - Specialising class members based on instance arguments - Capturing and rebinding implicit instance variables - Constraints added to class members, not the instance head - Errors for mismatched instance heads and member types - Tracking both explicit and quantified variables for class kinds
1 parent 9c939c7 commit 3e99d03

File tree

27 files changed

+799
-65
lines changed

27 files changed

+799
-65
lines changed

compiler-core/checking/src/algorithm.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ use std::slice;
5252

5353
use building_types::QueryResult;
5454
use files::FileId;
55+
use indexing::TermItemKind;
5556
use itertools::Itertools;
5657
use lowering::{DataIr, NewtypeIr, Scc, TermItemIr, TypeItemIr};
5758

@@ -66,8 +67,9 @@ pub fn check_source(queries: &impl ExternalQueries, file_id: FileId) -> QueryRes
6667
check_type_items(&mut state, &context)?;
6768

6869
check_term_signatures(&mut state, &context)?;
69-
check_instances(&mut state, &context)?;
70+
check_instance_heads(&mut state, &context)?;
7071
check_value_groups(&mut state, &context)?;
72+
check_instance_members(&mut state, &context)?;
7173

7274
Ok(state.checked)
7375
}
@@ -267,7 +269,7 @@ where
267269
Ok(())
268270
}
269271

270-
fn check_instances<Q>(
272+
fn check_instance_heads<Q>(
271273
state: &mut state::CheckState,
272274
context: &state::CheckContext<Q>,
273275
) -> QueryResult<()>
@@ -295,6 +297,55 @@ where
295297
Ok(())
296298
}
297299

300+
fn check_instance_members<Q>(
301+
state: &mut state::CheckState,
302+
context: &state::CheckContext<Q>,
303+
) -> QueryResult<()>
304+
where
305+
Q: ExternalQueries,
306+
{
307+
let items = context.lowered.term_scc.iter().flat_map(|scc| match scc {
308+
Scc::Base(item) | Scc::Recursive(item) => slice::from_ref(item),
309+
Scc::Mutual(items) => items.as_slice(),
310+
});
311+
312+
for &item_id in items {
313+
let Some(TermItemIr::Instance { members, resolution, .. }) =
314+
context.lowered.info.get_term_item(item_id)
315+
else {
316+
continue;
317+
};
318+
319+
let Some((class_file, class_id)) = *resolution else {
320+
continue;
321+
};
322+
323+
let TermItemKind::Instance { id: instance_id } = context.indexed.items[item_id].kind else {
324+
continue;
325+
};
326+
327+
let Some(instance) = state.checked.instances.get(&instance_id) else {
328+
continue;
329+
};
330+
331+
let instance_arguments = instance.arguments.clone();
332+
let instance_constraints = instance.constraints.clone();
333+
334+
let check_members = term_item::CheckInstanceMembers {
335+
instance_id: item_id,
336+
members,
337+
class_file,
338+
class_id,
339+
instance_arguments: &instance_arguments,
340+
instance_constraints: &instance_constraints,
341+
};
342+
343+
term_item::check_instance_members(state, context, check_members)?;
344+
}
345+
346+
Ok(())
347+
}
348+
298349
fn check_value_groups<Q>(
299350
state: &mut state::CheckState,
300351
context: &state::CheckContext<Q>,

compiler-core/checking/src/algorithm/constraint.rs

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::algorithm::fold::{FoldAction, TypeFold, fold_type};
2121
use crate::algorithm::state::{CheckContext, CheckState};
2222
use crate::algorithm::{transfer, unification};
2323
use crate::core::{Class, Instance, Variable, debruijn};
24-
use crate::{ExternalQueries, Type, TypeId};
24+
use crate::{CheckedModule, ExternalQueries, Type, TypeId};
2525

2626
pub fn solve_constraints<Q>(
2727
state: &mut CheckState,
@@ -197,22 +197,23 @@ where
197197
return Ok(());
198198
}
199199

200-
let Some(class_info) = get_class_info(state, context, file_id, item_id)? else {
200+
let Some(class_info) = lookup_file_class(state, context, file_id, item_id)? else {
201201
return Ok(());
202202
};
203203

204204
if class_info.superclasses.is_empty() {
205205
return Ok(());
206206
}
207207

208+
let initial_level = class_info.quantified_variables.0 + class_info.kind_variables.0;
208209
let mut bindings = FxHashMap::default();
209-
for (&level, &argument) in class_info.variable_levels.iter().zip(&arguments) {
210+
for (index, &argument) in arguments.iter().enumerate() {
211+
let level = debruijn::Level(initial_level + index as u32);
210212
bindings.insert(level, argument);
211213
}
212214

213-
for &(superclass, _) in &class_info.superclasses {
214-
let localized = transfer::localize(state, context, superclass);
215-
let substituted = ApplyBindings::on(state, &bindings, localized);
215+
for &(superclass, _) in class_info.superclasses.iter() {
216+
let substituted = ApplyBindings::on(state, &bindings, superclass);
216217
constraints.push(substituted);
217218
aux(state, context, substituted, constraints, seen)?;
218219
}
@@ -237,15 +238,15 @@ where
237238
state
238239
.checked
239240
.instances
240-
.iter()
241+
.values()
241242
.filter(|instance| instance.resolution.1 == item_id)
242243
.cloned()
243244
.collect_vec()
244245
} else {
245246
let checked = context.queries.checked(file_id)?;
246247
checked
247248
.instances
248-
.iter()
249+
.values()
249250
.filter(|instance| instance.resolution.1 == item_id)
250251
.cloned()
251252
.collect_vec()
@@ -264,8 +265,53 @@ where
264265
Ok(result)
265266
}
266267

267-
pub(crate) fn get_class_info<Q>(
268-
state: &CheckState,
268+
fn localize_class<Q>(state: &mut CheckState, context: &CheckContext<Q>, class: &Class) -> Class
269+
where
270+
Q: ExternalQueries,
271+
{
272+
let superclasses = class.superclasses.iter().map(|&(t, k)| {
273+
let t = transfer::localize(state, context, t);
274+
let k = transfer::localize(state, context, k);
275+
(t, k)
276+
});
277+
Class {
278+
superclasses: superclasses.collect(),
279+
quantified_variables: class.quantified_variables,
280+
kind_variables: class.kind_variables,
281+
}
282+
}
283+
284+
fn lookup_local_class<Q>(
285+
state: &mut CheckState,
286+
context: &CheckContext<Q>,
287+
item_id: TypeItemId,
288+
) -> Option<Class>
289+
where
290+
Q: ExternalQueries,
291+
{
292+
if let Some(class) = state.binding_group.lookup_class(item_id) {
293+
Some(class)
294+
} else {
295+
let class = state.checked.lookup_class(item_id)?;
296+
Some(localize_class(state, context, &class))
297+
}
298+
}
299+
300+
fn lookup_global_class<Q>(
301+
state: &mut CheckState,
302+
context: &CheckContext<Q>,
303+
checked: &CheckedModule,
304+
item_id: TypeItemId,
305+
) -> Option<Class>
306+
where
307+
Q: ExternalQueries,
308+
{
309+
let class = checked.classes.get(&item_id)?;
310+
Some(localize_class(state, context, class))
311+
}
312+
313+
pub(crate) fn lookup_file_class<Q>(
314+
state: &mut CheckState,
269315
context: &CheckContext<Q>,
270316
file_id: FileId,
271317
item_id: TypeItemId,
@@ -274,10 +320,10 @@ where
274320
Q: ExternalQueries,
275321
{
276322
if file_id == context.id {
277-
Ok(state.checked.classes.get(&item_id).cloned())
323+
Ok(lookup_local_class(state, context, item_id))
278324
} else {
279325
let checked = context.queries.checked(file_id)?;
280-
Ok(checked.classes.get(&item_id).cloned())
326+
Ok(lookup_global_class(state, context, &checked, item_id))
281327
}
282328
}
283329

compiler-core/checking/src/algorithm/state.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hash::FxHashMap;
1717
use sugar::{Bracketed, Sectioned};
1818

1919
use crate::algorithm::{constraint, quantify, transfer};
20-
use crate::core::{ForallBinder, Synonym, Type, TypeId, TypeInterner, debruijn};
20+
use crate::core::{Class, ForallBinder, Synonym, Type, TypeId, TypeInterner, debruijn};
2121
use crate::error::{CheckError, ErrorKind, ErrorStep};
2222
use crate::{CheckedModule, ExternalQueries};
2323

@@ -79,6 +79,26 @@ impl TypeScope {
7979
self.kinds.unbind(level);
8080
}
8181

82+
/// Unbinds variables starting from a level and returns captured implicit bindings.
83+
///
84+
/// This is used when checking instances to capture the implicit type variables
85+
/// from the instance head before unbinding, so they can be rebound when checking
86+
/// instance members.
87+
pub fn unbind_implicits(&mut self, level: debruijn::Level) -> Vec<InstanceHeadBinding> {
88+
let mut implicits = vec![];
89+
90+
for (level, variable) in self.bound.iter_from(level) {
91+
if let debruijn::Variable::Implicit { node, id } = variable
92+
&& let Some(&kind) = self.kinds.get(level)
93+
{
94+
implicits.push(InstanceHeadBinding { node, id, kind });
95+
}
96+
}
97+
98+
self.unbind(level);
99+
implicits
100+
}
101+
82102
pub fn size(&self) -> debruijn::Size {
83103
self.bound.size()
84104
}
@@ -127,6 +147,17 @@ impl TermScope {
127147
}
128148
}
129149

150+
/// A single implicit variable captured from an instance head.
151+
///
152+
/// Instance heads like `instance Show a => Show (Array a)` introduce
153+
/// implicit type variables that need to be visible when checking instance
154+
/// member implementations.
155+
pub struct InstanceHeadBinding {
156+
pub node: GraphNodeId,
157+
pub id: ImplicitBindingId,
158+
pub kind: TypeId,
159+
}
160+
130161
/// Tracks type variables declared in surface syntax.
131162
///
132163
/// The type checker checks kind/type declarations first before equation
@@ -158,6 +189,7 @@ pub struct SurfaceBindings {
158189
pub term_item: FxHashMap<TermItemId, Arc<[TypeVariableBindingId]>>,
159190
pub type_item: FxHashMap<TypeItemId, Arc<[TypeVariableBindingId]>>,
160191
pub let_binding: FxHashMap<LetBindingNameGroupId, Arc<[TypeVariableBindingId]>>,
192+
pub instance_head: FxHashMap<TermItemId, Arc<[InstanceHeadBinding]>>,
161193
}
162194

163195
impl SurfaceBindings {
@@ -184,6 +216,14 @@ impl SurfaceBindings {
184216
pub fn get_let(&self, id: LetBindingNameGroupId) -> Option<Arc<[TypeVariableBindingId]>> {
185217
self.let_binding.get(&id).cloned()
186218
}
219+
220+
pub fn insert_instance_head(&mut self, id: TermItemId, v: Arc<[InstanceHeadBinding]>) {
221+
self.instance_head.insert(id, v);
222+
}
223+
224+
pub fn get_instance_head(&self, id: TermItemId) -> Option<Arc<[InstanceHeadBinding]>> {
225+
self.instance_head.get(&id).cloned()
226+
}
187227
}
188228

189229
/// Collects wanted and given constraints.
@@ -262,6 +302,7 @@ pub struct BindingGroupContext {
262302
pub terms: FxHashMap<TermItemId, TypeId>,
263303
pub types: FxHashMap<TypeItemId, TypeId>,
264304
pub synonyms: FxHashMap<TypeItemId, Synonym>,
305+
pub classes: FxHashMap<TypeItemId, Class>,
265306
pub residual: FxHashMap<TermItemId, Vec<TypeId>>,
266307
pub data: FxHashMap<TypeItemId, CheckedDataLike>,
267308
}
@@ -278,6 +319,10 @@ impl BindingGroupContext {
278319
pub fn lookup_synonym(&self, id: TypeItemId) -> Option<Synonym> {
279320
self.synonyms.get(&id).copied()
280321
}
322+
323+
pub fn lookup_class(&self, id: TypeItemId) -> Option<Class> {
324+
self.classes.get(&id).cloned()
325+
}
281326
}
282327

283328
/// The core environment structure threaded through the [`algorithm`].
@@ -622,9 +667,14 @@ impl CheckState {
622667
}
623668
}
624669

670+
let mut classes = mem::take(&mut self.binding_group.classes);
625671
for (item_id, type_id) in mem::take(&mut self.binding_group.types) {
626-
if let Some((type_id, _)) = quantify::quantify(self, type_id) {
627-
let type_id = transfer::globalize(self, context, type_id);
672+
if let Some((quantified_type, quantified_count)) = quantify::quantify(self, type_id) {
673+
if let Some(mut class) = classes.remove(&item_id) {
674+
class.quantified_variables = quantified_count;
675+
self.checked.classes.insert(item_id, class);
676+
}
677+
let type_id = transfer::globalize(self, context, quantified_type);
628678
self.checked.types.insert(item_id, type_id);
629679
}
630680
}

compiler-core/checking/src/algorithm/term.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ where
3131
Ok(())
3232
}
3333

34-
fn infer_equations_core<Q>(
34+
pub fn infer_equations_core<Q>(
3535
state: &mut CheckState,
3636
context: &CheckContext<Q>,
3737
group_type: TypeId,

0 commit comments

Comments
 (0)