From c7fdeaf37a5c32cb85faf86ac72ec4b6d76dedcf Mon Sep 17 00:00:00 2001 From: John Date: Fri, 1 Mar 2024 02:49:22 -0600 Subject: [PATCH] cl-typeck: Begin work on new type-checker. Comments in lib.rs outline my current thought processes --- Cargo.toml | 1 + cl-typeck/Cargo.toml | 11 +++ cl-typeck/src/lib.rs | 210 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 cl-typeck/Cargo.toml create mode 100644 cl-typeck/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 07574ed..7d38886 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "cl-repl", + "cl-typeck", "cl-interpret", "cl-structures", "cl-token", diff --git a/cl-typeck/Cargo.toml b/cl-typeck/Cargo.toml new file mode 100644 index 0000000..d5e36f8 --- /dev/null +++ b/cl-typeck/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cl-typeck" +repository.workspace = true +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +cl-ast = { path = "../cl-ast" } diff --git a/cl-typeck/src/lib.rs b/cl-typeck/src/lib.rs new file mode 100644 index 0000000..2ec5450 --- /dev/null +++ b/cl-typeck/src/lib.rs @@ -0,0 +1,210 @@ +//! # The Conlang Type Checker +//! +//! As a statically typed language, Conlang requires a robust type checker to enforce correctness. + +#![warn(clippy::all)] +#![allow(unused)] +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +use cl_ast::*; + +pub mod intern { + //! Trivially-copyable, easily comparable typed indices for type system constructs + + /// Creates newtype indices over [`usize`] for use elsewhere in the type checker + macro_rules! def_id {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$( + $(#[$meta])* + #[repr(transparent)] + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name(usize); + + impl $name { + #[doc = concat!("Constructs a [`", stringify!($name), "`] from a [`usize`] without checking bounds.")] + /// # Safety + /// The provided value should be within the bounds of its associated container + pub unsafe fn from_raw_unchecked(value: usize) -> Self { + Self(value) + } + /// Gets the index of the type by-value + pub fn value(&self) -> usize { + self.0 + } + } + + impl From< $name > for usize { + fn from(value: $name) -> Self { + value.0 + } + } + )*}} + + // define the index types + def_id! { + /// Uniquely represents a Type + TypeID, + /// Uniquely represents a Value + ValueID, + } +} + +pub mod typedef { + //! Representations of type definitions + // use std::collections::HashMap; + + use crate::intern::TypeID; + use cl_ast::{Item, Visibility}; + + /// The definition of a type + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct TypeDef { + name: String, + kind: Option, + definition: Item, + } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum TypeKind { + /// A primitive type, built-in to the compiler + Intrinsic, + /// A user-defined structural product type + Struct(Vec<(String, Visibility, TypeID)>), + /// A user-defined union-like enum type + Enum(Vec<(String, TypeID)>), + /// A type alias + Alias(TypeID), + /// The unit type + Empty, + /// The Self type + SelfTy, + // TODO: other types + } +} + +pub mod valdef { + //! Representations of value definitions + + use crate::intern::{TypeID, ValueID}; + use cl_ast::Block; + + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct ValueDef { + name: String, + kind: Option, + } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum ValueKind { + Const(), + Static(), + Fn { + args: Vec, + rety: TypeID, + body: Block, + }, + } +} + +pub mod typeinfo { + //! Stores typeck-time type inference info + + use crate::intern::TypeID; + + /// The Type struct represents all valid types, and can be trivially equality-compared + pub struct Type { + /// You can only have a pointer chain 65535 pointers long. + ref_depth: u16, + /// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete) + kind: TKind, + } + + /// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete) + pub enum TKind { + /// A Concrete type has an associated [TypeDef](super::typedef::TypeDef) + Concrete(TypeID), + /// A Generic type is a *locally unique* comparable value, + /// valid only until the end of its inference context + Generic(usize), + } +} + +pub mod type_context { + //! A type context stores a map from names to TypeIDs + + use std::collections::HashMap; + + use crate::intern::TypeID; + + pub struct TypeCtx { + parent: Option>, + concrete: HashMap, + } +} + +/* +/// What is an inference rule? +/// An inference rule is a specification with a set of predicates and a judgement + +/// Let's give every type an ID +struct TypeID(usize); + +/// Let's give every type some data: + +struct TypeDef<'def> { + name: String, + definition: &'def Item, +} + +and store them in a big vector of type descriptions: + +struct TypeMap<'def> { + types: Vec>, +} +// todo: insertion of a type should yield a TypeID +// todo: impl index with TypeID + +Let's store type information as either a concrete type or a generic type: + +/// The Type struct represents all valid types, and can be trivially equality-compared +pub struct Type { + /// You can only have a pointer chain 65535 pointers long. + ref_depth: u16, + kind: TKind, +} +pub enum TKind { + Concrete(TypeID), + Generic(usize), +} + +And assume I can specify a rule based on its inputs and outputs: + +Rule { + operation: If, + /// The inputs field is populated by + inputs: [Concrete(BOOL), Generic(0), Generic(0)], + outputs: Generic(0), + /// This rule is compiler-intrinsic! + through: None, +} + +Rule { + operation: Add, + inputs: [Concrete(I32), Concrete(I32)], + outputs: Concrete(I32), + /// This rule is not compiler-intrinsic (it is overloaded!) + through: Some(&ImplAddForI32::Add), +} + + + +These rules can be stored in some kind of rule database: + +let rules: Hashmap> { + +} + +*/ + +/* + Potential solution: + Store reference to type field of each type expression in the AST +*/