diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 343c757..4502ef8 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -12,7 +12,7 @@ use crate::{ hir::Hir, scope::Scope, ty::{FunctionType, PairType, Rest, Type, Value}, - Comparison, ErrorKind, + ErrorKind, }; mod block; @@ -139,13 +139,6 @@ impl<'a> Compiler<'a> { } } - fn try_unwrap_optional(&mut self, ty: TypeId) -> TypeId { - match self.db.ty(ty) { - Type::Optional(inner) => self.try_unwrap_optional(*inner), - _ => ty, - } - } - fn detect_cycle(&mut self, type_id: TypeId, text_range: TextRange) -> bool { if self.db.is_cyclic(type_id) { self.db.error(ErrorKind::RecursiveTypeAlias, text_range); @@ -213,7 +206,18 @@ impl<'a> Compiler<'a> { format!( "{}::{} {{ {} }}", enum_name, - enum_variant.name, + match self.db.ty(enum_variant.enum_type) { + Type::Enum(enum_type) => { + enum_type + .variants + .iter() + .find(|item| *item.1 == ty) + .expect("enum type is missing variant") + .0 + .clone() + } + _ => unreachable!(), + }, fields.join(", ") ) } @@ -245,10 +249,10 @@ impl<'a> Compiler<'a> { self.db .compare_type_with_generics(from, to, &mut self.generic_type_stack) } else { - self.db.compare_type_raw(from, to) + self.db.compare_type(from, to) }; - if comparison > Comparison::Assignable { + if !comparison.is_assignable() { self.db.error( ErrorKind::TypeMismatch { expected: self.type_name(to), @@ -264,10 +268,10 @@ impl<'a> Compiler<'a> { self.db .compare_type_with_generics(from, to, &mut self.generic_type_stack) } else { - self.db.compare_type_raw(from, to) + self.db.compare_type(from, to) }; - if comparison > Comparison::Castable { + if !comparison.is_castable() { self.db.error( ErrorKind::CastMismatch { expected: self.type_name(to), diff --git a/crates/rue-compiler/src/compiler/expr.rs b/crates/rue-compiler/src/compiler/expr.rs index 2af9b4a..568ddd9 100644 --- a/crates/rue-compiler/src/compiler/expr.rs +++ b/crates/rue-compiler/src/compiler/expr.rs @@ -41,7 +41,7 @@ impl Compiler<'_> { Expr::PrefixExpr(prefix) => self.compile_prefix_expr(prefix), Expr::BinaryExpr(binary) => self.compile_binary_expr(binary), Expr::GroupExpr(expr) => self.compile_group_expr(expr, expected_type), - Expr::CastExpr(cast) => self.compile_cast_expr(cast, expected_type), + Expr::CastExpr(cast) => self.compile_cast_expr(cast), Expr::GuardExpr(guard) => self.compile_guard_expr(guard, expected_type), Expr::IfExpr(if_expr) => self.compile_if_expr(if_expr, expected_type), Expr::FunctionCallExpr(call) => self.compile_function_call_expr(call), diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index fa14f4b..629bd25 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -1,10 +1,11 @@ -use rue_parser::{AstNode, BinaryExpr, BinaryOp}; +use rowan::TextRange; +use rue_parser::{AstNode, BinaryExpr, BinaryOp, Expr}; use crate::{ compiler::Compiler, hir::{BinOp, Hir}, ty::{Guard, Value}, - Comparison, ErrorKind, + ErrorKind, HirId, TypeId, }; impl Compiler<'_> { @@ -14,236 +15,300 @@ impl Compiler<'_> { }; let text_range = binary.syntax().text_range(); - let mut value = self.unknown(); - - macro_rules! lhs { - () => { - binary - .lhs() - .map(|lhs| self.compile_expr(&lhs, None)) - .unwrap_or_else(|| self.unknown()) - }; + + let lhs = binary + .lhs() + .map(|lhs| self.compile_expr(&lhs, None)) + .unwrap_or_else(|| self.unknown()); + + let rhs_expr = binary.rhs(); + let rhs = rhs_expr.as_ref(); + + match op { + BinaryOp::Add => self.op_add(&lhs, rhs, text_range), + BinaryOp::Subtract => self.op_subtract(&lhs, rhs, text_range), + BinaryOp::Multiply => self.op_multiply(&lhs, rhs, text_range), + BinaryOp::Divide => self.op_divide(&lhs, rhs, text_range), + BinaryOp::Remainder => self.op_remainder(&lhs, rhs, text_range), + BinaryOp::Equals => self.op_equals(&lhs, rhs, text_range), + BinaryOp::NotEquals => self.op_not_equals(&lhs, rhs, text_range), + BinaryOp::GreaterThan => self.op_greater_than(&lhs, rhs, text_range), + BinaryOp::LessThan => self.op_less_than(&lhs, rhs, text_range), + BinaryOp::GreaterThanEquals => self.op_greater_than_equals(&lhs, rhs, text_range), + BinaryOp::LessThanEquals => self.op_less_than_equals(&lhs, rhs, text_range), + BinaryOp::And => self.op_and(lhs, rhs, text_range), + BinaryOp::Or => self.op_or(&lhs, rhs, text_range), } + } + + fn binary_op(&mut self, op: BinOp, lhs: HirId, rhs: HirId, type_id: TypeId) -> Value { + Value::new(self.db.alloc_hir(Hir::BinaryOp { op, lhs, rhs }), type_id) + } - macro_rules! rhs { - () => { - binary - .rhs() - .map(|rhs| self.compile_expr(&rhs, None)) - .unwrap_or_else(|| self.unknown()) - }; + fn op_add(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + if self + .db + .compare_type(lhs.type_id, self.builtins.public_key) + .is_equal() + { + return self.add_public_key(lhs.hir_id, rhs, text_range); } - let (op, lhs, rhs, type_id) = match op { - BinaryOp::Add => { - let lhs = lhs!(); - let rhs = rhs!(); - - if self - .db - .compare_type_raw(lhs.type_id, self.builtins.public_key) - == Comparison::Equal - { - self.type_check(rhs.type_id, self.builtins.public_key, text_range); - ( - BinOp::PointAdd, - lhs.hir_id, - rhs.hir_id, - self.builtins.public_key, - ) - } else if self.db.compare_type_raw(lhs.type_id, self.builtins.bytes) - == Comparison::Equal - { - self.type_check(rhs.type_id, self.builtins.bytes, text_range); - (BinOp::Concat, lhs.hir_id, rhs.hir_id, self.builtins.bytes) - } else { - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - (BinOp::Add, lhs.hir_id, rhs.hir_id, self.builtins.int) - } - } - BinaryOp::Subtract => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - (BinOp::Subtract, lhs.hir_id, rhs.hir_id, self.builtins.int) - } - BinaryOp::Multiply => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - (BinOp::Multiply, lhs.hir_id, rhs.hir_id, self.builtins.int) - } - BinaryOp::Divide => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - (BinOp::Divide, lhs.hir_id, rhs.hir_id, self.builtins.int) - } - BinaryOp::Remainder => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - (BinOp::Remainder, lhs.hir_id, rhs.hir_id, self.builtins.int) - } - BinaryOp::Equals => { - let lhs = lhs!(); - let rhs = rhs!(); - - if self.db.compare_type_raw(lhs.type_id, self.builtins.bytes) > Comparison::Castable - || self.db.compare_type_raw(rhs.type_id, self.builtins.bytes) - > Comparison::Castable - { - self.db.error( - ErrorKind::NonAtomEquality(self.type_name(lhs.type_id)), - text_range, - ); - } else if self.db.compare_type_raw(lhs.type_id, self.builtins.nil) - == Comparison::Equal - { - if let Hir::Reference(symbol_id) = self.db.hir(rhs.hir_id) { - value.guards.insert( - *symbol_id, - Guard::new(self.builtins.nil, self.try_unwrap_optional(rhs.type_id)), - ); - } - } else if self.db.compare_type_raw(rhs.type_id, self.builtins.nil) - == Comparison::Equal - { - if let Hir::Reference(symbol_id) = self.db.hir(lhs.hir_id) { - value.guards.insert( - *symbol_id, - Guard::new(self.builtins.nil, self.try_unwrap_optional(lhs.type_id)), - ); - } - } else { - self.type_check(rhs.type_id, lhs.type_id, text_range); - } - - (BinOp::Equals, lhs.hir_id, rhs.hir_id, self.builtins.bool) - } - BinaryOp::NotEquals => { - let lhs = lhs!(); - let rhs = rhs!(); - - if self.db.compare_type_raw(lhs.type_id, self.builtins.bytes) > Comparison::Castable - || self.db.compare_type_raw(rhs.type_id, self.builtins.bytes) - > Comparison::Castable - { - self.db.error( - ErrorKind::NonAtomEquality(self.type_name(lhs.type_id)), - text_range, - ); - } else if self.db.compare_type_raw(lhs.type_id, self.builtins.nil) - == Comparison::Equal - { - if let Hir::Reference(symbol_id) = self.db.hir(rhs.hir_id) { - value.guards.insert( - *symbol_id, - Guard::new(self.try_unwrap_optional(rhs.type_id), self.builtins.nil), - ); - } - } else if self.db.compare_type_raw(rhs.type_id, self.builtins.nil) - == Comparison::Equal - { - if let Hir::Reference(symbol_id) = self.db.hir(lhs.hir_id) { - value.guards.insert( - *symbol_id, - Guard::new(self.try_unwrap_optional(lhs.type_id), self.builtins.nil), - ); - } - } else { - self.type_check(rhs.type_id, lhs.type_id, text_range); - } - - (BinOp::NotEquals, lhs.hir_id, rhs.hir_id, self.builtins.bool) - } - BinaryOp::GreaterThan => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - ( - BinOp::GreaterThan, - lhs.hir_id, - rhs.hir_id, - self.builtins.bool, - ) - } - BinaryOp::LessThan => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - (BinOp::LessThan, lhs.hir_id, rhs.hir_id, self.builtins.bool) - } - BinaryOp::GreaterThanEquals => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - ( - BinOp::GreaterThanEquals, - lhs.hir_id, - rhs.hir_id, - self.builtins.bool, - ) - } - BinaryOp::LessThanEquals => { - let lhs = lhs!(); - let rhs = rhs!(); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - ( - BinOp::LessThanEquals, - lhs.hir_id, - rhs.hir_id, - self.builtins.bool, - ) + if self + .db + .compare_type(lhs.type_id, self.builtins.bytes) + .is_equal() + { + return self.add_bytes(lhs.hir_id, rhs, text_range); + } + + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op(BinOp::Add, lhs.hir_id, rhs.hir_id, self.builtins.int) + } + + fn add_public_key(&mut self, lhs: HirId, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.public_key))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(rhs.type_id, self.builtins.public_key, text_range); + self.binary_op(BinOp::PointAdd, lhs, rhs.hir_id, self.builtins.public_key) + } + + fn add_bytes(&mut self, lhs: HirId, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bytes))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(rhs.type_id, self.builtins.bytes, text_range); + self.binary_op(BinOp::Concat, lhs, rhs.hir_id, self.builtins.bytes) + } + + fn op_subtract(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op(BinOp::Subtract, lhs.hir_id, rhs.hir_id, self.builtins.int) + } + + fn op_multiply(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op(BinOp::Multiply, lhs.hir_id, rhs.hir_id, self.builtins.int) + } + + fn op_divide(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op(BinOp::Divide, lhs.hir_id, rhs.hir_id, self.builtins.int) + } + + fn op_remainder(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op(BinOp::Remainder, lhs.hir_id, rhs.hir_id, self.builtins.int) + } + + fn op_equals(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bytes))) + .unwrap_or_else(|| self.unknown()); + + let mut value = self.binary_op(BinOp::Equals, lhs.hir_id, rhs.hir_id, self.builtins.bool); + + let mut is_atom = true; + + if !self + .db + .compare_type(lhs.type_id, self.builtins.bytes) + .is_castable() + { + self.db.error( + ErrorKind::NonAtomEquality(self.type_name(lhs.type_id)), + text_range, + ); + is_atom = false; + } + + if !self + .db + .compare_type(rhs.type_id, self.builtins.bytes) + .is_castable() + { + self.db.error( + ErrorKind::NonAtomEquality(self.type_name(rhs.type_id)), + text_range, + ); + is_atom = false; + } + + if self + .db + .compare_type(lhs.type_id, self.builtins.nil) + .is_equal() + { + if let Hir::Reference(symbol_id) = self.db.hir(rhs.hir_id) { + value.guards.insert( + *symbol_id, + Guard::new(self.builtins.nil, self.db.non_optional(rhs.type_id)), + ); } - BinaryOp::And => { - let lhs = lhs!(); - self.type_guard_stack.push(lhs.then_guards()); - let rhs = rhs!(); - self.type_guard_stack.pop().unwrap(); - - self.type_check(lhs.type_id, self.builtins.bool, text_range); - self.type_check(rhs.type_id, self.builtins.bool, text_range); - - value.type_id = self.builtins.bool; - value.guards.extend(lhs.guards); - value.guards.extend(rhs.guards); - - ( - BinOp::LogicalAnd, - lhs.hir_id, - rhs.hir_id, - self.builtins.bool, - ) + } + + if self + .db + .compare_type(rhs.type_id, self.builtins.nil) + .is_equal() + { + if let Hir::Reference(symbol_id) = self.db.hir(lhs.hir_id) { + value.guards.insert( + *symbol_id, + Guard::new(self.builtins.nil, self.db.non_optional(lhs.type_id)), + ); } - BinaryOp::Or => { - let lhs = lhs!(); - self.type_guard_stack.push(lhs.else_guards()); - let rhs = rhs!(); - self.type_guard_stack.pop().unwrap(); + } + + if is_atom { + self.type_check(rhs.type_id, lhs.type_id, text_range); + } - self.type_check(lhs.type_id, self.builtins.bool, text_range); - self.type_check(rhs.type_id, self.builtins.bool, text_range); + value + } - value.type_id = self.builtins.bool; + fn op_not_equals(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let comparison = self.op_equals(lhs, rhs, text_range); - // TODO: Support type guards for `||`. + let mut value = Value::new( + self.db.alloc_hir(Hir::Not(comparison.hir_id)), + self.builtins.bool, + ); - (BinOp::LogicalOr, lhs.hir_id, rhs.hir_id, self.builtins.bool) - } - }; + for (symbol_id, guard) in comparison.guards { + value.guards.insert(symbol_id, !guard); + } + + value + } + + fn op_greater_than(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); - value.type_id = type_id; - value.hir_id = self.db.alloc_hir(Hir::BinaryOp { op, lhs, rhs }); + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op( + BinOp::GreaterThan, + lhs.hir_id, + rhs.hir_id, + self.builtins.bool, + ) + } + + fn op_less_than(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op(BinOp::LessThan, lhs.hir_id, rhs.hir_id, self.builtins.bool) + } + + fn op_greater_than_equals( + &mut self, + lhs: &Value, + rhs: Option<&Expr>, + text_range: TextRange, + ) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op( + BinOp::GreaterThanEquals, + lhs.hir_id, + rhs.hir_id, + self.builtins.bool, + ) + } + + fn op_less_than_equals( + &mut self, + lhs: &Value, + rhs: Option<&Expr>, + text_range: TextRange, + ) -> Value { + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .unwrap_or_else(|| self.unknown()); + + self.type_check(lhs.type_id, self.builtins.int, text_range); + self.type_check(rhs.type_id, self.builtins.int, text_range); + self.binary_op( + BinOp::LessThanEquals, + lhs.hir_id, + rhs.hir_id, + self.builtins.bool, + ) + } + + fn op_and(&mut self, lhs: Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + self.type_guard_stack.push(lhs.then_guards()); + + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bool))) + .unwrap_or_else(|| self.unknown()); + + self.type_guard_stack.pop().unwrap(); + + self.type_check(lhs.type_id, self.builtins.bool, text_range); + self.type_check(rhs.type_id, self.builtins.bool, text_range); + + let mut value = self.binary_op( + BinOp::LogicalAnd, + lhs.hir_id, + rhs.hir_id, + self.builtins.bool, + ); + value.guards.extend(lhs.guards); + value.guards.extend(rhs.guards); value } + + fn op_or(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { + self.type_guard_stack.push(lhs.then_guards()); + + let rhs = rhs + .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bool))) + .unwrap_or_else(|| self.unknown()); + + self.type_guard_stack.pop().unwrap(); + + self.type_check(lhs.type_id, self.builtins.bool, text_range); + self.type_check(rhs.type_id, self.builtins.bool, text_range); + self.binary_op(BinOp::LogicalOr, lhs.hir_id, rhs.hir_id, self.builtins.bool) + } } diff --git a/crates/rue-compiler/src/compiler/expr/cast_expr.rs b/crates/rue-compiler/src/compiler/expr/cast_expr.rs index 5c46d20..9310609 100644 --- a/crates/rue-compiler/src/compiler/expr/cast_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/cast_expr.rs @@ -1,22 +1,27 @@ use rue_parser::{AstNode, CastExpr}; -use crate::{compiler::Compiler, ty::Value, TypeId}; +use crate::{compiler::Compiler, ty::Value}; impl Compiler<'_> { - pub fn compile_cast_expr(&mut self, cast: &CastExpr, expected_type: Option) -> Value { - let Some(expr) = cast - .expr() - .map(|expr| self.compile_expr(&expr, expected_type)) - else { + pub fn compile_cast_expr(&mut self, cast: &CastExpr) -> Value { + // There is no expected type for the left hand side, since it's changed with a cast. + let Some(expr) = cast.expr().map(|expr| self.compile_expr(&expr, None)) else { return self.unknown(); }; - let ty = cast + // It's fine to default to unknown, since the cast check will succeed anyways. + let type_id = cast .ty() .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); - self.cast_check(expr.type_id, ty, cast.expr().unwrap().syntax().text_range()); + // Make sure that the cast is valid, but do it even if not to make the rest of the type checking succeed. + self.cast_check( + expr.type_id, + type_id, + cast.expr().unwrap().syntax().text_range(), + ); - Value::new(expr.hir_id, ty) + // Change the type of the value. We throw away type guards here. + Value::new(expr.hir_id, type_id) } } diff --git a/crates/rue-compiler/src/compiler/expr/group_expr.rs b/crates/rue-compiler/src/compiler/expr/group_expr.rs index d33a6bf..0a24c4d 100644 --- a/crates/rue-compiler/src/compiler/expr/group_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/group_expr.rs @@ -8,13 +8,11 @@ impl Compiler<'_> { group_expr: &GroupExpr, expected_type: Option, ) -> Value { - let Some(expr) = group_expr + // Compile the inner expression, or return unknown if it's missing. + // This would be a parser error, so no diagnostic is needed. + group_expr .expr() .map(|expr| self.compile_expr(&expr, expected_type)) - else { - return self.unknown(); - }; - - expr + .unwrap_or_else(|| self.unknown()) } } diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 0e74e69..8bfde66 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -47,7 +47,7 @@ impl Compiler<'_> { hir_id: HirId, text_range: TextRange, ) -> Option<(Guard, HirId)> { - if self.db.compare_type_raw(from, to) <= Comparison::Assignable { + if self.db.compare_type(from, to) <= Comparison::Assignable { self.db.warning( WarningKind::RedundantTypeCheck(self.type_name(from)), text_range, @@ -57,11 +57,11 @@ impl Compiler<'_> { match (self.db.ty(from).clone(), self.db.ty(to).clone()) { (Type::Any, Type::Pair(PairType { first, rest })) => { - if self.db.compare_type_raw(first, self.builtins.any) > Comparison::Equal { + if !self.db.compare_type(first, self.builtins.any).is_equal() { self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); } - if self.db.compare_type_raw(rest, self.builtins.any) > Comparison::Equal { + if !self.db.compare_type(rest, self.builtins.any).is_equal() { self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); } @@ -78,11 +78,11 @@ impl Compiler<'_> { Some((Guard::new(to, pair_type), hir_id)) } (Type::List(inner), Type::Pair(PairType { first, rest })) => { - if self.db.compare_type_raw(first, inner) > Comparison::Equal { + if !self.db.compare_type(first, inner).is_equal() { self.db.error(ErrorKind::NonListPairTypeGuard, text_range); } - if self.db.compare_type_raw(rest, from) > Comparison::Equal { + if !self.db.compare_type(rest, from).is_equal() { self.db.error(ErrorKind::NonListPairTypeGuard, text_range); } diff --git a/crates/rue-compiler/src/compiler/expr/index_access_expr.rs b/crates/rue-compiler/src/compiler/expr/index_access_expr.rs index e5ab3cf..7dc8dfe 100644 --- a/crates/rue-compiler/src/compiler/expr/index_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/index_access_expr.rs @@ -18,7 +18,12 @@ impl Compiler<'_> { let Some(index_token) = index_access.index() else { return self.unknown(); }; - let index = Self::compile_int_raw(&index_token); + + let index = index_token + .text() + .replace('_', "") + .parse() + .expect("failed to parse integer literal"); let Type::List(item_type) = self.db.ty(value.type_id).clone() else { self.db.error( diff --git a/crates/rue-compiler/src/compiler/expr/literal_expr.rs b/crates/rue-compiler/src/compiler/expr/literal_expr.rs index 3f9797a..aaf0408 100644 --- a/crates/rue-compiler/src/compiler/expr/literal_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/literal_expr.rs @@ -1,9 +1,8 @@ -use std::{fmt, str::FromStr}; - use clvmr::Allocator; +use num_bigint::BigInt; use rue_parser::{LiteralExpr, SyntaxKind, SyntaxToken}; -use crate::{compiler::Compiler, hir::Hir, ty::Value}; +use crate::{compiler::Compiler, hir::Hir, ty::Value, ErrorKind}; impl Compiler<'_> { pub fn compile_literal_expr(&mut self, literal: &LiteralExpr) -> Value { @@ -12,87 +11,96 @@ impl Compiler<'_> { }; match value.kind() { - SyntaxKind::Int => self.compile_int(&value), - SyntaxKind::Hex => self.compile_hex(&value), - SyntaxKind::String => self.compile_string(&value), - SyntaxKind::True => { - Value::new(self.db.alloc_hir(Hir::Atom(vec![1])), self.builtins.bool) - } - SyntaxKind::False => { - Value::new(self.db.alloc_hir(Hir::Atom(Vec::new())), self.builtins.bool) - } - SyntaxKind::Nil => { - Value::new(self.db.alloc_hir(Hir::Atom(Vec::new())), self.builtins.nil) - } + SyntaxKind::Int => self.compile_int_literal(&value), + SyntaxKind::Hex => self.compile_hex_literal(&value), + SyntaxKind::String => self.compile_string_literal(&value), + SyntaxKind::True => self.compile_bool_literal(true), + SyntaxKind::False => self.compile_bool_literal(false), + SyntaxKind::Nil => self.compile_nil_literal(), _ => unreachable!(), } } - pub fn compile_int_raw(int: &SyntaxToken) -> T - where - T: FromStr, - E: fmt::Debug, - { - int.text() - .replace('_', "") - .parse() - .expect("failed to parse into BigInt") + fn compile_bool_literal(&mut self, value: bool) -> Value { + let atom = if value { vec![1] } else { vec![] }; + Value::new(self.db.alloc_hir(Hir::Atom(atom)), self.builtins.bool) } - pub fn compile_int(&mut self, int: &SyntaxToken) -> Value { - let num = Self::compile_int_raw(int); + fn compile_nil_literal(&mut self) -> Value { + Value::new(self.db.alloc_hir(Hir::Atom(Vec::new())), self.builtins.nil) + } - let mut allocator = Allocator::new(); - let ptr = allocator - .new_number(num) - .expect("number is too large to be represented in memory in an Allocator instance"); + fn compile_int_literal(&mut self, int: &SyntaxToken) -> Value { + // Parse the literal into `BigInt`. + // It should not be possible to have a syntax error at this point. + let bigint = int + .text() + .replace('_', "") + .parse() + .expect("failed to parse integer literal"); - Value::new( - self.db - .alloc_hir(Hir::Atom(allocator.atom(ptr).as_ref().to_vec())), - self.builtins.int, - ) + let atom = Self::bigint_to_bytes(bigint).unwrap_or_else(|| { + self.db.error(ErrorKind::IntegerTooLarge, int.text_range()); + Vec::new() + }); + + // Extract the atom representation of the number. + Value::new(self.db.alloc_hir(Hir::Atom(atom)), self.builtins.int) } - fn compile_hex(&mut self, hex: &SyntaxToken) -> Value { - let Ok(bytes) = hex::decode( + fn compile_hex_literal(&mut self, hex: &SyntaxToken) -> Value { + // Parse the hex literal into bytes. + // It should not be possible to have a syntax error at this point. + let bytes = hex::decode( hex.text() .replace("0x", "") .replace("0X", "") .replace('_', ""), - ) else { - return Value::new(self.builtins.unknown_hir, self.builtins.bytes); - }; + ) + .expect("failed to parse hex literal"); let bytes_len = bytes.len(); + // Return the atom with the corresponding type based on the length. Value::new( self.db.alloc_hir(Hir::Atom(bytes)), if bytes_len == 32 { + // We'll assume this is a `Bytes32` since it's the correct length. + // This makes putting hashes in the code more convenient. self.builtins.bytes32 } else if bytes_len == 48 { + // We'll assume this is a `PublicKey` since it's the correct length. + // It's unlikely to intend the type being `Bytes`, but you can cast if needed. self.builtins.public_key } else { + // Everything else is just `Bytes`. + // Leading zeros are not removed, so `0x00` is different than `0`. self.builtins.bytes }, ) } - fn compile_string(&mut self, string: &SyntaxToken) -> Value { + fn compile_string_literal(&mut self, string: &SyntaxToken) -> Value { + // Extract the quote character. let text = string.text(); let quote = text.chars().next().unwrap(); - let after_prefix = &text[1..]; - let before_suffix = after_prefix.strip_suffix(quote).unwrap_or(after_prefix); - - let bytes = before_suffix.as_bytes(); + // Remove the quotes, if present, and create an atom. Value::new( - self.db.alloc_hir(Hir::Atom(bytes.to_vec())), - if bytes.len() == 32 { - self.builtins.bytes32 - } else { - self.builtins.bytes - }, + self.db + .alloc_hir(Hir::Atom(text.replace(quote, "").as_bytes().to_vec())), + self.builtins.bytes, ) } + + pub fn bigint_to_bytes(bigint: BigInt) -> Option> { + // Create a CLVM allocator. + let mut allocator = Allocator::new(); + + // Try to allocate the number. + let ptr = allocator.new_number(bigint).ok()?; + + // Extract the atom representation of the number. + Some(allocator.atom(ptr).as_ref().to_vec()) + } } diff --git a/crates/rue-compiler/src/compiler/expr/pair_expr.rs b/crates/rue-compiler/src/compiler/expr/pair_expr.rs index 9670ea0..9555d10 100644 --- a/crates/rue-compiler/src/compiler/expr/pair_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/pair_expr.rs @@ -8,44 +8,40 @@ use crate::{ }; impl Compiler<'_> { - pub fn compile_pair_expr( - &mut self, - pair_expr: &PairExpr, - expected_type: Option, - ) -> Value { - let expected_first = expected_type.and_then(|ty| match self.db.ty(ty) { - Type::Pair(pair) => Some(pair.first), - _ => None, - }); + pub fn compile_pair_expr(&mut self, pair: &PairExpr, expected_type: Option) -> Value { + // Extract the first and rest type out of the expected type. + let first = expected_type.and_then(|type_id| self.db.first_type(type_id)); + let rest = expected_type.and_then(|type_id| self.db.rest_type(type_id)); - let expected_rest = expected_type.and_then(|ty| match self.db.ty(ty) { - Type::Pair(pair) => Some(pair.rest), - _ => None, - }); + // Compile the first expression, if present. + // It's a parser error if not, so it's fine to return unknown. + let first = pair + .first() + .map(|expr| { + let value = self.compile_expr(&expr, first); + self.type_check( + value.type_id, + first.unwrap_or(self.builtins.unknown), + expr.syntax().text_range(), + ); + value + }) + .unwrap_or_else(|| self.unknown()); - let first = if let Some(first) = pair_expr.first() { - let value = self.compile_expr(&first, expected_first); - self.type_check( - value.type_id, - expected_first.unwrap_or(self.builtins.unknown), - first.syntax().text_range(), - ); - value - } else { - self.unknown() - }; - - let rest = if let Some(rest) = pair_expr.rest() { - let value = self.compile_expr(&rest, expected_rest); - self.type_check( - value.type_id, - expected_rest.unwrap_or(self.builtins.unknown), - rest.syntax().text_range(), - ); - value - } else { - self.unknown() - }; + // Compile the rest expression, if present. + // It's a parser error if not, so it's fine to return unknown. + let rest = pair + .first() + .map(|expr| { + let value = self.compile_expr(&expr, rest); + self.type_check( + value.type_id, + rest.unwrap_or(self.builtins.unknown), + expr.syntax().text_range(), + ); + value + }) + .unwrap_or_else(|| self.unknown()); let hir_id = self.db.alloc_hir(Hir::Pair(first.hir_id, rest.hir_id)); let type_id = self.db.alloc_type(Type::Pair(PairType { @@ -53,6 +49,8 @@ impl Compiler<'_> { rest: rest.type_id, })); + // We throw away type guards by creating this new value. + // They shouldn't be relevant since the type is not `Bool`. Value::new(hir_id, type_id) } } diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 3e41dc2..de2c106 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -31,7 +31,18 @@ impl Compiler<'_> { let symbol_id = match item { PathItem::Symbol(symbol_id) => symbol_id, - PathItem::Type(..) => { + PathItem::Type(type_id) => { + if let Type::EnumVariant(variant_type) = self.db.ty(type_id).clone() { + let Type::Enum(enum_type) = self.db.ty(variant_type.enum_type) else { + unreachable!(); + }; + + if enum_type.has_fields { + self.db.error(ErrorKind::EnumVariantWithFields, text_range); + } + + return Value::new(variant_type.discriminant, type_id); + } self.db.error(ErrorKind::ExpectedSymbolPath, text_range); return self.unknown(); } diff --git a/crates/rue-compiler/src/compiler/expr/prefix_expr.rs b/crates/rue-compiler/src/compiler/expr/prefix_expr.rs index d68207a..b0dadab 100644 --- a/crates/rue-compiler/src/compiler/expr/prefix_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/prefix_expr.rs @@ -8,24 +8,39 @@ use crate::{ impl Compiler<'_> { pub fn compile_prefix_expr(&mut self, prefix_expr: &PrefixExpr) -> Value { - let Some(expr) = prefix_expr.expr() else { - return self.unknown(); + // Determine the expected type based on the prefix operator. + let expected_type = match prefix_expr.op() { + Some(PrefixOp::Not) => Some(self.builtins.bool), + Some(PrefixOp::Neg) => Some(self.builtins.int), + None => None, }; - let expr = self.compile_expr(&expr, None); + // Compile the expression. + let expr = prefix_expr + .expr() + .map(|expr| self.compile_expr(&expr, None)) + .unwrap_or_else(|| self.unknown()); + // If the expression has no prefix operator, return the expression as is. + // This is likely a parser error of some sort (in fact, it shouldn't happen). let Some(op) = prefix_expr.op() else { - return self.unknown(); + return expr; }; + // Check the type of the expression. + self.type_check( + expr.type_id, + expected_type.unwrap_or(self.builtins.unknown), + prefix_expr + .expr() + .map_or(prefix_expr.syntax().text_range(), |ast| { + ast.syntax().text_range() + }), + ); + match op { PrefixOp::Not => { - self.type_check( - expr.type_id, - self.builtins.bool, - prefix_expr.syntax().text_range(), - ); - + // Negate the expression and its type guards. let mut value = Value::new(self.db.alloc_hir(Hir::Not(expr.hir_id)), self.builtins.bool); @@ -35,22 +50,16 @@ impl Compiler<'_> { value } - PrefixOp::Neg => { - self.type_check( - expr.type_id, - self.builtins.int, - prefix_expr.syntax().text_range(), - ); - - Value::new( - self.db.alloc_hir(Hir::BinaryOp { - op: BinOp::Subtract, - lhs: self.builtins.nil_hir, - rhs: expr.hir_id, - }), - self.builtins.int, - ) - } + PrefixOp::Neg => Value::new( + // Subtract the expression from nil. + // TODO: Is there a more efficient way to do this? + self.db.alloc_hir(Hir::BinaryOp { + op: BinOp::Subtract, + lhs: self.builtins.nil_hir, + rhs: expr.hir_id, + }), + self.builtins.int, + ), } } } diff --git a/crates/rue-compiler/src/compiler/item/enum_item.rs b/crates/rue-compiler/src/compiler/item/enum_item.rs index d1e10f3..ee8d453 100644 --- a/crates/rue-compiler/src/compiler/item/enum_item.rs +++ b/crates/rue-compiler/src/compiler/item/enum_item.rs @@ -1,35 +1,60 @@ -use indexmap::{IndexMap, IndexSet}; +use std::collections::HashSet; + +use indexmap::IndexMap; +use num_bigint::BigInt; +use num_traits::Zero; use rue_parser::EnumItem; use crate::{ compiler::Compiler, - ty::{EnumType, EnumVariant, Type}, + hir::Hir, + ty::{EnumType, EnumVariantType, Type}, ErrorKind, TypeId, }; impl Compiler<'_> { - /// Define a type for an enum in the current scope. - /// This creates the enum variants as well, but they are left as unknown types. pub fn declare_enum_item(&mut self, enum_item: &EnumItem) -> TypeId { let mut variants = IndexMap::new(); + let mut has_fields = false; + // Add each of the variants to the enum. for variant in enum_item.variants() { + // We don't add variants with missing names, since + // enum types don't support them currently. let Some(name) = variant.name() else { continue; }; - // Silently ignore duplicate variants, since they will be caught later. + // Don't overwrite existing variant names. if variants.contains_key(name.text()) { + self.db.error( + ErrorKind::DuplicateEnumVariant(name.text().to_string()), + name.text_range(), + ); continue; } + // Check if the variant has fields. + if variant.fields().is_some() { + has_fields = true; + } + + // Allocate a new type for the variant. + // It has to be `Unknown` for now, since field types may not be declared yet. let type_id = self.db.alloc_type(Type::Unknown); + + // Add the variant to the enum and define the token for the variant. variants.insert(name.to_string(), type_id); self.db.insert_type_token(type_id, name); } - let type_id = self.db.alloc_type(Type::Enum(EnumType { variants })); + // Allocate a new type for the enum. + let type_id = self.db.alloc_type(Type::Enum(EnumType { + has_fields, + variants, + })); + // Add the enum to the scope and define the token for the enum. if let Some(name) = enum_item.name() { self.scope_mut().define_type(name.to_string(), type_id); self.db.insert_type_token(type_id, name); @@ -38,45 +63,90 @@ impl Compiler<'_> { type_id } - /// Compile and resolve an enum type, and each of its variants' struct fields. - pub fn compile_enum_item(&mut self, enum_item: &EnumItem, type_id: TypeId) { - self.type_definition_stack.push(type_id); - - let Type::Enum(enum_type) = self.db.ty(type_id).clone() else { + pub fn compile_enum_item(&mut self, enum_item: &EnumItem, enum_type_id: TypeId) { + let Type::Enum(enum_type) = self.db.ty(enum_type_id).clone() else { unreachable!(); }; - let mut visited_variants = IndexSet::new(); + // We add this here to track types that are referenced by the enum. + self.type_definition_stack.push(enum_type_id); + + let mut names = HashSet::new(); + let mut discriminants = HashSet::new(); + let mut highest_discriminant = None; + // Compile each of the variants. for variant in enum_item.variants() { + // We don't add variants with missing names, since + // enum types don't support them currently. let Some(name) = variant.name() else { continue; }; - // If the variant is a duplicate, we don't want to overwrite the existing variant. - if !visited_variants.insert(name.to_string()) { - self.db.error( - ErrorKind::DuplicateEnumVariant(name.to_string()), - name.text_range(), - ); + // Don't overwrite existing variant names. + if !names.insert(name.to_string()) { continue; } - let variant_type = enum_type.variants[name.text()]; - - self.type_definition_stack.push(variant_type); + // Get the type id for the variant. + let variant_type_id = enum_type.variants[name.text()]; + + // Track types that are referenced by the variant. + self.type_definition_stack.push(variant_type_id); + + // Compile the fields of the variant. + let fields = variant + .fields() + .map(|ast| self.compile_struct_fields(ast.fields())) + .unwrap_or_default(); + + // Get the discriminant of the variant. + let discriminant = if let Some(token) = variant.discriminant() { + let discriminant: BigInt = token + .text() + .replace('_', "") + .parse() + .expect("failed to parse integer literal"); + + if !discriminants.insert(discriminant.clone()) { + self.db.error( + ErrorKind::DuplicateEnumDiscriminant(discriminant.to_string()), + token.text_range(), + ); + } + + if let Some(highest) = highest_discriminant.as_mut() { + if &discriminant > highest { + highest_discriminant = Some(discriminant.clone()); + } + } else { + highest_discriminant = Some(discriminant.clone()); + } + + discriminant + } else if let Some(discriminant) = highest_discriminant.as_mut() { + *discriminant += 1; + discriminant.clone() + } else { + highest_discriminant = Some(BigInt::zero()); + BigInt::zero() + }; - let fields = self.compile_struct_fields(variant.fields()); + let atom = Self::bigint_to_bytes(discriminant).unwrap_or_else(|| { + self.db.error( + ErrorKind::EnumDiscriminantTooLarge, + variant + .discriminant() + .map_or(name.text_range(), |token| token.text_range()), + ); + Vec::new() + }); - let discriminant = variant - .discriminant() - .map_or(self.builtins.unknown_hir, |discriminant| { - self.compile_int(&discriminant).hir_id - }); + let discriminant = self.db.alloc_hir(Hir::Atom(atom)); - *self.db.ty_mut(variant_type) = Type::EnumVariant(EnumVariant { - name: name.to_string(), - enum_type: type_id, + // Update the variant to use the real `EnumVariant` type. + *self.db.ty_mut(variant_type_id) = Type::EnumVariant(EnumVariantType { + enum_type: enum_type_id, fields, discriminant, }); diff --git a/crates/rue-compiler/src/database/comparison.rs b/crates/rue-compiler/src/database/comparison.rs index 7682405..089c9ee 100644 --- a/crates/rue-compiler/src/database/comparison.rs +++ b/crates/rue-compiler/src/database/comparison.rs @@ -18,6 +18,20 @@ pub enum Comparison { Unrelated, } +impl Comparison { + pub fn is_equal(self) -> bool { + self == Self::Equal + } + + pub fn is_assignable(self) -> bool { + self <= Self::Assignable + } + + pub fn is_castable(self) -> bool { + self <= Self::Castable + } +} + impl BitAnd for Comparison { type Output = Self; diff --git a/crates/rue-compiler/src/database/type_system.rs b/crates/rue-compiler/src/database/type_system.rs index e59e918..f3a7c7d 100644 --- a/crates/rue-compiler/src/database/type_system.rs +++ b/crates/rue-compiler/src/database/type_system.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; use crate::{ - ty::{EnumType, EnumVariant, FunctionType, PairType, StructType, Type}, + ty::{EnumType, EnumVariantType, FunctionType, PairType, StructType, Type}, Comparison, Database, TypeId, }; @@ -31,7 +31,7 @@ impl Database { self.substitute_type_visitor(type_id, substitutions, &mut HashSet::new()) } - pub fn compare_type_raw(&self, lhs: TypeId, rhs: TypeId) -> Comparison { + pub fn compare_type(&self, lhs: TypeId, rhs: TypeId) -> Comparison { self.compare_type_visitor( lhs, rhs, @@ -56,6 +56,27 @@ impl Database { self.is_cyclic_visitor(type_id, &mut HashSet::new()) } + pub fn first_type(&self, type_id: TypeId) -> Option { + let Type::Pair(pair) = self.ty(type_id) else { + return None; + }; + Some(pair.first) + } + + pub fn rest_type(&self, type_id: TypeId) -> Option { + let Type::Pair(pair) = self.ty(type_id) else { + return None; + }; + Some(pair.rest) + } + + pub fn non_optional(&mut self, ty: TypeId) -> TypeId { + match self.ty(ty) { + Type::Optional(inner) => self.non_optional(*inner), + _ => ty, + } + } + pub fn substitute_type_visitor( &mut self, type_id: TypeId, @@ -86,6 +107,7 @@ impl Database { } Type::Enum(enum_type) => { let new_enum = EnumType { + has_fields: enum_type.has_fields, variants: enum_type .variants .iter() @@ -105,8 +127,7 @@ impl Database { } } Type::EnumVariant(enum_variant) => { - let new_variant = EnumVariant { - name: enum_variant.name.clone(), + let new_variant = EnumVariantType { enum_type: enum_variant.enum_type, fields: enum_variant .fields @@ -462,7 +483,7 @@ mod tests { use crate::{ compiler::{builtins, Builtins}, - ty::{EnumType, EnumVariant, FunctionType, PairType, Rest, StructType}, + ty::{EnumType, EnumVariantType, FunctionType, PairType, Rest, StructType}, }; use super::*; @@ -509,10 +530,7 @@ mod tests { generic_types: vec![a, b], })); - assert_eq!( - db.compare_type_raw(substituted, expected), - Comparison::Equal - ); + assert_eq!(db.compare_type(substituted, expected), Comparison::Equal); } #[test] @@ -520,25 +538,19 @@ mod tests { let (mut db, ty) = setup(); let int_alias = db.alloc_type(Type::Alias(ty.int)); - assert_eq!(db.compare_type_raw(int_alias, ty.int), Comparison::Equal); - assert_eq!(db.compare_type_raw(ty.int, int_alias), Comparison::Equal); - assert_eq!(db.compare_type_raw(int_alias, int_alias), Comparison::Equal); + assert_eq!(db.compare_type(int_alias, ty.int), Comparison::Equal); + assert_eq!(db.compare_type(ty.int, int_alias), Comparison::Equal); + assert_eq!(db.compare_type(int_alias, int_alias), Comparison::Equal); let double_alias = db.alloc_type(Type::Alias(int_alias)); - assert_eq!(db.compare_type_raw(double_alias, ty.int), Comparison::Equal); - assert_eq!(db.compare_type_raw(ty.int, double_alias), Comparison::Equal); + assert_eq!(db.compare_type(double_alias, ty.int), Comparison::Equal); + assert_eq!(db.compare_type(ty.int, double_alias), Comparison::Equal); assert_eq!( - db.compare_type_raw(double_alias, double_alias), - Comparison::Equal - ); - assert_eq!( - db.compare_type_raw(double_alias, int_alias), - Comparison::Equal - ); - assert_eq!( - db.compare_type_raw(int_alias, double_alias), + db.compare_type(double_alias, double_alias), Comparison::Equal ); + assert_eq!(db.compare_type(double_alias, int_alias), Comparison::Equal); + assert_eq!(db.compare_type(int_alias, double_alias), Comparison::Equal); } #[test] @@ -546,48 +558,45 @@ mod tests { let (mut db, ty) = setup(); let a = db.alloc_type(Type::Generic); let b = db.alloc_type(Type::Generic); - assert_eq!(db.compare_type_raw(a, b), Comparison::Unrelated); - assert_eq!(db.compare_type_raw(a, ty.int), Comparison::Unrelated); - assert_eq!(db.compare_type_raw(ty.int, a), Comparison::Unrelated); - assert_eq!(db.compare_type_raw(a, a), Comparison::Equal); + assert_eq!(db.compare_type(a, b), Comparison::Unrelated); + assert_eq!(db.compare_type(a, ty.int), Comparison::Unrelated); + assert_eq!(db.compare_type(ty.int, a), Comparison::Unrelated); + assert_eq!(db.compare_type(a, a), Comparison::Equal); } #[test] fn test_any_type() { let (db, ty) = setup(); - assert_eq!(db.compare_type_raw(ty.any, ty.int), Comparison::Castable); - assert_eq!(db.compare_type_raw(ty.any, ty.any), Comparison::Equal); - assert_eq!(db.compare_type_raw(ty.int, ty.any), Comparison::Assignable); + assert_eq!(db.compare_type(ty.any, ty.int), Comparison::Castable); + assert_eq!(db.compare_type(ty.any, ty.any), Comparison::Equal); + assert_eq!(db.compare_type(ty.int, ty.any), Comparison::Assignable); } #[test] fn test_unknown_type() { let (db, ty) = setup(); - assert_eq!(db.compare_type_raw(ty.any, ty.unknown), Comparison::Equal); - assert_eq!(db.compare_type_raw(ty.int, ty.unknown), Comparison::Equal); - assert_eq!(db.compare_type_raw(ty.unknown, ty.int), Comparison::Equal); - assert_eq!(db.compare_type_raw(ty.unknown, ty.any), Comparison::Equal); + assert_eq!(db.compare_type(ty.any, ty.unknown), Comparison::Equal); + assert_eq!(db.compare_type(ty.int, ty.unknown), Comparison::Equal); + assert_eq!(db.compare_type(ty.unknown, ty.int), Comparison::Equal); + assert_eq!(db.compare_type(ty.unknown, ty.any), Comparison::Equal); } #[test] fn test_atom_types() { let (db, ty) = setup(); + assert_eq!(db.compare_type(ty.bytes, ty.bytes32), Comparison::Superset); + assert_eq!(db.compare_type(ty.bytes32, ty.bytes), Comparison::Equal); assert_eq!( - db.compare_type_raw(ty.bytes, ty.bytes32), - Comparison::Superset - ); - assert_eq!(db.compare_type_raw(ty.bytes32, ty.bytes), Comparison::Equal); - assert_eq!( - db.compare_type_raw(ty.bytes, ty.public_key), + db.compare_type(ty.bytes, ty.public_key), Comparison::Superset ); assert_eq!( - db.compare_type_raw(ty.public_key, ty.bytes), + db.compare_type(ty.public_key, ty.bytes), Comparison::Castable ); - assert_eq!(db.compare_type_raw(ty.bytes, ty.int), Comparison::Castable); - assert_eq!(db.compare_type_raw(ty.int, ty.bytes), Comparison::Castable); - assert_eq!(db.compare_type_raw(ty.int, ty.int), Comparison::Equal); + assert_eq!(db.compare_type(ty.bytes, ty.int), Comparison::Castable); + assert_eq!(db.compare_type(ty.int, ty.bytes), Comparison::Castable); + assert_eq!(db.compare_type(ty.int, ty.int), Comparison::Equal); } #[test] @@ -595,21 +604,15 @@ mod tests { let (mut db, ty) = setup(); let list = db.alloc_type(Type::List(ty.int)); - assert_eq!(db.compare_type_raw(ty.nil, ty.int), Comparison::Castable); - assert_eq!(db.compare_type_raw(ty.nil, ty.bool), Comparison::Castable); + assert_eq!(db.compare_type(ty.nil, ty.int), Comparison::Castable); + assert_eq!(db.compare_type(ty.nil, ty.bool), Comparison::Castable); + assert_eq!(db.compare_type(ty.nil, ty.bytes), Comparison::Assignable); + assert_eq!(db.compare_type(ty.nil, ty.bytes32), Comparison::Unrelated); assert_eq!( - db.compare_type_raw(ty.nil, ty.bytes), - Comparison::Assignable - ); - assert_eq!( - db.compare_type_raw(ty.nil, ty.bytes32), - Comparison::Unrelated - ); - assert_eq!( - db.compare_type_raw(ty.nil, ty.public_key), + db.compare_type(ty.nil, ty.public_key), Comparison::Unrelated ); - assert_eq!(db.compare_type_raw(ty.nil, list), Comparison::Assignable); + assert_eq!(db.compare_type(ty.nil, list), Comparison::Assignable); } #[test] @@ -620,44 +623,35 @@ mod tests { first: ty.int, rest: ty.int, })); - assert_eq!(db.compare_type_raw(int_pair, int_pair), Comparison::Equal); + assert_eq!(db.compare_type(int_pair, int_pair), Comparison::Equal); let bytes_pair = db.alloc_type(Type::Pair(PairType { first: ty.bytes, rest: ty.bytes, })); - assert_eq!( - db.compare_type_raw(int_pair, bytes_pair), - Comparison::Castable - ); - assert_eq!( - db.compare_type_raw(bytes_pair, int_pair), - Comparison::Castable - ); + assert_eq!(db.compare_type(int_pair, bytes_pair), Comparison::Castable); + assert_eq!(db.compare_type(bytes_pair, int_pair), Comparison::Castable); let bytes32_pair = db.alloc_type(Type::Pair(PairType { first: ty.bytes32, rest: ty.bytes32, })); assert_eq!( - db.compare_type_raw(bytes_pair, bytes32_pair), + db.compare_type(bytes_pair, bytes32_pair), Comparison::Superset ); - assert_eq!( - db.compare_type_raw(bytes32_pair, bytes_pair), - Comparison::Equal - ); + assert_eq!(db.compare_type(bytes32_pair, bytes_pair), Comparison::Equal); let bytes32_bytes = db.alloc_type(Type::Pair(PairType { first: ty.bytes32, rest: ty.bytes, })); assert_eq!( - db.compare_type_raw(bytes_pair, bytes32_bytes), + db.compare_type(bytes_pair, bytes32_bytes), Comparison::Superset ); assert_eq!( - db.compare_type_raw(bytes32_bytes, bytes_pair), + db.compare_type(bytes32_bytes, bytes_pair), Comparison::Equal ); } @@ -667,36 +661,27 @@ mod tests { let (mut db, ty) = setup(); let int_list = db.alloc_type(Type::List(ty.int)); - assert_eq!(db.compare_type_raw(int_list, int_list), Comparison::Equal); + assert_eq!(db.compare_type(int_list, int_list), Comparison::Equal); let pair_list = db.alloc_type(Type::Pair(PairType { first: ty.int, rest: int_list, })); - assert_eq!( - db.compare_type_raw(pair_list, int_list), - Comparison::Assignable - ); - assert_eq!( - db.compare_type_raw(int_list, pair_list), - Comparison::Superset - ); + assert_eq!(db.compare_type(pair_list, int_list), Comparison::Assignable); + assert_eq!(db.compare_type(int_list, pair_list), Comparison::Superset); let pair_nil = db.alloc_type(Type::Pair(PairType { first: ty.int, rest: ty.nil, })); - assert_eq!( - db.compare_type_raw(pair_nil, int_list), - Comparison::Assignable - ); + assert_eq!(db.compare_type(pair_nil, int_list), Comparison::Assignable); let pair_unrelated_first = db.alloc_type(Type::Pair(PairType { first: pair_list, rest: ty.nil, })); assert_eq!( - db.compare_type_raw(pair_unrelated_first, int_list), + db.compare_type(pair_unrelated_first, int_list), Comparison::Unrelated ); @@ -705,7 +690,7 @@ mod tests { rest: ty.int, })); assert_eq!( - db.compare_type_raw(pair_unrelated_rest, int_list), + db.compare_type(pair_unrelated_rest, int_list), Comparison::Unrelated ); } @@ -717,21 +702,18 @@ mod tests { let two_ints = db.alloc_type(Type::Struct(StructType { fields: fields(&[ty.int, ty.int]), })); - assert_eq!(db.compare_type_raw(two_ints, two_ints), Comparison::Equal); + assert_eq!(db.compare_type(two_ints, two_ints), Comparison::Equal); let one_int = db.alloc_type(Type::Struct(StructType { fields: fields(&[ty.int]), })); - assert_eq!( - db.compare_type_raw(one_int, two_ints), - Comparison::Unrelated - ); + assert_eq!(db.compare_type(one_int, two_ints), Comparison::Unrelated); let empty_struct = db.alloc_type(Type::Struct(StructType { fields: fields(&[]), })); assert_eq!( - db.compare_type_raw(empty_struct, empty_struct), + db.compare_type(empty_struct, empty_struct), Comparison::Equal ); } @@ -742,25 +724,19 @@ mod tests { let enum_type = db.alloc_type(Type::Unknown); - let variant = db.alloc_type(Type::EnumVariant(EnumVariant { - name: "Variant".to_string(), + let variant = db.alloc_type(Type::EnumVariant(EnumVariantType { enum_type, fields: fields(&[ty.int]), discriminant: ty.unknown_hir, })); *db.ty_mut(enum_type) = Type::Enum(EnumType { + has_fields: true, variants: fields(&[variant]), }); - assert_eq!( - db.compare_type_raw(enum_type, variant), - Comparison::Superset - ); - assert_eq!( - db.compare_type_raw(variant, enum_type), - Comparison::Assignable - ); + assert_eq!(db.compare_type(enum_type, variant), Comparison::Superset); + assert_eq!(db.compare_type(variant, enum_type), Comparison::Assignable); } #[test] @@ -773,10 +749,7 @@ mod tests { return_type: ty.bool, generic_types: Vec::new(), })); - assert_eq!( - db.compare_type_raw(int_to_bool, int_to_bool), - Comparison::Equal - ); + assert_eq!(db.compare_type(int_to_bool, int_to_bool), Comparison::Equal); let int_to_int = db.alloc_type(Type::Function(FunctionType { param_types: vec![ty.int], @@ -785,11 +758,11 @@ mod tests { generic_types: Vec::new(), })); assert_eq!( - db.compare_type_raw(int_to_bool, int_to_int), + db.compare_type(int_to_bool, int_to_int), Comparison::Castable ); assert_eq!( - db.compare_type_raw(int_to_int, int_to_bool), + db.compare_type(int_to_int, int_to_bool), Comparison::Superset ); @@ -801,11 +774,11 @@ mod tests { generic_types: Vec::new(), })); assert_eq!( - db.compare_type_raw(int_to_bool, int_list_to_bool), + db.compare_type(int_to_bool, int_list_to_bool), Comparison::Unrelated ); assert_eq!( - db.compare_type_raw(int_list_to_bool, int_to_bool), + db.compare_type(int_list_to_bool, int_to_bool), Comparison::Unrelated ); } diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index 14b75f0..b930d2e 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -152,6 +152,15 @@ pub enum ErrorKind { #[error("duplicate enum variant `{0}`")] DuplicateEnumVariant(String), + #[error("duplicate enum discriminant `{0}`")] + DuplicateEnumDiscriminant(String), + + #[error("enum discriminant too large")] + EnumDiscriminantTooLarge, + + #[error("cannot reference enum variants of enums with fields")] + EnumVariantWithFields, + #[error("unknown enum variant `{0}`")] UnknownEnumVariant(String), @@ -196,6 +205,9 @@ pub enum ErrorKind { #[error("cannot check equality on non-atom type `{0}`")] NonAtomEquality(String), + + #[error("integer too large to allocate in memory")] + IntegerTooLarge, } /// Join a list of names into a string, wrapped in backticks. diff --git a/crates/rue-compiler/src/optimizer.rs b/crates/rue-compiler/src/optimizer.rs index 0fb5bdc..c02eaa8 100644 --- a/crates/rue-compiler/src/optimizer.rs +++ b/crates/rue-compiler/src/optimizer.rs @@ -84,7 +84,6 @@ impl<'a> Optimizer<'a> { fn opt_definition(&mut self, env_id: EnvironmentId, symbol_id: SymbolId) -> LirId { match self.db.symbol(symbol_id).clone() { - Symbol::Unknown | Symbol::Module(..) => unreachable!(), Symbol::Function(Function { hir_id, scope_id, .. }) => { @@ -104,28 +103,45 @@ impl<'a> Optimizer<'a> { self.db.alloc_lir(Lir::FunctionBody(body)) } Symbol::Const(Const { hir_id, .. }) => self.opt_hir(env_id, hir_id), - Symbol::Parameter(..) | Symbol::InlineFunction(..) | Symbol::InlineConst(..) => { - unreachable!(); + Symbol::Let(symbol) if self.graph.symbol_usages(symbol_id) > 0 => { + self.opt_hir(env_id, symbol.hir_id) } - Symbol::Let(symbol) => self.opt_hir(env_id, symbol.hir_id), + Symbol::Unknown + | Symbol::Module(..) + | Symbol::Parameter(..) + | Symbol::Let(..) + | Symbol::InlineFunction(..) + | Symbol::InlineConst(..) => unreachable!(), } } fn opt_hir(&mut self, env_id: EnvironmentId, hir_id: HirId) -> LirId { - match self.db.hir(hir_id) { + match self.db.hir(hir_id).clone() { Hir::Unknown => self.db.alloc_lir(Lir::Atom(Vec::new())), Hir::Atom(atom) => self.db.alloc_lir(Lir::Atom(atom.clone())), - Hir::Pair(first, rest) => self.opt_pair(env_id, *first, *rest), - Hir::Reference(symbol_id) => self.opt_reference(env_id, *symbol_id), + Hir::Pair(first, rest) => self.opt_pair(env_id, first, rest), + Hir::Reference(symbol_id) => self.opt_reference(env_id, symbol_id), Hir::Definition { scope_id, hir_id } => { - self.opt_env_definition(env_id, *scope_id, *hir_id) + let definition_env_id = self.graph.env(scope_id); + for symbol_id in self.db.env_mut(definition_env_id).definitions() { + let Symbol::Let(..) = self.db.symbol(symbol_id) else { + continue; + }; + + if self.graph.symbol_usages(symbol_id) == 1 { + self.db + .env_mut(definition_env_id) + .remove_definition(symbol_id); + } + } + self.opt_env_definition(env_id, scope_id, hir_id) } Hir::FunctionCall { callee, args, varargs, } => { - if let Hir::Reference(symbol_id) = self.db.hir(*callee) { + if let Hir::Reference(symbol_id) = self.db.hir(callee) { if let Symbol::InlineFunction(Function { scope_id, ty, @@ -140,11 +156,11 @@ impl<'a> Optimizer<'a> { &ty.clone(), *hir_id, args.clone(), - *varargs, + varargs, ); } } - self.opt_function_call(env_id, *callee, args.clone(), *varargs) + self.opt_function_call(env_id, callee, args, varargs) } Hir::BinaryOp { op, lhs, rhs } => { let handler = match op { @@ -164,21 +180,21 @@ impl<'a> Optimizer<'a> { BinOp::LogicalAnd => Self::opt_logical_and, BinOp::LogicalOr => Self::opt_logical_or, }; - handler(self, env_id, *lhs, *rhs) + handler(self, env_id, lhs, rhs) } - Hir::First(value) => self.opt_first(env_id, *value), - Hir::Rest(value) => self.opt_rest(env_id, *value), - Hir::Not(value) => self.opt_not(env_id, *value), - Hir::Raise(value) => self.opt_raise(env_id, *value), - Hir::Sha256(value) => self.opt_sha256(env_id, *value), - Hir::IsCons(value) => self.opt_is_cons(env_id, *value), - Hir::Strlen(value) => self.opt_strlen(env_id, *value), - Hir::PubkeyForExp(value) => self.opt_pubkey_for_exp(env_id, *value), + Hir::First(value) => self.opt_first(env_id, value), + Hir::Rest(value) => self.opt_rest(env_id, value), + Hir::Not(value) => self.opt_not(env_id, value), + Hir::Raise(value) => self.opt_raise(env_id, value), + Hir::Sha256(value) => self.opt_sha256(env_id, value), + Hir::IsCons(value) => self.opt_is_cons(env_id, value), + Hir::Strlen(value) => self.opt_strlen(env_id, value), + Hir::PubkeyForExp(value) => self.opt_pubkey_for_exp(env_id, value), Hir::If { condition, then_block, else_block, - } => self.opt_if(env_id, *condition, *then_block, *else_block), + } => self.opt_if(env_id, condition, then_block, else_block), } } @@ -259,6 +275,9 @@ impl<'a> Optimizer<'a> { } Symbol::InlineFunction(..) => self.db.alloc_lir(Lir::Atom(vec![])), Symbol::InlineConst(Const { hir_id, .. }) => self.opt_hir(env_id, hir_id), + Symbol::Let(symbol) if self.graph.symbol_usages(symbol_id) == 1 => { + self.opt_hir(env_id, symbol.hir_id) + } Symbol::Let(..) | Symbol::Const(..) => self.opt_path(env_id, symbol_id), Symbol::Parameter { .. } => { for inline_parameter_map in self.inline_parameter_stack.iter().rev() { diff --git a/crates/rue-compiler/src/optimizer/dependency_graph.rs b/crates/rue-compiler/src/optimizer/dependency_graph.rs index ad673e8..ab4e0f3 100644 --- a/crates/rue-compiler/src/optimizer/dependency_graph.rs +++ b/crates/rue-compiler/src/optimizer/dependency_graph.rs @@ -355,12 +355,6 @@ impl<'a> GraphTraversal<'a> { self.compute_hir_edges(child_scope_id, hir_id, visited); } Hir::Reference(symbol_id) => { - self.graph - .symbol_usages - .entry(symbol_id) - .and_modify(|count| *count += 1) - .or_insert(1); - match self.db.symbol(symbol_id).clone() { Symbol::Unknown | Symbol::Module(..) => unreachable!(), Symbol::Function(fun) | Symbol::InlineFunction(fun) => { diff --git a/crates/rue-compiler/src/ty.rs b/crates/rue-compiler/src/ty.rs index b1092df..3ccf45d 100644 --- a/crates/rue-compiler/src/ty.rs +++ b/crates/rue-compiler/src/ty.rs @@ -22,7 +22,7 @@ pub enum Type { List(TypeId), Struct(StructType), Enum(EnumType), - EnumVariant(EnumVariant), + EnumVariant(EnumVariantType), Function(FunctionType), Alias(TypeId), Optional(TypeId), @@ -41,12 +41,12 @@ pub struct StructType { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumType { + pub has_fields: bool, pub variants: IndexMap, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct EnumVariant { - pub name: String, +pub struct EnumVariantType { pub enum_type: TypeId, pub fields: IndexMap, pub discriminant: HirId, diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index 6207633..d7c4763 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -71,6 +71,7 @@ ast_node!(TypeAliasItem); ast_node!(StructItem); ast_node!(EnumItem); ast_node!(EnumVariant); +ast_node!(EnumVariantFields); ast_node!(ConstItem); ast_node!(StructField); ast_node!(ImportItem); @@ -341,11 +342,8 @@ impl EnumVariant { .find(|token| token.kind() == SyntaxKind::Ident) } - pub fn fields(&self) -> Vec { - self.syntax() - .children() - .filter_map(StructField::cast) - .collect() + pub fn fields(&self) -> Option { + self.syntax().children().find_map(EnumVariantFields::cast) } pub fn discriminant(&self) -> Option { @@ -356,6 +354,15 @@ impl EnumVariant { } } +impl EnumVariantFields { + pub fn fields(&self) -> Vec { + self.syntax() + .children() + .filter_map(StructField::cast) + .collect() + } +} + impl ConstItem { pub fn name(&self) -> Option { self.syntax() diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index e2d0690..8b1a3a4 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -147,9 +147,12 @@ fn enum_item(p: &mut Parser<'_>, cp: Checkpoint) { fn enum_variant(p: &mut Parser<'_>) { p.start(SyntaxKind::EnumVariant); p.expect(SyntaxKind::Ident); - p.expect(SyntaxKind::Assign); - p.expect(SyntaxKind::Int); - if p.try_eat(SyntaxKind::OpenBrace) { + if p.try_eat(SyntaxKind::Assign) { + p.expect(SyntaxKind::Int); + } + if p.at(SyntaxKind::OpenBrace) { + p.start(SyntaxKind::EnumVariantFields); + p.bump(); while !p.at(SyntaxKind::CloseBrace) { struct_field(p); if !p.try_eat(SyntaxKind::Comma) { @@ -157,6 +160,7 @@ fn enum_variant(p: &mut Parser<'_>) { } } p.expect(SyntaxKind::CloseBrace); + p.finish(); } p.finish(); } diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index e19ddbc..00c941d 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -85,6 +85,7 @@ pub enum SyntaxKind { StructField, EnumItem, EnumVariant, + EnumVariantFields, ConstItem, ImportItem, ImportPath, @@ -204,6 +205,7 @@ impl fmt::Display for SyntaxKind { Self::StructField => "struct field", Self::EnumItem => "enum item", Self::EnumVariant => "enum variant", + Self::EnumVariantFields => "enum variant fields", Self::ConstItem => "const item", Self::ImportItem => "import item", Self::ImportPath => "import path", diff --git a/tests.toml b/tests.toml index 0838dea..0438118 100644 --- a/tests.toml +++ b/tests.toml @@ -41,11 +41,11 @@ output = "1" hash = "41c686371ebb31d9e697cad70b910b68a367ebb452b6a4c48da832d7215f3d38" [hex] -bytes = 167 -cost = 3841 +bytes = 203 +cost = 4635 input = "()" output = "0x8b1651043cbbeb0e94d3db3ac81462a1b9c38706c165ac61c66fb54b4d448fc9c7f1b5ae1edc940f18cd1581597a50f48f44a5e5226a23570fea78cdc135707dff02ffff01ff06ffff14ffff01827d94ffff01298080ff0180ff02ffff01ff06ffff14ffff01827d94ffff0000000000" -hash = "5da7ebc63772dc4ed857d0fdf6dea1435d012ae530b4af1418c3fa80708f59da" +hash = "c873546a22ff735b3019f0b96af87222d99cfc7400ce0f68e4159ba96bc9340c" [nil] bytes = 1 @@ -125,11 +125,11 @@ output = "()" hash = "effeb1746e184bef22476b4c4d1d78c6abadcc50baf77340a31093e232a33e2f" [multisig] -bytes = 581 -cost = 28340 +bytes = 535 +cost = 27502 input = "((0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) 1 (()) ((51 0x0000000000000000000000000000000000000000000000000000000000000000 1000)))" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x502cea601814d49886e0713402860d6054f6947d811b3b93100c6b364626651a) (g1_negate 0x0000000000000000000000000000000000000000000000000000000000000000 1000))" -hash = "de54be8cbc8fcd942fac31cafe4269dc416060892c6a5fb9c74926123c33b3c5" +hash = "d4eab02c66dbc76f72b2f5f98b945db9fe3de19cafefc794221e0c066c246b02" [if] bytes = 25 @@ -139,11 +139,11 @@ output = "42" hash = "837525fb91b88dbf02d9ef5a0b322f6943f93424b6e8fe6d26dd1be6d3311871" [type_guards] -bytes = 91 -cost = 1844 +bytes = 63 +cost = 1332 input = "()" output = "5" -hash = "02b98083cb7ae70daea35a867c0de9fc5a0b198bb0b5427efcef7056e7beaa68" +hash = "32ed6e4964102d7093ef350019dfd14c99a3ea9feac1f3502194384b8976f7c1" [if_stmt] bytes = 107 @@ -153,11 +153,11 @@ output = "\"small\"" hash = "beaad80d005a15cffa2be04ba60e8dab6462381745fc0599619f0a50e82eda79" [let] -bytes = 79 -cost = 3690 +bytes = 51 +cost = 3174 input = "()" output = "128" -hash = "0a357412c1f0d72ec5b275e0b41096e37f205a1f51c94c7a995691508139fa7f" +hash = "ab22424773ba46a0dc3c41009422f432c74cf1e123e4573a40abc84874a46d31" [assert] bytes = 97 @@ -204,18 +204,18 @@ compiler_errors = [ ] [p2_conditions] -bytes = 167 -cost = 19407 +bytes = 153 +cost = 19149 input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000)))" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0xc239bbcfc69fb5abacdd8dc174c6b33170c6d902dec3bc1c87662020cf044313) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" -hash = "abc43bf1142122da14e73971da004668ce7b8785ca2b6ac41427b8ef3193f957" +hash = "769eb86a3d074f2e9beba52db764fc6b858f6df703c99cc5b0e8b0fba6cb06f7" [p2_delegated_or_hidden] -bytes = 305 -cost = 24974 +bytes = 261 +cost = 24716 input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q . ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x3d7e1145b5969c12f4889f4f1f66bde0e4d3ba54b91784cf604294d162b44b69) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" -hash = "670f06e052dedc68788dd17acb7d100203a1533d11f9c679d0f23bd3d555f122" +hash = "c891006043970c3e1f1e3d975081447096760b8a6c70b037b00613bfa585021c" [fp_utils] bytes = 873 @@ -223,3 +223,63 @@ cost = 92626 input = "()" output = "()" hash = "5f8d285dbefef4c4c7ccfcc4f204710c21f46ea45bfe6d5b4e81562ef183adde" + +[cast_expr] +bytes = 73 +cost = 1022 +input = "()" +output = "()" +hash = "a6f4a0e74a8a9f813a570ad56a8686f7e42bc26c116f6cf0f3f881e0a4ffb70e" + +[group_expr] +bytes = 3 +cost = 20 +input = "()" +output = "42" +hash = "ce97d04b4f84066533255fcc56345626c245afd50e24c6c14c52c8e78e8b9f33" + +[pair_expr] +parser_errors = [] +compiler_errors = [ + "expected type `Bytes`, but found `Int` at 2:31", + "expected type `(Int, Bytes)`, but found `(Int, Int)` at 2:5", +] + +[duplicate_variants] +parser_errors = [] +compiler_errors = [ + "duplicate enum variant `SameName` at 3:5", + "duplicate enum discriminant `1` at 4:29", + "unused enum variant `SameName` at 2:5", + "unused enum variant `DuplicateDiscriminant` at 4:5", +] + +[unused_symbols] +parser_errors = [] +compiler_errors = [ + "unused inline constant `UNUSED_INDIRECT_CONSTANT` at 1:14", + "unused inline constant `UNUSED_CONSTANT` at 2:14", + "unused function `unused_indirect_function` at 21:5", + "unused function `unused_function` at 25:5", + "unused inline constant `UNUSED_INNER_CONSTANT` at 6:18", + "unused function `unused_inner_function` at 8:9", + "unused let binding `unused_let` at 5:9", +] + +[unused_types] +parser_errors = [] +compiler_errors = [ + "unused type alias `A` at 2:6", + "unused enum variant `A` at 7:5", + "unused enum variant `B` at 10:5", + "unused enum variant `C` at 14:5", + "unused enum `Some` at 6:6", + "unused struct `Thing` at 17:8", +] + +[not] +bytes = 15 +cost = 623 +input = "()" +output = "()" +hash = "e7a613b58b23e44d70e889f8d8975724c9f786d19d5491428c6ca63eb87420ad" diff --git a/tests/errors/duplicate_variants.rue b/tests/errors/duplicate_variants.rue new file mode 100644 index 0000000..a67cd92 --- /dev/null +++ b/tests/errors/duplicate_variants.rue @@ -0,0 +1,10 @@ +enum Choices { + SameName = 1, + SameName = 2, + DuplicateDiscriminant = 1, + Different = 3, +} + +fun main() -> Choices { + Choices::Different +} diff --git a/tests/expressions/cast_expr.rue b/tests/expressions/cast_expr.rue new file mode 100644 index 0000000..fdc0c9c --- /dev/null +++ b/tests/expressions/cast_expr.rue @@ -0,0 +1,11 @@ +fun main() -> Nil { + let x: Int = 42 as Bytes as Int; + let y: Int = [1, 2, 3] as Any as Int; + + // This ensures that there isn't an "expected type" preventing this from compiling. + list([x, y, 3] as Bytes[]) +} + +fun list(_items: Bytes[]) -> Nil { + nil +} diff --git a/tests/expressions/group_expr.rue b/tests/expressions/group_expr.rue new file mode 100644 index 0000000..fa36503 --- /dev/null +++ b/tests/expressions/group_expr.rue @@ -0,0 +1,3 @@ +fun main() -> Int { + ((((42)))) +} diff --git a/tests/expressions/pair_expr.rue b/tests/expressions/pair_expr.rue new file mode 100644 index 0000000..9990e94 --- /dev/null +++ b/tests/expressions/pair_expr.rue @@ -0,0 +1,4 @@ +fun main() -> Int { + let pair: (Int, Bytes) = (24, "Bob"); + pair.rest.length * pair.first +} diff --git a/tests/literals/hex.rue b/tests/literals/hex.rue index 6ff438e..85d28af 100644 --- a/tests/literals/hex.rue +++ b/tests/literals/hex.rue @@ -1,4 +1,6 @@ fun main() -> Bytes { + assert 0x00 != nil; + 0x8b1651043cbbeb0e94d3db3ac81462a1b9c38706c165ac61c66fb54b4d448fc9 + bytes32_literal() + public_key_literal() as Bytes diff --git a/tests/operators/not.rue b/tests/operators/not.rue new file mode 100644 index 0000000..d3641bc --- /dev/null +++ b/tests/operators/not.rue @@ -0,0 +1,3 @@ +fun main() -> Bool { + !!(!true) +} diff --git a/tests/warnings/unused_symbols.rue b/tests/warnings/unused_symbols.rue new file mode 100644 index 0000000..804c0cc --- /dev/null +++ b/tests/warnings/unused_symbols.rue @@ -0,0 +1,27 @@ +inline const UNUSED_INDIRECT_CONSTANT: Int = 42; +inline const UNUSED_CONSTANT: Int = UNUSED_INDIRECT_CONSTANT; + +fun main() -> Nil { + let unused_let = UNUSED_CONSTANT + UNUSED_INDIRECT_CONSTANT; + inline const UNUSED_INNER_CONSTANT: Int = UNUSED_CONSTANT + UNUSED_INDIRECT_CONSTANT; + + fun unused_inner_function() -> Nil { + fun unused_inner_inner_function() -> Nil { + fun unused_deeper_function() -> Nil { + nil + } + unused_function() + } + unused_function() + } + + nil +} + +fun unused_indirect_function() -> Nil { + nil +} + +fun unused_function() -> Nil { + unused_indirect_function() +} diff --git a/tests/warnings/unused_types.rue b/tests/warnings/unused_types.rue new file mode 100644 index 0000000..c742d14 --- /dev/null +++ b/tests/warnings/unused_types.rue @@ -0,0 +1,25 @@ +type C = D; +type A = B; +type B = C; +type D = Int; + +enum Some { + A = 1 { + a: Int, + }, + B = 2 { + b: Bytes, + c: (Nil, (Nil, Nil)), + }, + C = 3 {}, +} + +struct Thing { + first: PublicKey, + next: Bytes32, + last: Bool, +} + +fun main() -> B { + 42 +}