Skip to content

Commit

Permalink
Split typing.rs into abstract_type.rs and concrete_type.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
VonTum committed May 22, 2024
1 parent 70e4e0c commit b3fdbaa
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 338 deletions.
121 changes: 121 additions & 0 deletions src/abstract_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::ops::{Deref, Index};

use crate::{errors::ErrorCollector, file_position::{Span, SpanFile}, flattening::{BinaryOperator, UnaryOperator}, linker::{get_builtin_type, Linkable, NamedType, TypeUUID}};

/// This contains only the information that can be easily type-checked.
///
/// Its most important components are the names and structure of types.
///
/// What isn't included are the parameters of types. So Array Sizes for example.
#[derive(Debug, Clone)]
pub enum AbstractType {
Error,
Unknown,
Named(TypeUUID),
Array(Box<AbstractType>)
}

impl AbstractType {
pub fn to_string<TypVec : Index<TypeUUID, Output = NamedType>>(&self, linker_types : &TypVec) -> String {
match self {
AbstractType::Error => {
"{error}".to_owned()
}
AbstractType::Unknown => {
"{unknown}".to_owned()
}
AbstractType::Named(id) => {
linker_types[*id].get_full_name()
}
AbstractType::Array(sub) => sub.deref().to_string(linker_types) + "[]",
}
}
pub fn contains_error_or_unknown<const CHECK_ERROR : bool, const CHECK_UNKNOWN : bool>(&self) -> bool {
match self {
AbstractType::Error => CHECK_ERROR,
AbstractType::Unknown => CHECK_UNKNOWN,
AbstractType::Named(_id) => false,
AbstractType::Array(arr_box) => {
arr_box.deref().contains_error_or_unknown::<CHECK_ERROR, CHECK_UNKNOWN>()
}
}
}
}


pub const BOOL_TYPE : AbstractType = AbstractType::Named(get_builtin_type("bool"));
pub const INT_TYPE : AbstractType = AbstractType::Named(get_builtin_type("int"));
const ERROR_TYPE : AbstractType = AbstractType::Error;

pub fn typecheck_unary_operator<TypVec : Index<TypeUUID, Output = NamedType>>(op : UnaryOperator, input_typ : &AbstractType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) -> AbstractType {
if op == UnaryOperator::Not {
typecheck(input_typ, span, &BOOL_TYPE, "! input", linker_types, None, errors);
BOOL_TYPE
} else if op == UnaryOperator::Negate {
typecheck(input_typ, span, &INT_TYPE, "- input", linker_types, None, errors);
INT_TYPE
} else {
let gather_type = match op {
UnaryOperator::And => BOOL_TYPE,
UnaryOperator::Or => BOOL_TYPE,
UnaryOperator::Xor => BOOL_TYPE,
UnaryOperator::Sum => INT_TYPE,
UnaryOperator::Product => INT_TYPE,
_ => unreachable!()
};
let arr_content_typ = typecheck_is_array_indexer(input_typ, span, linker_types, errors);
typecheck(arr_content_typ, span, &gather_type, &format!("{op} input"), linker_types, None, errors);

gather_type
}
}
pub fn get_binary_operator_types(op : BinaryOperator) -> ((AbstractType, AbstractType), AbstractType) {
match op {
BinaryOperator::And => ((BOOL_TYPE, BOOL_TYPE), BOOL_TYPE),
BinaryOperator::Or => ((BOOL_TYPE, BOOL_TYPE), BOOL_TYPE),
BinaryOperator::Xor => ((BOOL_TYPE, BOOL_TYPE), BOOL_TYPE),
BinaryOperator::Add => ((INT_TYPE, INT_TYPE), INT_TYPE),
BinaryOperator::Subtract => ((INT_TYPE, INT_TYPE), INT_TYPE),
BinaryOperator::Multiply => ((INT_TYPE, INT_TYPE), INT_TYPE),
BinaryOperator::Divide => ((INT_TYPE, INT_TYPE), INT_TYPE),
BinaryOperator::Modulo => ((INT_TYPE, INT_TYPE), INT_TYPE),
BinaryOperator::Equals => ((INT_TYPE, INT_TYPE), BOOL_TYPE),
BinaryOperator::NotEquals => ((INT_TYPE, INT_TYPE), BOOL_TYPE),
BinaryOperator::GreaterEq => ((INT_TYPE, INT_TYPE), BOOL_TYPE),
BinaryOperator::Greater => ((INT_TYPE, INT_TYPE), BOOL_TYPE),
BinaryOperator::LesserEq => ((INT_TYPE, INT_TYPE), BOOL_TYPE),
BinaryOperator::Lesser => ((INT_TYPE, INT_TYPE), BOOL_TYPE),
}
}

