aboutsummaryrefslogtreecommitdiff
path: root/src/checker_state.rs
diff options
context:
space:
mode:
authortslil <tslil@posteo.de>2026-04-24 10:44:41 +0100
committertslil <tslil@posteo.de>2026-04-24 12:52:34 +0100
commit36a9377163ecd3300f7b9ad3c4d8e93d97ce41ff (patch)
treef1ced8aec7fddeaafb336363d29402750457bbc3 /src/checker_state.rs
parent7152f09f199f440e38263fdafb39b9eda71d7c53 (diff)
computing cases
Diffstat (limited to 'src/checker_state.rs')
-rw-r--r--src/checker_state.rs233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/checker_state.rs b/src/checker_state.rs
new file mode 100644
index 0000000..c4b4a3a
--- /dev/null
+++ b/src/checker_state.rs
@@ -0,0 +1,233 @@
+use crate::ast::*;
+use tracing::instrument;
+
+use derive_more::Display;
+use std::collections::HashMap;
+use std::fmt;
+
+#[derive(Display)]
+pub enum CheckerError {
+ #[display("Unbound: {_0}")]
+ Unbound(String),
+ #[display("Rebinding: {_0}")]
+ Rebinding(String),
+ #[display("The following functionality is unimplemented: {_0}")]
+ Unimplemented(String),
+ #[display("Element claimed to belong to {_0} but actually belongs to {_1}")]
+ WrongSetForElement(Set, Set),
+ #[display("Element {element} does belong to set {claimed}: {reason}")]
+ ElementDoesNotBelong {
+ element: Element,
+ claimed: Set,
+ reason: String,
+ },
+ #[display("Case analysis {_0} does not have consistent set for scrutinee")]
+ IncosistentCaseScrutineeSet(Element),
+ #[display("Incomplete case analysis: covered [{}] but required [{}]",
+ found.join(", "),
+ required.join(", ")
+ )]
+ IncompleteCaseAnalysis {
+ found: Vec<String>,
+ required: Vec<String>,
+ },
+}
+
+#[derive(Display, Clone)]
+#[display("{field_set} @ {owner_set}")]
+pub struct SetField {
+ pub field_set: Set,
+ pub owner_set: Set,
+}
+
+#[derive(Display, Clone)]
+#[display("{element} : {set}")]
+pub struct CheckedElement {
+ pub element: Element,
+ pub set: Set,
+}
+
+#[derive(Default, Clone)]
+pub struct CheckerState {
+ wf_sets: HashMap<String, Set>,
+ wf_elements: HashMap<String, CheckedElement>,
+ wf_signatures: HashMap<String, Signature>,
+ wf_instances: HashMap<String, Instance>,
+ record_fields: HashMap<String, SetField>,
+ variant_fields: HashMap<String, SetField>,
+}
+
+impl fmt::Display for CheckerState {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fn section<K, V>(f: &mut fmt::Formatter<'_>, name: &str, map: &HashMap<K, V>) -> fmt::Result
+ where
+ K: fmt::Display + Ord,
+ V: fmt::Display,
+ {
+ writeln!(f, " {name} = {{")?;
+ let mut entries: Vec<_> = map.iter().collect();
+ entries.sort_by(|a, b| a.0.cmp(b.0));
+ for (k, v) in entries {
+ writeln!(f, " {k} ~> {v},")?;
+ }
+ writeln!(f, " }},")
+ }
+
+ writeln!(f, "CheckState {{")?;
+ section(f, "sets", &self.wf_sets)?;
+ section(f, "elements", &self.wf_elements)?;
+ section(f, "record_fields", &self.record_fields)?;
+ section(f, "variant_fields", &self.variant_fields)?;
+ section(f, "signatures", &self.wf_signatures)?;
+ section(f, "instances", &self.wf_instances)?;
+ writeln!(f, " }}")?;
+ Ok(())
+ }
+}
+
+// The invariant we're maintaining is that everything is fully evaluated before
+// we commit it to be stored in the state.
+impl CheckerState {
+ // Because of our invariant we don't actually need to do anything
+ // non-trivial here.
+ #[instrument(skip(self), level = "debug", fields(%set_a, %set_b))]
+ pub fn set_equal(&self, set_a: &Set, set_b: &Set) -> bool {
+ set_a == set_b
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn assert_unbound_set(&self, name: &String) -> Result<(), CheckerError> {
+ if self.wf_sets.contains_key(name) {
+ Err(CheckerError::Rebinding(name.clone()))
+ } else {
+ Ok(())
+ }
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn assert_unbound_element(&self, name: &String) -> Result<(), CheckerError> {
+ if self.wf_elements.contains_key(name) {
+ Err(CheckerError::Rebinding(name.clone()))
+ } else {
+ Ok(())
+ }
+ }
+
+ #[instrument(skip(self), level = "debug", fields(%name, %set_ref, %belongs_to))]
+ fn assert_correct_owner(
+ &self,
+ name: &String,
+ set_ref: &SetField,
+ belongs_to: &Set,
+ ) -> Result<(), CheckerError> {
+ if !self.set_equal(&set_ref.owner_set, belongs_to) {
+ Err(CheckerError::Rebinding(name.clone()))
+ } else {
+ Ok(())
+ }
+ }
+
+ #[instrument(skip(self), level = "debug", fields(%name, %field_set, %owner_set))]
+ fn add_record_field(
+ &mut self,
+ name: &String,
+ field_set: &Set,
+ owner_set: &Set,
+ ) -> Result<(), CheckerError> {
+ if let Some(set_ref) = self.record_fields.get(name) {
+ self.assert_correct_owner(name, set_ref, owner_set)?;
+ };
+ self.record_fields.insert(
+ name.clone(),
+ SetField {
+ field_set: field_set.clone(),
+ owner_set: owner_set.clone(),
+ },
+ );
+ Ok(())
+ }
+
+ #[instrument(skip(self), level = "debug", fields(%name, %field_set, %owner_set))]
+ fn add_variant_field(
+ &mut self,
+ name: &String,
+ field_set: &Set,
+ owner_set: &Set,
+ ) -> Result<(), CheckerError> {
+ if let Some(set_ref) = self.variant_fields.get(name) {
+ self.assert_correct_owner(name, set_ref, owner_set)?;
+ };
+ self.variant_fields.insert(
+ name.clone(),
+ SetField {
+ field_set: field_set.clone(),
+ owner_set: owner_set.clone(),
+ },
+ );
+ Ok(())
+ }
+
+ #[instrument(skip(self), level = "debug", fields(%name, %set))]
+ pub fn add_set(&mut self, name: &String, set: Set) -> Result<(), CheckerError> {
+ self.assert_unbound_set(name)?;
+ match &set {
+ Set::Record(fields) => {
+ for RecordField {
+ name: rfn,
+ set: field_set,
+ } in fields
+ {
+ self.add_record_field(rfn, field_set, &set)?;
+ }
+ }
+ Set::Variant(fields) => {
+ for VariantField {
+ name: vfn,
+ set: field_set,
+ } in fields
+ {
+ self.add_variant_field(vfn, field_set, &set)?;
+ }
+ }
+ _ => (),
+ };
+ self.wf_sets.insert(name.clone(), set);
+ Ok(())
+ }
+
+ pub fn add_element(
+ &mut self,
+ name: String,
+ element: Element,
+ set: Set,
+ ) -> Result<(), CheckerError> {
+ self.assert_unbound_element(&name)?;
+ self.wf_elements
+ .insert(name, CheckedElement { element, set });
+ Ok(())
+ }
+
+ pub fn lookup_set(&self, name: &String) -> Result<&Set, CheckerError> {
+ self.wf_sets
+ .get(name)
+ .map_or(Err(CheckerError::Unbound(name.clone())), Ok)
+ }
+
+ pub fn lookup_element(&self, name: &String) -> Result<&CheckedElement, CheckerError> {
+ self.wf_elements
+ .get(name)
+ .map_or(Err(CheckerError::Unbound(name.clone())), Ok)
+ }
+
+ pub fn lookup_record_field(&self, name: &String) -> Result<&SetField, CheckerError> {
+ self.record_fields
+ .get(name)
+ .map_or(Err(CheckerError::Unbound(name.clone())), Ok)
+ }
+
+ pub fn lookup_variant_field(&self, name: &String) -> Result<&SetField, CheckerError> {
+ self.variant_fields
+ .get(name)
+ .map_or(Err(CheckerError::Unbound(name.clone())), Ok)
+ }
+}