Skip to content

Commit

Permalink
refactor(types): cleaner(?) type checking
Browse files Browse the repository at this point in the history
  • Loading branch information
MilkeeyCat committed Oct 9, 2024
1 parent f63ea6d commit 81148b8
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 75 deletions.
8 changes: 4 additions & 4 deletions src/parser/expr/expr.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::{int_repr::UIntLitRepr, ExprError, IntLitRepr};
use crate::{
parser::op::{BinOp, UnOp},
passes::TypeChecker,
scope::Scope,
symbol_table::{Symbol, SymbolTableError},
type_table,
Expand Down Expand Up @@ -106,9 +105,10 @@ impl Expression for ExprBinary {
| BinOp::BitwiseAnd
| BinOp::BitwiseOr
| BinOp::Shl
| BinOp::Shr => {
Ok(TypeChecker::check_bin(&self.op, &self.left, &self.right, scope).unwrap())
}
| BinOp::Shr => Ok(Type::common_type(
self.left.type_(scope)?,
self.right.type_(scope)?,
)),
BinOp::LessThan
| BinOp::GreaterThan
| BinOp::LessEqual
Expand Down
149 changes: 87 additions & 62 deletions src/passes/type_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,80 +207,105 @@ impl TypeChecker {
Ok(())
}

pub fn check_bin(op: &BinOp, left: &Expr, right: &Expr, scope: &Scope) -> Result<Type> {
Self::check_expr(left, scope)?;
Self::check_expr(right, scope)?;
pub fn check_add<'a>(mut lhs: &'a Expr, mut rhs: &'a Expr, scope: &Scope) -> Result<()> {
let mut left_type = lhs.type_(scope)?;
let mut right_type = rhs.type_(scope)?;

// Canonicalize `offset + ptr` to `ptr + offset`
if !left_type.ptr() && right_type.ptr() {
std::mem::swap(&mut lhs, &mut rhs);
std::mem::swap(&mut left_type, &mut right_type);
}

let left_type = left.type_(scope)?;
let right_type = right.type_(scope)?;
if left_type.ptr() {
if !Expr::int_lit_only(rhs) {
assert_eq!(right_type, Type::UInt(UintType::Usize));
}

let type_ = match op {
BinOp::Add | BinOp::Sub => {
if left_type.int() && right_type.int() {
if Expr::int_lit_only(left) || Expr::int_lit_only(right) {
if left_type > right_type {
Type::promote(right_type, left_type)?
} else {
Type::promote(left_type, right_type)?
}
} else {
assert_eq!(left_type, right_type);
return Ok(());
}

left_type
}
} else if left_type.ptr() && right_type.int() || left_type.int() && right_type.ptr()
{
let (ptr, int, int_expr) = if left_type.ptr() {
(left_type, right_type, right)
} else {
(right_type, left_type, left)
};

if int != Type::UInt(UintType::Usize) && !Expr::int_lit_only(int_expr) {
panic!("Only interger of type usize is allowed in pointer arithmetic");
}
if left_type.int() && right_type.int() {
Self::check_bin_num_with_lit(lhs, rhs, scope)?;

if Expr::int_lit_only(int_expr) {
Type::promote(int, Type::UInt(UintType::Usize))?;
}
return Ok(());
}

ptr
} else {
panic!("Go fuck yourself");
}
panic!("Invalid operands");
}

pub fn check_sub(lhs: &Expr, rhs: &Expr, scope: &Scope) -> Result<()> {
let left_type = lhs.type_(scope)?;
let right_type = rhs.type_(scope)?;

if left_type.ptr() {
if !Expr::int_lit_only(rhs) {
assert_eq!(right_type, Type::UInt(UintType::Usize));
}

return Ok(());
}

if left_type.int() && right_type.int() {
Self::check_bin_num_with_lit(lhs, rhs, scope)?;

return Ok(());
}

panic!("Invalid operands");
}

pub fn check_bin_num_with_lit(lhs: &Expr, rhs: &Expr, scope: &Scope) -> Result<()> {
let left_type = lhs.type_(scope)?;
let right_type = rhs.type_(scope)?;

assert!(left_type.int() && right_type.int());

Ok(match (Expr::int_lit_only(lhs), Expr::int_lit_only(rhs)) {
(true, false) => {
assert!(Type::common_type(left_type, right_type.clone()) <= right_type);
}
(false, true) => {
assert!(Type::common_type(left_type.clone(), right_type) <= left_type);
}
(false, false) => assert_eq!(left_type, right_type),
(true, true) => (),
})
}

pub fn check_bin(op: &BinOp, left: &Expr, right: &Expr, scope: &Scope) -> Result<()> {
Self::check_expr(left, scope)?;
Self::check_expr(right, scope)?;

let left_type = left.type_(scope)?;
let right_type = right.type_(scope)?;

Ok(match op {
BinOp::Add => Self::check_add(left, right, scope)?,
BinOp::Sub => Self::check_sub(left, right, scope)?,
BinOp::Mul
| BinOp::Div
| BinOp::LessThan
| BinOp::LessEqual
| BinOp::GreaterThan
| BinOp::GreaterEqual
| BinOp::BitwiseOr
| BinOp::BitwiseAnd
| BinOp::Shl
| BinOp::Shr => Self::check_bin_num_with_lit(left, right, scope)?,
BinOp::LogicalAnd | BinOp::LogicalOr => {
assert_eq!(left_type, Type::Bool);
assert_eq!(right_type, Type::Bool);
Type::Bool
}
BinOp::Assign => unreachable!(),
_ => {
assert!(
left_type.int() && right_type.int(),
"Math operations can be applied only to integers"
);

match (Expr::int_lit_only(left), Expr::int_lit_only(right)) {
(true, true) => {
if left_type > right_type {
Type::promote(right_type, left_type)?
} else {
Type::promote(left_type, right_type)?
}
}
(true, false) => Type::promote(left_type, right_type)?,
(false, true) => Type::promote(right_type, left_type)?,
(false, false) => {
assert_eq!(left_type, right_type);

left_type
}
BinOp::Equal | BinOp::NotEqual => {
if left_type.int() && right_type.int() {
Self::check_bin_num_with_lit(left, right, scope)?;
} else {
assert!(left_type == right_type);
}
}
};

Ok(type_)
})
}

fn check_assign(left_type: Type, right: &Expr, scope: &Scope) -> Result<()> {
Expand All @@ -305,7 +330,7 @@ impl TypeChecker {
}

if left_type.int() && Expr::int_lit_only(right) {
Type::promote(right_type, left_type)?;
assert!(Type::common_type(left_type.clone(), right_type) <= left_type);
} else {
assert_eq!(left_type, right_type);
}
Expand Down
39 changes: 30 additions & 9 deletions src/types/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,6 @@ impl Type {
}
}

pub fn promote(from: Self, to: Self) -> Result<Self, TypeError> {
Ok(match (from, to) {
(Type::UInt(lhs), Type::UInt(rhs)) if lhs <= rhs => Type::UInt(rhs),
(Type::Int(lhs), Type::Int(rhs)) if lhs <= rhs => Type::Int(rhs),
(Type::UInt(uint), Type::Int(int)) if uint.clone().to_signed() <= int => Type::Int(int),
(from, to) => return Err(TypeError::Promotion(from, to)),
})
}

pub fn size(&self) -> Option<usize> {
match self {
Type::Void => Some(0),
Expand All @@ -176,4 +167,34 @@ impl Type {
_ => unreachable!(),
}
}

pub fn common_type(lhs: Type, rhs: Type) -> Type {
match (lhs, rhs) {
(type_ @ Type::Ptr(_), int) | (int, type_ @ Type::Ptr(_)) if int.int() => type_,
(Type::UInt(lhs), Type::UInt(rhs)) => {
if lhs > rhs {
Type::UInt(lhs)
} else {
Type::UInt(rhs)
}
}
(Type::Int(lhs), Type::Int(rhs)) => {
if lhs > rhs {
Type::Int(lhs)
} else {
Type::Int(rhs)
}
}
(Type::UInt(uint), Type::Int(int)) | (Type::Int(int), Type::UInt(uint)) => {
let uint_int = uint.to_signed();

if uint_int <= int {
Type::Int(int)
} else {
Type::Int(uint_int)
}
}
(lhs, rhs) => unreachable!("Failed to get common type for {lhs} and {rhs}"),
}
}
}

0 comments on commit 81148b8

Please sign in to comment.