fn type_compare(expected : &AbstractType, found : &AbstractType) -> bool {
match (expected, found) {
(AbstractType::Named(exp), AbstractType::Named(fnd)) => exp == fnd,
(AbstractType::Array(exp), AbstractType::Array(fnd)) => {
type_compare(&exp.deref(), &fnd.deref())
}
(AbstractType::Error, _) | (_, AbstractType::Error) => true, // Just assume correct, because the other side has an error
(AbstractType::Unknown, _) | (_, AbstractType::Unknown) => todo!("Type Unification"),
_ => false,
}
}
pub fn typecheck<TypVec : Index<TypeUUID, Output = NamedType>>(found : &AbstractType, span : Span, expected : &AbstractType, context : &str, linker_types : &TypVec, declared_here : Option<SpanFile>, errors : &ErrorCollector) {
if !type_compare(expected, found) {
let expected_name = expected.to_string(linker_types);
let found_name = found.to_string(linker_types);
let err_ref = errors.error(span, format!("Typing Error: {context} expects a {expected_name} but was given a {found_name}"));
if let Some(declared_here) = declared_here {
err_ref.info(declared_here, "Declared here");
}
assert!(expected_name != found_name, "{expected_name} != {found_name}");
}
}

pub fn typecheck_is_array_indexer<'a, TypVec : Index<TypeUUID, Output = NamedType>>(arr_type : &'a AbstractType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) -> &'a AbstractType {
let AbstractType::Array(arr_element_type) = arr_type else {
let arr_type_name = arr_type.to_string(linker_types);
errors.error(span, format!("Typing Error: Attempting to index into this, but it is not of array type, instead found a {arr_type_name}"));
return &ERROR_TYPE;
};
&arr_element_type.deref()
}
2 changes: 1 addition & 1 deletion src/codegen_fallback.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::ops::Deref;

use crate::{flattening::{Instruction, Module}, instantiation::{RealWirePathElem, InstantiatedModule, RealWire, RealWireDataSource, WireID}, linker::{get_builtin_type, TypeUUID}, typing::ConcreteType};
use crate::{flattening::{Instruction, Module}, instantiation::{RealWirePathElem, InstantiatedModule, RealWire, RealWireDataSource, WireID}, linker::{get_builtin_type, TypeUUID}, concrete_type::ConcreteType};

