diff --git a/compiler/passes/src/type_checker/check_expressions.rs b/compiler/passes/src/type_checker/check_expressions.rs index 4934b6495b..6e5d9b6c71 100644 --- a/compiler/passes/src/type_checker/check_expressions.rs +++ b/compiler/passes/src/type_checker/check_expressions.rs @@ -39,7 +39,7 @@ fn return_incorrect_type(t1: Option, t2: Option, expected: Option TypeChecker<'a> { - pub(crate) fn compare_expr_type(&self, expr: &Expression, expected: Option, span: &Span) -> Option { + pub(crate) fn compare_expr_type(&mut self, expr: &Expression, expected: Option, span: &Span) -> Option { match expr { Expression::Identifier(ident) => { if let Some(var) = self.symbol_table.lookup_variable(&ident.name) { @@ -55,11 +55,95 @@ impl<'a> TypeChecker<'a> { ValueExpression::Boolean(_, _) => Some(self.assert_type(Type::Boolean, expected, value.span())), ValueExpression::Char(_) => Some(self.assert_type(Type::Char, expected, value.span())), ValueExpression::Field(_, _) => Some(self.assert_type(Type::Field, expected, value.span())), - ValueExpression::Integer(type_, _, _) => { + ValueExpression::Integer(type_, str_content, _) => { + match type_ { + IntegerType::I8 => { + let int = if self.negate { + self.negate = false; + format!("-{str_content}") + } else { + str_content.clone() + }; + + if int.parse::().is_err() { + self.handler + .emit_err(TypeCheckerError::invalid_int_value(int, "i8", value.span()).into()); + } + } + IntegerType::I16 => { + let int = if self.negate { + self.negate = false; + format!("-{str_content}") + } else { + str_content.clone() + }; + + if int.parse::().is_err() { + self.handler + .emit_err(TypeCheckerError::invalid_int_value(int, "i16", value.span()).into()); + } + } + IntegerType::I32 => { + let int = if self.negate { + self.negate = false; + format!("-{str_content}") + } else { + str_content.clone() + }; + + if int.parse::().is_err() { + self.handler + .emit_err(TypeCheckerError::invalid_int_value(int, "i32", value.span()).into()); + } + } + IntegerType::I64 => { + let int = if self.negate { + self.negate = false; + format!("-{str_content}") + } else { + str_content.clone() + }; + + if int.parse::().is_err() { + self.handler + .emit_err(TypeCheckerError::invalid_int_value(int, "i64", value.span()).into()); + } + } + IntegerType::I128 => { + let int = if self.negate { + self.negate = false; + format!("-{str_content}") + } else { + str_content.clone() + }; + + if int.parse::().is_err() { + self.handler + .emit_err(TypeCheckerError::invalid_int_value(int, "i128", value.span()).into()); + } + } + + IntegerType::U8 if str_content.parse::().is_err() => self + .handler + .emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", value.span()).into()), + IntegerType::U16 if str_content.parse::().is_err() => self + .handler + .emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", value.span()).into()), + IntegerType::U32 if str_content.parse::().is_err() => self + .handler + .emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", value.span()).into()), + IntegerType::U64 if str_content.parse::().is_err() => self + .handler + .emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", value.span()).into()), + IntegerType::U128 if str_content.parse::().is_err() => self + .handler + .emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", value.span()).into()), + _ => {} + } Some(self.assert_type(Type::IntegerType(*type_), expected, value.span())) } ValueExpression::Group(_) => Some(self.assert_type(Type::Group, expected, value.span())), - ValueExpression::String(_, _) => None, + ValueExpression::String(_, _) => unreachable!("String types are not reachable"), }, Expression::Binary(binary) => match binary.op { BinaryOperation::And | BinaryOperation::Or => { @@ -163,19 +247,25 @@ impl<'a> TypeChecker<'a> { self.compare_expr_type(&unary.inner, expected, unary.inner.span()) } UnaryOperation::Negate => { - /* match expected { - Type::IntegerType( - IntegerType::I8 - | IntegerType::I16 - | IntegerType::I32 - | IntegerType::I64 - | IntegerType::I128, - ) => {}, - Type::Field | Type::Group => {} - _ => self.handler.emit_err( - TypeCheckerError::type_is_not_negatable(expected.clone(), unary.inner.span()).into(), - ), - } */ + // -128i8 + // -(-128i16 + 3i16) = 125i16 + match expected.as_ref() { + Some( + Type::IntegerType( + IntegerType::I8 + | IntegerType::I16 + | IntegerType::I32 + | IntegerType::I64 + | IntegerType::I128, + ) + | Type::Field + | Type::Group, + ) => self.negate = !self.negate, + Some(t) => self + .handler + .emit_err(TypeCheckerError::type_is_not_negatable(t, unary.inner.span()).into()), + _ => {} + }; self.compare_expr_type(&unary.inner, expected, unary.inner.span()) } }, diff --git a/compiler/passes/src/type_checker/check_statements.rs b/compiler/passes/src/type_checker/check_statements.rs index d17eb3f317..e085db034d 100644 --- a/compiler/passes/src/type_checker/check_statements.rs +++ b/compiler/passes/src/type_checker/check_statements.rs @@ -25,9 +25,9 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> { // statements should always have some parent block let parent = self.parent.unwrap(); - if let Some(func) = self.symbol_table.lookup_fn(&parent) { - self.compare_expr_type(&input.expression, Some(func.output.clone()), input.expression.span()); - } + // Would never be None. + let func_output_type = self.symbol_table.lookup_fn(&parent).map(|f| f.output.clone()); + self.compare_expr_type(&input.expression, func_output_type, input.expression.span()); VisitResult::VisitChildren } @@ -59,7 +59,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> { fn visit_assign(&mut self, input: &'a AssignStatement) -> VisitResult { let var_name = &input.assignee.identifier.name; - if let Some(var) = self.symbol_table.lookup_variable(var_name) { + let var_type = if let Some(var) = self.symbol_table.lookup_variable(var_name) { match &var.declaration { Declaration::Const => self .handler @@ -70,11 +70,17 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> { _ => {} } - self.compare_expr_type(&input.value, Some(var.type_.clone()), input.value.span()); + Some(var.type_.clone()) } else { self.handler.emit_err( TypeCheckerError::unknown_sym("variable", &input.assignee.identifier.name, &input.assignee.span).into(), - ) + ); + + None + }; + + if var_type.is_some() { + self.compare_expr_type(&input.value, var_type, input.value.span()); } VisitResult::VisitChildren diff --git a/compiler/passes/src/type_checker/checker.rs b/compiler/passes/src/type_checker/checker.rs index c4f8a29440..c38eebf023 100644 --- a/compiler/passes/src/type_checker/checker.rs +++ b/compiler/passes/src/type_checker/checker.rs @@ -24,6 +24,7 @@ pub struct TypeChecker<'a> { pub(crate) symbol_table: &'a mut SymbolTable<'a>, pub(crate) handler: &'a Handler, pub(crate) parent: Option, + pub(crate) negate: bool, } const ARITHMATIC_TYPES: &[Type] = &[ @@ -74,6 +75,7 @@ impl<'a> TypeChecker<'a> { symbol_table, handler, parent: None, + negate: false, } } diff --git a/leo/errors/src/errors/type_checker/type_checker_error.rs b/leo/errors/src/errors/type_checker/type_checker_error.rs index 842e663c64..6646d294ca 100644 --- a/leo/errors/src/errors/type_checker/type_checker_error.rs +++ b/leo/errors/src/errors/type_checker/type_checker_error.rs @@ -123,12 +123,12 @@ create_messages!( help: None, } - /// For when a type is does not exist. + /// For when an integer is not in a valid range. @formatted - unknown_type { - args: (), + invalid_int_value { + args: (value: impl Display, type_: impl Display), msg: format!( - "The type", + "The value {value} is not a valid `{type_}`", ), help: None, }