Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 60 additions & 7 deletions src/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,47 @@ impl Checker {
self.constraints.push(c);
}

fn check_int_literal_suffix(
&mut self,
id: ExprID,
value: i64,
suffix: IntLiteralSuffix,
arena: &ExprArena,
negative: bool,
) -> TypeID {
let shown_value = if negative {
format!("-{value}")
} else {
value.to_string()
};

match suffix {
IntLiteralSuffix::I32 => {
let max = if negative {
-(i32::MIN as i64)
} else {
i32::MAX as i64
};
if value > max {
self.errors.push(TypeError {
location: arena.locs[id],
message: format!("integer literal {shown_value} is out of range for i32"),
});
}
mk_type(Type::Int32)
}
IntLiteralSuffix::U32 => {
if value > u32::MAX as i64 {
self.errors.push(TypeError {
location: arena.locs[id],
message: format!("integer literal {value} is out of range for u32"),
});
}
mk_type(Type::UInt32)
}
}
}

fn check_unop(
&mut self,
id: ExprID,
Expand All @@ -218,10 +259,18 @@ impl Checker {
arena: &ExprArena,
decls: &DeclTable,
) -> TypeID {
let argt = self.check_expr(arg, arena, decls);

match op {
Unop::Neg => {
// A leading `-` in literal position is considered by signed
// suffix range checks, so `-2147483648i32` is valid even
// though `2147483648i32` is not.
if let Expr::Int(n, Some(suffix @ IntLiteralSuffix::I32)) = &arena[arg] {
let ty = self.check_int_literal_suffix(arg, *n, *suffix, arena, true);
self.types[arg] = ty;
return ty;
}

let argt = self.check_expr(arg, arena, decls);
let ft = self.fresh();
let alts: Vec<_> = self
.neg_overloads
Expand All @@ -246,6 +295,7 @@ impl Checker {
r
}
Unop::Not => {
let argt = self.check_expr(arg, arena, decls);
self.eq(
argt,
mk_type(Type::Bool),
Expand Down Expand Up @@ -372,7 +422,7 @@ impl Checker {
fn check_expr(&mut self, id: ExprID, arena: &ExprArena, decls: &DeclTable) -> TypeID {
let ty = match &arena[id] {
Expr::True | Expr::False => mk_type(Type::Bool),
Expr::Int(_) => {
Expr::Int(_, None) => {
let ty = self.fresh();
let alts = vec![
Alt {
Expand All @@ -384,8 +434,12 @@ impl Checker {
self.add_constraint(Constraint::Or(ty, alts, arena.locs[id], None));
ty
}
Expr::UInt(_) => mk_type(Type::UInt32),
Expr::Real(_) => mk_type(Type::Float32),
Expr::Int(n, Some(suffix)) => {
self.check_int_literal_suffix(id, *n, *suffix, arena, false)
}
Expr::Real(_, None) => mk_type(Type::Float32),
Expr::Real(_, Some(FloatLiteralSuffix::F32)) => mk_type(Type::Float32),
Expr::Real(_, Some(FloatLiteralSuffix::F64)) => mk_type(Type::Float64),
Expr::Char(_) => mk_type(Type::Int8),
Expr::String(s) => {
let int8 = mk_type(Type::Int8);
Expand Down Expand Up @@ -801,8 +855,7 @@ impl Checker {

// Extract constant size when available (e.g. [0; 5]).
let array_size = match &arena[*size] {
Expr::Int(n) => ArraySize::Known(*n as i32),
Expr::UInt(n) => ArraySize::Known(*n as i32),
Expr::Int(n, _) => ArraySize::Known(*n as i32),
_ => ArraySize::Known(0),
};

Expand Down
9 changes: 7 additions & 2 deletions src/decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,12 @@ fn format_func_decl(func: &FuncDecl, is_macro: bool) -> String {
let requires = func
.requires
.iter()
.map(|&r| format!(" require {}", func.arena.exprs[r].pretty_print(&func.arena, 0)))
.map(|&r| {
format!(
" require {}",
func.arena.exprs[r].pretty_print(&func.arena, 0)
)
})
.collect::<Vec<_>>()
.join("");

Expand Down Expand Up @@ -564,7 +569,7 @@ mod tests {
let mut arena = ExprArena::new();
// Create body: { x + 1 }
let x_id = arena.add(Expr::Id(Name::str("x")), test_loc());
let one_id = arena.add(Expr::Int(1), test_loc());
let one_id = arena.add(Expr::Int(1, None), test_loc());
let add_id = arena.add(Expr::Binop(Binop::Plus, x_id, one_id), test_loc());
let body_id = arena.add(Expr::Block(vec![add_id]), test_loc());

Expand Down
30 changes: 30 additions & 0 deletions src/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,36 @@ use std::fmt;
use std::hash::Hash;
use std::ops::Deref;

#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
pub enum IntLiteralSuffix {
I32,
U32,
}

impl fmt::Display for IntLiteralSuffix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IntLiteralSuffix::I32 => write!(f, "i32"),
IntLiteralSuffix::U32 => write!(f, "u32"),
}
}
}

#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
pub enum FloatLiteralSuffix {
F32,
F64,
}

impl fmt::Display for FloatLiteralSuffix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FloatLiteralSuffix::F32 => write!(f, "f32"),
FloatLiteralSuffix::F64 => write!(f, "f64"),
}
}
}

/// An interned string.
#[derive(Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Name(Intern<String>);
Expand Down
78 changes: 41 additions & 37 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ pub enum Expr {
/// Identifier expression.
Id(Name),

/// Signed integer literal.
Int(i64),
/// Integer literal, with optional explicit suffix.
Int(i64, Option<IntLiteralSuffix>),

/// Unsigned integer literal.
UInt(u64),

/// Floating-point literal as string.
Real(String), // f64 is not hashable so we just use the string representation
/// Floating-point literal as string, with optional explicit suffix.
Real(String, Option<FloatLiteralSuffix>), // f64 is not hashable so we just use the string representation

/// Function call expression with function and arguments.
Call(ExprID, Vec<ExprID>),
Expand Down Expand Up @@ -129,7 +126,7 @@ impl Expr {
///
/// ```ignore
/// let mut arena = ExprArena::new();
/// let expr_id = arena.add(Expr::Int(42), test_loc());
/// let expr_id = arena.add(Expr::Int(42, None), test_loc());
/// let output = arena.exprs[expr_id].pretty_print(&arena, 0);
/// // Output: "42"
/// ```
Expand All @@ -140,9 +137,14 @@ impl Expr {
let args_str: Vec<_> = args.iter().map(|t| t.pretty_print()).collect();
format!("{}⟨{}⟩", name, args_str.join(", "))
}
Expr::Int(n) => n.to_string(),
Expr::UInt(n) => n.to_string(),
Expr::Real(s) => s.clone(),
Expr::Int(n, suffix) => match suffix {
Some(suffix) => format!("{n}{suffix}"),
None => n.to_string(),
},
Expr::Real(s, suffix) => match suffix {
Some(suffix) => format!("{s}{suffix}"),
None => s.clone(),
},
Expr::String(s) => format!("\"{}\"", s),
Expr::Char(c) => format!("'{}'", c),
Expr::True => "true".to_string(),
Expand Down Expand Up @@ -358,9 +360,8 @@ pub fn copy_expr(
dst_arena.add(Expr::Id(*name), loc)
}
Expr::TypeApp(name, args) => dst_arena.add(Expr::TypeApp(*name, args.clone()), loc),
Expr::Int(n) => dst_arena.add(Expr::Int(*n), loc),
Expr::UInt(n) => dst_arena.add(Expr::UInt(*n), loc),
Expr::Real(s) => dst_arena.add(Expr::Real(s.clone()), loc),
Expr::Int(n, suffix) => dst_arena.add(Expr::Int(*n, *suffix), loc),
Expr::Real(s, suffix) => dst_arena.add(Expr::Real(s.clone(), *suffix), loc),
Expr::String(s) => dst_arena.add(Expr::String(s.clone()), loc),
Expr::Char(c) => dst_arena.add(Expr::Char(*c), loc),
Expr::True => dst_arena.add(Expr::True, loc),
Expand Down Expand Up @@ -551,10 +552,13 @@ mod tests {
fn test_pretty_print_literals() {
let arena = ExprArena::new();

assert_eq!(Expr::Int(42).pretty_print(&arena, 0), "42");
assert_eq!(Expr::UInt(100).pretty_print(&arena, 0), "100");
assert_eq!(Expr::Int(42, None).pretty_print(&arena, 0), "42");
assert_eq!(
Expr::Int(100, Some(IntLiteralSuffix::U32)).pretty_print(&arena, 0),
"100u32"
);
assert_eq!(
Expr::Real("3.14".to_string()).pretty_print(&arena, 0),
Expr::Real("3.14".to_string(), None).pretty_print(&arena, 0),
"3.14"
);
assert_eq!(
Expand All @@ -581,8 +585,8 @@ mod tests {
fn test_pretty_print_binop() {
let mut arena = ExprArena::new();

let lhs_id = arena.add(Expr::Int(1), test_loc());
let rhs_id = arena.add(Expr::Int(2), test_loc());
let lhs_id = arena.add(Expr::Int(1, None), test_loc());
let rhs_id = arena.add(Expr::Int(2, None), test_loc());

let add_expr = Expr::Binop(Binop::Plus, lhs_id, rhs_id);
assert_eq!(add_expr.pretty_print(&arena, 0), "1 + 2");
Expand All @@ -598,7 +602,7 @@ mod tests {
fn test_pretty_print_unop() {
let mut arena = ExprArena::new();

let expr_id = arena.add(Expr::Int(42), test_loc());
let expr_id = arena.add(Expr::Int(42, None), test_loc());

let neg_expr = Expr::Unop(Unop::Neg, expr_id);
assert_eq!(neg_expr.pretty_print(&arena, 0), "-42");
Expand All @@ -613,8 +617,8 @@ mod tests {
let mut arena = ExprArena::new();

let func_id = arena.add(Expr::Id(Name::str("foo")), test_loc());
let arg1_id = arena.add(Expr::Int(1), test_loc());
let arg2_id = arena.add(Expr::Int(2), test_loc());
let arg1_id = arena.add(Expr::Int(1, None), test_loc());
let arg2_id = arena.add(Expr::Int(2, None), test_loc());

let call_expr = Expr::Call(func_id, vec![arg1_id, arg2_id]);
assert_eq!(call_expr.pretty_print(&arena, 0), "foo(1, 2)");
Expand All @@ -627,9 +631,9 @@ mod tests {
fn test_pretty_print_array_literal() {
let mut arena = ExprArena::new();

let elem1 = arena.add(Expr::Int(1), test_loc());
let elem2 = arena.add(Expr::Int(2), test_loc());
let elem3 = arena.add(Expr::Int(3), test_loc());
let elem1 = arena.add(Expr::Int(1, None), test_loc());
let elem2 = arena.add(Expr::Int(2, None), test_loc());
let elem3 = arena.add(Expr::Int(3, None), test_loc());

let arr_expr = Expr::ArrayLiteral(vec![elem1, elem2, elem3]);
assert_eq!(arr_expr.pretty_print(&arena, 0), "[1, 2, 3]");
Expand All @@ -640,7 +644,7 @@ mod tests {
let mut arena = ExprArena::new();

let arr_id = arena.add(Expr::Id(Name::str("arr")), test_loc());
let idx_id = arena.add(Expr::Int(0), test_loc());
let idx_id = arena.add(Expr::Int(0, None), test_loc());

let index_expr = Expr::ArrayIndex(arr_id, idx_id);
assert_eq!(index_expr.pretty_print(&arena, 0), "arr[0]");
Expand Down Expand Up @@ -669,7 +673,7 @@ mod tests {
assert_eq!(var2.pretty_print(&arena, 0), "var x: i32");

// var x = 42
let init_id = arena.add(Expr::Int(42), test_loc());
let init_id = arena.add(Expr::Int(42, None), test_loc());
let var3 = Expr::Var(Name::str("x"), Some(init_id), None);
assert_eq!(var3.pretty_print(&arena, 0), "var x = 42");

Expand All @@ -682,7 +686,7 @@ mod tests {
fn test_pretty_print_let() {
let mut arena = ExprArena::new();

let value_id = arena.add(Expr::Int(42), test_loc());
let value_id = arena.add(Expr::Int(42, None), test_loc());

// let x = 42
let let1 = Expr::Let(Name::str("x"), value_id, None);
Expand All @@ -699,7 +703,7 @@ mod tests {

// |x| x + 1
let x_id = arena.add(Expr::Id(Name::str("x")), test_loc());
let one_id = arena.add(Expr::Int(1), test_loc());
let one_id = arena.add(Expr::Int(1, None), test_loc());
let body_id = arena.add(Expr::Binop(Binop::Plus, x_id, one_id), test_loc());

let lambda = Expr::Lambda {
Expand All @@ -718,8 +722,8 @@ mod tests {
let mut arena = ExprArena::new();

let cond_id = arena.add(Expr::True, test_loc());
let then_id = arena.add(Expr::Int(1), test_loc());
let else_id = arena.add(Expr::Int(2), test_loc());
let then_id = arena.add(Expr::Int(1, None), test_loc());
let else_id = arena.add(Expr::Int(2, None), test_loc());

// if true 1 else 2 (simplified without blocks)
let if_expr = Expr::If(cond_id, then_id, Some(else_id));
Expand All @@ -745,8 +749,8 @@ mod tests {
fn test_pretty_print_for() {
let mut arena = ExprArena::new();

let start_id = arena.add(Expr::Int(0), test_loc());
let end_id = arena.add(Expr::Int(10), test_loc());
let start_id = arena.add(Expr::Int(0, None), test_loc());
let end_id = arena.add(Expr::Int(10, None), test_loc());
let body_id = arena.add(Expr::Block(vec![]), test_loc());

let for_expr = Expr::For {
Expand All @@ -768,8 +772,8 @@ mod tests {
assert_eq!(empty_block.pretty_print(&arena, 0), "{}");

// Block with expressions
let expr1_id = arena.add(Expr::Int(1), test_loc());
let expr2_id = arena.add(Expr::Int(2), test_loc());
let expr1_id = arena.add(Expr::Int(1, None), test_loc());
let expr2_id = arena.add(Expr::Int(2, None), test_loc());
let block = Expr::Block(vec![expr1_id, expr2_id]);

assert_eq!(block.pretty_print(&arena, 0), "{\n 1\n 2\n}");
Expand All @@ -779,7 +783,7 @@ mod tests {
fn test_pretty_print_return() {
let mut arena = ExprArena::new();

let value_id = arena.add(Expr::Int(42), test_loc());
let value_id = arena.add(Expr::Int(42, None), test_loc());
let return_expr = Expr::Return(value_id);

assert_eq!(return_expr.pretty_print(&arena, 0), "return 42");
Expand All @@ -789,7 +793,7 @@ mod tests {
fn test_pretty_print_tuple() {
let mut arena = ExprArena::new();

let elem1 = arena.add(Expr::Int(1), test_loc());
let elem1 = arena.add(Expr::Int(1, None), test_loc());
let elem2 = arena.add(Expr::String("hello".to_string()), test_loc());

let tuple_expr = Expr::Tuple(vec![elem1, elem2]);
Expand Down
Loading
Loading