fn get_type_name_size(id : TypeUUID) -> u64 {
if id == get_builtin_type("int") {
Expand Down
151 changes: 151 additions & 0 deletions src/concrete_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@

use std::ops::{Deref, Index};

use crate::{abstract_type::AbstractType, errors::ErrorCollector, file_position::Span, flattening::{BinaryOperator, UnaryOperator}, linker::{get_builtin_type, Linkable, NamedType, TypeUUID}, value::Value};

pub const BOOL_CONCRETE_TYPE : ConcreteType = ConcreteType::Named(get_builtin_type("bool"));
pub const INT_CONCRETE_TYPE : ConcreteType = ConcreteType::Named(get_builtin_type("int"));

#[derive(Debug,Clone,PartialEq,Eq)]
pub enum ConcreteType {
Named(TypeUUID),
Value(Value),
Array(Box<(ConcreteType, ConcreteType)>),
Unknown,
Error
}

impl Into<AbstractType> for &ConcreteType {
fn into(self) -> AbstractType {
match self {
ConcreteType::Named(name) => {
AbstractType::Named(*name)
}
ConcreteType::Value(_) => {
unreachable!("Turning a ConcreteType::Value into an AbstractType");
}
ConcreteType::Array(arr) => {
let (sub, _sz) = arr.deref();
let concrete_sub : AbstractType = sub.into();
AbstractType::Array(Box::new(concrete_sub))
}
ConcreteType::Unknown => AbstractType::Unknown,
ConcreteType::Error => AbstractType::Error
}
}
}


/// Panics on Type Errors that should have been caught by [AbstractType]
///
/// TODO Add checks for array sizes being equal etc.
pub fn get_unary_operator_expected_output(op : UnaryOperator, input_typ : &ConcreteType) -> ConcreteType {
let gather_type = match op {
UnaryOperator::Not => {
assert_eq!(*input_typ, BOOL_CONCRETE_TYPE);
return BOOL_CONCRETE_TYPE
}
UnaryOperator::Negate => {
assert_eq!(*input_typ, INT_CONCRETE_TYPE);
return INT_CONCRETE_TYPE
}
UnaryOperator::And => BOOL_CONCRETE_TYPE,
UnaryOperator::Or => BOOL_CONCRETE_TYPE,
UnaryOperator::Xor => BOOL_CONCRETE_TYPE,
UnaryOperator::Sum => INT_CONCRETE_TYPE,
UnaryOperator::Product => INT_CONCRETE_TYPE
};
assert_eq!(input_typ.down_array(), &gather_type);
gather_type
}

/// Panics on Type Errors that should have been caught by [AbstractType]
///
/// TODO Add checks for array sizes being equal etc.
pub fn get_binary_operator_expected_output(op : BinaryOperator, left_typ : &ConcreteType, right_typ : &ConcreteType) -> ConcreteType {
let ((in_left, in_right), out) = match op {
BinaryOperator::And => ((BOOL_CONCRETE_TYPE, BOOL_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::Or => ((BOOL_CONCRETE_TYPE, BOOL_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::Xor => ((BOOL_CONCRETE_TYPE, BOOL_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::Add => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE),
BinaryOperator::Subtract => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE),
BinaryOperator::Multiply => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE),
BinaryOperator::Divide => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE),
BinaryOperator::Modulo => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE),
BinaryOperator::Equals => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::NotEquals => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::GreaterEq => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::Greater => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::LesserEq => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
BinaryOperator::Lesser => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE),
};

assert_eq!(*left_typ, in_left);
assert_eq!(*right_typ, in_right);

out
}

impl ConcreteType {
#[track_caller]
pub fn unwrap_value(&self) -> &Value {
let ConcreteType::Value(v) = self else {unreachable!("unwrap_value")};
v
}
pub fn to_string<TypVec : Index<TypeUUID, Output = NamedType>>(&self, linker_types : &TypVec) -> String {
match self {
ConcreteType::Named(name) => linker_types[*name].get_full_name(),
ConcreteType::Array(arr_box) => {
let (elem_typ, arr_size) = arr_box.deref();
format!("{}[{}]", elem_typ.to_string(linker_types), arr_size.unwrap_value().unwrap_integer())
}
ConcreteType::Value(v) => format!("{{concrete_type_{v}}}"),
ConcreteType::Unknown => format!("{{concrete_type_unknown}}"),
ConcreteType::Error => format!("{{concrete_type_error}}"),
}
}
pub fn down_array(&self) -> &ConcreteType {
let ConcreteType::Array(arr_box) = self else {unreachable!("Must be an array!")};
let (sub, _sz) = arr_box.deref();
sub
}

pub fn type_compare(&self, found : &ConcreteType) -> bool {
match (self, found) {
(ConcreteType::Named(exp), ConcreteType::Named(fnd)) => exp == fnd,
(ConcreteType::Array(exp), ConcreteType::Array(fnd)) => {
let (target_arr_typ, target_arr_size) = exp.deref();
let (found_arr_typ, found_arr_size) = fnd.deref();
target_arr_typ.type_compare(found_arr_typ) && target_arr_size.type_compare(found_arr_size)
}
(ConcreteType::Value(lv), ConcreteType::Value(rv)) => lv == rv,
(ConcreteType::Error, _) | (_, ConcreteType::Error) => true, // Just assume correct, because the other side has an error
(ConcreteType::Unknown, _) | (_, ConcreteType::Unknown) => todo!("Type Unification {self:?} {found:?}"),
_ => false,
}
}
pub fn check_type<TypVec : Index<TypeUUID, Output = NamedType>>(&self, source_type : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) {
if !self.type_compare(source_type) {
errors.error(span, format!("Concrete Type Error! Expected {} but found {}", self.to_string(linker_types), source_type.to_string(linker_types)));
}
}

pub fn check_or_update_type<TypVec : Index<TypeUUID, Output = NamedType>>(&mut self, source_type : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) {
if *self == ConcreteType::Unknown {
*self = source_type.clone();
} else {
self.check_type(source_type, span, linker_types, errors);
}
}

pub fn typecheck_concrete_unary_operator<TypVec : Index<TypeUUID, Output = NamedType>>(&mut self, op : UnaryOperator, input_typ : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) {
let expected = get_unary_operator_expected_output(op, input_typ);

self.check_or_update_type(&expected, span, linker_types, errors);
}
pub fn typecheck_concrete_binary_operator<TypVec : Index<TypeUUID, Output = NamedType>>(&mut self, op : BinaryOperator, left_typ : &ConcreteType, right_typ : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) {
let expected = get_binary_operator_expected_output(op, left_typ, right_typ);

self.check_or_update_type(&expected, span, linker_types, errors);
}
}
2 changes: 1 addition & 1 deletion src/dev_aid/lsp/tree_walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use std::ops::Deref;

use crate::{
file_position::Span, flattening::{Declaration, FlatID, Instruction, Interface, InterfaceID, Module, Port, PortID, SubModuleInstance, WireInstance, WireReference, WireReferenceRoot, WireSource}, linker::{FileData, Linker, ModuleUUID, NameElem}, typing::WrittenType
file_position::Span, flattening::{Declaration, FlatID, Instruction, Interface, InterfaceID, Module, Port, PortID, SubModuleInstance, WireInstance, WireReference, WireReferenceRoot, WireSource, WrittenType}, linker::{FileData, Linker, ModuleUUID, NameElem}
};

#[derive(Clone, Copy, Debug)]
Expand Down
67 changes: 59 additions & 8 deletions src/flattening/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ mod initialization;
mod typechecking;
mod parse;

use std::ops::{Deref, Index};

pub use parse::flatten_all_modules;
pub use initialization::gather_initial_file_data;
pub use typechecking::typecheck_all_modules;

use crate::{
arena_alloc::{FlatAlloc, UUIDMarker, UUIDRange, UUID}, errors::ErrorCollector, file_position::{BracketSpan, FileText, Span}, instantiation::InstantiationList, linker::{ConstantUUID, LinkInfo, ModuleUUID}, parser::Documentation, pretty_print_many_spans, typing::{AbstractType, WrittenType}, value::Value
abstract_type::AbstractType, arena_alloc::{FlatAlloc, UUIDMarker, UUIDRange, UUID}, errors::ErrorCollector, file_position::{BracketSpan, FileText, Span}, instantiation::InstantiationList, linker::{ConstantUUID, LinkInfo, Linkable, ModuleUUID, NamedType, TypeUUID}, parser::Documentation, pretty_print_many_spans, value::Value
};

/// Modules are compiled in 4 stages. All modules must pass through each stage before advancing to the next stage.
Expand Down Expand Up @@ -328,6 +330,15 @@ pub struct PortInfo {
pub port_identifier_typ : IdentifierType
}

#[derive(Debug)]
pub struct WireInstance {
pub interface : InterfaceID,
pub typ : AbstractType,
pub is_compiletime : bool,
pub span : Span,
pub source : WireSource
}

#[derive(Debug)]
pub enum WireSource {
WireRef(WireReference), // Used to add a span to the reference of a wire.
Expand Down Expand Up @@ -361,15 +372,55 @@ impl WireSource {

const IS_GEN_UNINIT : bool = false;

#[derive(Debug)]
pub struct WireInstance {
pub interface : InterfaceID,
pub typ : AbstractType,
pub is_compiletime : bool,
pub span : Span,
pub source : WireSource

#[derive(Debug, Clone)]
pub enum WrittenType {
Error(Span),
Named(Span, TypeUUID),
Array(Span, Box<(WrittenType, FlatID, BracketSpan)>)
}

impl WrittenType {
pub fn get_span(&self) -> Span {
match self {
WrittenType::Error(span) | WrittenType::Named(span, _) | WrittenType::Array(span, _) => *span
}
}

pub fn to_type(&self) -> AbstractType {
match self {
WrittenType::Error(_) => AbstractType::Error,
WrittenType::Named(_, id) => AbstractType::Named(*id),
WrittenType::Array(_, arr_box) => {
let (elem_typ, _arr_idx, _br_span) = arr_box.deref();
AbstractType::Array(Box::new(elem_typ.to_type()))
}
}
}

pub fn for_each_generative_input<F : FnMut(FlatID)>(&self, mut f : F) {
match self {
WrittenType::Error(_) | WrittenType::Named(_, _) => {}
WrittenType::Array(_span, arr_box) => {
f(arr_box.deref().1)
}
}
}

pub fn to_string<TypVec : Index<TypeUUID, Output = NamedType>>(&self, linker_types : &TypVec) -> String {
match self {
WrittenType::Error(_) => {
"{error}".to_owned()
}
WrittenType::Named(_, id) => {
linker_types[*id].get_full_name()
}
WrittenType::Array(_, sub) => sub.deref().0.to_string(linker_types) + "[]",
}
}
}


#[derive(Debug)]
pub struct Declaration {
pub interface : InterfaceID,
Expand Down
2 changes: 1 addition & 1 deletion src/flattening/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{iter::zip, ops::{Deref, DerefMut}, str::FromStr};
use num::BigInt;
use sus_proc_macro::{field, kind, kw};
use crate::{
arena_alloc::{UUIDRange, UUID}, debug::SpanDebugger, errors::ErrorCollector, file_position::{BracketSpan, Span}, linker::{with_module_editing_context, ConstantUUIDMarker, Linker, ModuleUUID, ModuleUUIDMarker, NameElem, NameResolver, NamedConstant, NamedType, ResolvedName, Resolver, TypeUUIDMarker, WorkingOnResolver}, parser::Cursor, typing::{AbstractType, WrittenType}, value::Value
arena_alloc::{UUIDRange, UUID}, debug::SpanDebugger, errors::ErrorCollector, file_position::{BracketSpan, Span}, linker::{with_module_editing_context, ConstantUUIDMarker, Linker, ModuleUUID, ModuleUUIDMarker, NameElem, NameResolver, NamedConstant, NamedType, ResolvedName, Resolver, TypeUUIDMarker, WorkingOnResolver}, parser::Cursor, abstract_type::AbstractType, value::Value
};

use super::name_context::LocalVariableContext;
Expand Down
Loading

0 comments on commit b3fdbaa

Please sign in to comment.