From b1337900fbcc6d2dae8cb622df07ac804dcce7bf Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 13 Aug 2024 16:46:54 -0400 Subject: [PATCH] Add analysis to prevent tuple structs with wrong types --- dust-lang/src/analyzer.rs | 68 ++++++++++++++++++++++++++++++++++----- dust-lang/src/parser.rs | 4 +-- dust-lang/src/vm.rs | 17 ++++++++++ 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index c256feb..ed939f8 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -131,7 +131,7 @@ impl<'a> Analyzer<'a> { if !fields.contains_key(identifier) { return Err(AnalyzerError::UndefinedField { identifier: right.as_ref().clone(), - map: left.as_ref().clone(), + statement: left.as_ref().clone(), }); } } @@ -143,7 +143,7 @@ impl<'a> Analyzer<'a> { if !fields.contains_key(&Identifier::new(field_name)) { return Err(AnalyzerError::UndefinedField { identifier: right.as_ref().clone(), - map: left.as_ref().clone(), + statement: left.as_ref().clone(), }); } } @@ -291,16 +291,45 @@ impl<'a> Analyzer<'a> { } Statement::Constant(_) => {} Statement::Invokation { - invokee: function, + invokee, value_arguments, .. } => { - self.analyze_statement(function)?; + self.analyze_statement(invokee)?; + + let invokee_type = invokee.inner.expected_type(self.context); if let Some(arguments) = value_arguments { for argument in arguments { self.analyze_statement(argument)?; } + + if let Some(Type::Struct(struct_type)) = invokee_type { + match struct_type { + StructType::Unit { name } => todo!(), + StructType::Tuple { fields, .. } => { + for (expected_type, argument) in fields.iter().zip(arguments.iter()) + { + let actual_type = argument.inner.expected_type(self.context); + + if let Some(actual_type) = actual_type { + expected_type.check(&actual_type).map_err(|conflict| { + AnalyzerError::TypeConflict { + actual_statement: argument.clone(), + actual_type: conflict.actual, + expected: expected_type.clone(), + } + })?; + } else { + return Err(AnalyzerError::ExpectedValue { + actual: argument.clone(), + }); + } + } + } + StructType::Fields { name, fields } => todo!(), + } + } } } Statement::Identifier(identifier) => { @@ -537,7 +566,7 @@ pub enum AnalyzerError { }, UndefinedField { identifier: Node, - map: Node, + statement: Node, }, UndefinedType { identifier: Node, @@ -625,7 +654,10 @@ impl Display for AnalyzerError { expected, actual_statement, actual_type ) } - AnalyzerError::UndefinedField { identifier, map } => { + AnalyzerError::UndefinedField { + identifier, + statement: map, + } => { write!(f, "Undefined field {} in map {}", identifier, map) } AnalyzerError::UndefinedType { identifier } => { @@ -650,6 +682,26 @@ mod tests { use super::*; + #[test] + fn tuple_struct_with_wrong_field_types() { + let source = " + struct Foo(int, float) + Foo(1, 2) + "; + + assert_eq!( + analyze(source), + Err(DustError::AnalyzerError { + analyzer_error: AnalyzerError::TypeConflict { + actual_statement: Node::new(Statement::Constant(Value::integer(2)), (55, 56)), + actual_type: Type::Integer, + expected: Type::Float + }, + source + }) + ); + } + #[test] fn constant_list_index_out_of_bounds() { let source = "[1, 2, 3][3]"; @@ -684,7 +736,7 @@ mod tests { Err(DustError::AnalyzerError { analyzer_error: AnalyzerError::UndefinedField { identifier: Node::new(Statement::Identifier(Identifier::new("y")), (10, 11)), - map: Node::new( + statement: Node::new( Statement::Map(vec![( Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)), Node::new(Statement::Constant(Value::integer(1)), (6, 7)) @@ -706,7 +758,7 @@ mod tests { Err(DustError::AnalyzerError { analyzer_error: AnalyzerError::UndefinedField { identifier: Node::new(Statement::Constant(Value::string("y")), (10, 13)), - map: Node::new( + statement: Node::new( Statement::Map(vec![( Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)), Node::new(Statement::Constant(Value::integer(1)), (6, 7)) diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index ed8dcab..3085cf2 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -1027,9 +1027,7 @@ impl Display for ParseError { #[cfg(test)] mod tests { - use crate::{ - BinaryOperator, Identifier, StructDefinition, StructInstantiation, Type, UnaryOperator, - }; + use crate::{BinaryOperator, Identifier, StructDefinition, Type, UnaryOperator}; use super::*; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 662d1ad..b66ec4c 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -866,6 +866,23 @@ mod tests { use super::*; + #[test] + fn assign_tuple_struct_variable() { + let input = " + struct Foo(int) + x = Foo(42) + x + "; + + assert_eq!( + run(input), + Ok(Some(Value::r#struct(Struct::Tuple { + name: Identifier::new("Foo"), + fields: vec![Value::integer(42)] + }))) + ) + } + #[test] fn define_and_instantiate_tuple_struct() { let input = "struct Foo(int) Foo(42)";