From 5a123419bd739381fec3bf543fd839b2980fd90d Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Sat, 14 Mar 2026 19:53:06 -0500 Subject: [PATCH] Assign statements ir and fixing some things. --- dm/src/repl.rs | 2 +- dmc-lib/src/ast/add_expression.rs | 4 +- dmc-lib/src/ast/assign_statement.rs | 38 +++++++++++ dmc-lib/src/ast/call.rs | 3 +- dmc-lib/src/ast/constructor.rs | 30 +++++++-- dmc-lib/src/ast/expression.rs | 33 +++++++++- dmc-lib/src/ast/expression_statement.rs | 2 +- dmc-lib/src/ast/field.rs | 4 ++ dmc-lib/src/ast/ir_util.rs | 83 ++++++++++++++++++++++++ dmc-lib/src/ast/let_statement.rs | 25 +------ dmc-lib/src/ast/mod.rs | 1 + dmc-lib/src/ast/negative_expression.rs | 2 +- dmc-lib/src/ast/statement.rs | 2 +- dmc-lib/src/ast/subtract_expression.rs | 4 +- dmc-lib/src/ir/ir_assign.rs | 14 ++-- dmc-lib/src/symbol/expressible_symbol.rs | 36 +--------- e2e-tests/src/lib.rs | 42 ++++++++++++ examples/assign_field.dm | 11 ++++ 18 files changed, 256 insertions(+), 80 deletions(-) create mode 100644 dmc-lib/src/ast/ir_util.rs create mode 100644 examples/assign_field.dm diff --git a/dm/src/repl.rs b/dm/src/repl.rs index eb1a457..d8fbca5 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -114,7 +114,7 @@ fn compile_expression( let mut ir_builder = IrBuilder::new(); let entry_block_id = ir_builder.new_block(); - let maybe_ir_expression = expression.to_ir(&mut ir_builder, &symbol_table); + let maybe_ir_expression = expression.to_ir_expression(&mut ir_builder, &symbol_table); // if Some, return the value ir_builder diff --git a/dmc-lib/src/ast/add_expression.rs b/dmc-lib/src/ast/add_expression.rs index 4897c6c..710c21e 100644 --- a/dmc-lib/src/ast/add_expression.rs +++ b/dmc-lib/src/ast/add_expression.rs @@ -83,11 +83,11 @@ impl AddExpression { pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrAdd { let lhs_ir_expression = self .lhs - .to_ir(builder, symbol_table) + .to_ir_expression(builder, symbol_table) .expect("Attempt to add non-expression"); let rhs_ir_expression = self .rhs - .to_ir(builder, symbol_table) + .to_ir_expression(builder, symbol_table) .expect("Attempt to add non-expression"); IrAdd::new(lhs_ir_expression, rhs_ir_expression) } diff --git a/dmc-lib/src/ast/assign_statement.rs b/dmc-lib/src/ast/assign_statement.rs index 5a2a2e2..8b53e5e 100644 --- a/dmc-lib/src/ast/assign_statement.rs +++ b/dmc-lib/src/ast/assign_statement.rs @@ -1,6 +1,12 @@ use crate::ast::expression::Expression; +use crate::ast::ir_builder::IrBuilder; +use crate::ast::ir_util::get_or_init_mut_field_pointer_variable; use crate::diagnostic::{Diagnostic, SecondaryLabel}; use crate::error_codes::{ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_NO_L_VALUE}; +use crate::ir::ir_assign::IrAssign; +use crate::ir::ir_set_field::IrSetField; +use crate::ir::ir_statement::IrStatement; +use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol_table::SymbolTable; pub struct AssignStatement { @@ -157,6 +163,38 @@ impl AssignStatement { Err(diagnostics) } } + + pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { + let destination_symbol = match &*self.destination { + Expression::Identifier(identifier) => identifier.expressible_symbol(), + _ => unreachable!("Destination must be a mutable L value"), + }; + + let ir_statement = match destination_symbol { + ExpressibleSymbol::Field(field_symbol) => { + let mut_field_pointer_variable = + get_or_init_mut_field_pointer_variable(builder, field_symbol).clone(); + let ir_set_field = IrSetField::new( + &mut_field_pointer_variable, + self.value + .to_ir_expression(builder, symbol_table) + .expect("Attempt to convert non-value to value"), + ); + IrStatement::SetField(ir_set_field) + } + ExpressibleSymbol::Variable(variable_symbol) => { + let vr_variable = variable_symbol.borrow().vr_variable().clone(); + let ir_assign = IrAssign::new( + vr_variable, + self.value.to_ir_operation(builder, symbol_table), + ); + IrStatement::Assign(ir_assign) + } + _ => unreachable!("Destination must be a mutable L value"), + }; + + builder.current_block_mut().add_statement(ir_statement); + } } #[cfg(test)] diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index 37ce077..7324775 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -5,7 +5,6 @@ use crate::diagnostic::Diagnostic; use crate::ir::ir_call::IrCall; use crate::ir::ir_expression::IrExpression; use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::symbol::callable_symbol::CallableSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol_table::SymbolTable; @@ -176,7 +175,7 @@ impl Call { let arguments: Vec = self .arguments .iter() - .map(|argument| argument.to_ir(builder, symbol_table)) + .map(|argument| argument.to_ir_expression(builder, symbol_table)) .inspect(|expression| { if expression.is_none() { panic!("Attempt to pass non-expression") diff --git a/dmc-lib/src/ast/constructor.rs b/dmc-lib/src/ast/constructor.rs index b27b18a..d1d8ec5 100644 --- a/dmc-lib/src/ast/constructor.rs +++ b/dmc-lib/src/ast/constructor.rs @@ -200,13 +200,19 @@ impl Constructor { .iter() .enumerate() .map(|(i, parameter)| { - let parameter_symbol = parameter.parameter_symbol().borrow(); + let mut parameter_symbol = parameter.parameter_symbol().borrow_mut(); let offset = (parameters_count as isize).neg() + i as isize; - Rc::new(IrParameter::new( + let ir_parameter = Rc::new(IrParameter::new( parameter_symbol.declared_name(), parameter_symbol.type_info().clone(), offset, - )) + )); + + // make sure to save ir_parameter to symbol so others can access it + let to_save = ir_parameter.clone(); + parameter_symbol.set_ir_parameter(to_save); + + ir_parameter }) .collect::>(); @@ -219,6 +225,11 @@ impl Constructor { &TypeInfo::ClassInstance(class_symbol.clone()), ); let self_variable = Rc::new(RefCell::new(alloc_assign_destination)); + + // save self variable so statements can assign stuff to self's fields + ir_builder + .set_self_parameter_or_variable(IrParameterOrVariable::Variable(self_variable.clone())); + let alloc_assign = IrAssign::new( self_variable.clone(), IrOperation::Allocate(IrAllocate::new(class_symbol.borrow().declared_name_owned())), @@ -252,16 +263,18 @@ impl Constructor { // save the mut ref to the builder for other uses if needed ir_builder .field_mut_pointer_variables_mut() - .insert(field_mut_ref_variable_name.clone(), field_mut_ref_variable); + .insert(field.declared_name_owned(), field_mut_ref_variable); // n.b. field name, not t var name // now write the initializer result to the field let field_mut_ref_variable = ir_builder .field_mut_pointer_variables() - .get(&field_mut_ref_variable_name) + .get(field.declared_name()) .unwrap() .clone(); - let ir_expression = initializer.to_ir(&mut ir_builder, symbol_table).unwrap(); + let ir_expression = initializer + .to_ir_expression(&mut ir_builder, symbol_table) + .unwrap(); let ir_set_field = IrSetField::new( &field_mut_ref_variable, // dumb that we clone it and then ref it ir_expression, @@ -272,6 +285,11 @@ impl Constructor { } } + // do "declared" statements of constructor + for statement in &self.statements { + statement.to_ir(&mut ir_builder, symbol_table, false); + } + // return complete self object let ir_return_statement = IrStatement::Return(IrReturn::new(Some(IrExpression::Variable( self_variable.clone(), diff --git a/dmc-lib/src/ast/expression.rs b/dmc-lib/src/ast/expression.rs index 49c2a24..7e2be02 100644 --- a/dmc-lib/src/ast/expression.rs +++ b/dmc-lib/src/ast/expression.rs @@ -111,7 +111,38 @@ impl Expression { } } - pub fn to_ir( + pub fn to_ir_operation( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + ) -> IrOperation { + match self { + Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)), + Expression::Integer(integer_literal) => { + IrOperation::Load(IrExpression::Int(integer_literal.value())) + } + Expression::Double(double_literal) => { + IrOperation::Load(IrExpression::Double(double_literal.value())) + } + Expression::String(string_literal) => { + IrOperation::Load(IrExpression::String(string_literal.content().into())) + } + Expression::Identifier(identifier) => { + IrOperation::Load(identifier.expressible_symbol().ir_expression(builder)) + } + Expression::Add(additive_expression) => { + IrOperation::Add(additive_expression.to_ir(builder, symbol_table)) + } + Expression::Subtract(subtract_expression) => { + IrOperation::Subtract(subtract_expression.to_ir_subtract(builder, symbol_table)) + } + Expression::Negative(negative_expression) => { + IrOperation::Load(negative_expression.to_ir(builder, symbol_table)) + } + } + } + + pub fn to_ir_expression( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, diff --git a/dmc-lib/src/ast/expression_statement.rs b/dmc-lib/src/ast/expression_statement.rs index 7929468..58d951b 100644 --- a/dmc-lib/src/ast/expression_statement.rs +++ b/dmc-lib/src/ast/expression_statement.rs @@ -62,7 +62,7 @@ impl ExpressionStatement { symbol_table: &SymbolTable, should_return_value: bool, ) { - let ir_expression = self.expression.to_ir(builder, symbol_table); + let ir_expression = self.expression.to_ir_expression(builder, symbol_table); if ir_expression.is_some() && should_return_value { builder .current_block_mut() diff --git a/dmc-lib/src/ast/field.rs b/dmc-lib/src/ast/field.rs index 49cc75b..014872d 100644 --- a/dmc-lib/src/ast/field.rs +++ b/dmc-lib/src/ast/field.rs @@ -42,6 +42,10 @@ impl Field { &self.declared_name } + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + pub fn declared_name_source_range(&self) -> &SourceRange { &self.declared_name_source_range } diff --git a/dmc-lib/src/ast/ir_util.rs b/dmc-lib/src/ast/ir_util.rs new file mode 100644 index 0000000..6212265 --- /dev/null +++ b/dmc-lib/src/ast/ir_util.rs @@ -0,0 +1,83 @@ +use crate::ast::ir_builder::IrBuilder; +use crate::ir::ir_assign::IrAssign; +use crate::ir::ir_get_field_ref::IrGetFieldRef; +use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut; +use crate::ir::ir_operation::IrOperation; +use crate::ir::ir_statement::IrStatement; +use crate::ir::ir_variable::IrVariable; +use crate::symbol::Symbol; +use crate::symbol::field_symbol::FieldSymbol; +use std::cell::RefCell; +use std::rc::Rc; + +pub fn get_or_init_field_pointer_variable<'a>( + builder: &'a mut IrBuilder, + field_symbol: &Rc>, +) -> &'a Rc> { + // This following should work because blocks are flat in the ir; if a variable is defined in the + // ir block from this point forward, it's available to all subsequent blocks. + if !builder + .field_pointer_variables() + .contains_key(field_symbol.borrow().declared_name()) + { + let field_ref_variable = IrVariable::new_vr( + builder.new_t_var().into(), + builder.current_block().id(), + field_symbol.borrow().type_info(), + ); + let as_rc = Rc::new(RefCell::new(field_ref_variable)); + let to_insert = as_rc.clone(); + let self_parameter_or_variable = builder.self_parameter_or_variable().clone(); + builder + .current_block_mut() + .add_statement(IrStatement::Assign(IrAssign::new( + as_rc, + IrOperation::GetFieldRef(IrGetFieldRef::new( + self_parameter_or_variable.clone(), + field_symbol.borrow().field_index(), + )), + ))); + builder + .field_pointer_variables_mut() + .insert(field_symbol.borrow().declared_name_owned(), to_insert); + } + builder + .field_pointer_variables() + .get(field_symbol.borrow().declared_name()) + .unwrap() +} + +pub fn get_or_init_mut_field_pointer_variable<'a>( + builder: &'a mut IrBuilder, + field_symbol: &Rc>, +) -> &'a Rc> { + if !builder + .field_mut_pointer_variables() + .contains_key(field_symbol.borrow().declared_name()) + { + let mut_field_pointer_variable = IrVariable::new_vr( + builder.new_t_var().into(), + builder.current_block().id(), + field_symbol.borrow().type_info(), + ); + let as_rc = Rc::new(RefCell::new(mut_field_pointer_variable)); + let to_insert = as_rc.clone(); + let self_parameter_or_variable = builder.self_parameter_or_variable().clone(); + builder + .current_block_mut() + .add_statement(IrStatement::Assign(IrAssign::new( + as_rc, + IrOperation::GetFieldRefMut(IrGetFieldRefMut::new( + self_parameter_or_variable.clone(), + field_symbol.borrow().field_index(), + )), + ))); + builder + .field_mut_pointer_variables_mut() + .insert(field_symbol.borrow().declared_name_owned(), to_insert); + } + builder + .field_mut_pointer_variables() + .get(field_symbol.borrow().declared_name()) + .unwrap() +} diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index fb35f28..4e3a5f0 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -2,8 +2,6 @@ use crate::ast::expression::Expression; use crate::ast::ir_builder::IrBuilder; use crate::diagnostic::Diagnostic; use crate::ir::ir_assign::IrAssign; -use crate::ir::ir_expression::IrExpression; -use crate::ir::ir_operation::IrOperation; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; @@ -106,28 +104,7 @@ impl LetStatement { } pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { - let init_operation = match self.initializer() { - Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)), - Expression::Integer(integer_literal) => { - IrOperation::Load(IrExpression::Int(integer_literal.value())) - } - Expression::Double(double_literal) => { - IrOperation::Load(IrExpression::Double(double_literal.value())) - } - Expression::String(string_literal) => { - IrOperation::Load(IrExpression::String(string_literal.content().into())) - } - Expression::Identifier(identifier) => { - IrOperation::Load(identifier.expressible_symbol().ir_expression(builder)) - } - Expression::Add(additive_expression) => { - IrOperation::Add(additive_expression.to_ir(builder, symbol_table)) - } - Expression::Subtract(subtract_expression) => { - IrOperation::Subtract(subtract_expression.to_ir_subtract(builder, symbol_table)) - } - Expression::Negative(_) => todo!(), - }; + let init_operation = self.initializer.to_ir_operation(builder, symbol_table); let destination_symbol = symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name); diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index ecdce2e..078c4ed 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -16,6 +16,7 @@ pub mod function; pub mod identifier; pub mod integer_literal; pub mod ir_builder; +pub(crate) mod ir_util; pub mod let_statement; pub mod negative_expression; pub mod parameter; diff --git a/dmc-lib/src/ast/negative_expression.rs b/dmc-lib/src/ast/negative_expression.rs index 4aaa0ef..f84c4e1 100644 --- a/dmc-lib/src/ast/negative_expression.rs +++ b/dmc-lib/src/ast/negative_expression.rs @@ -70,7 +70,7 @@ impl NegativeExpression { pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrExpression { let operand_as_ir = self .operand - .to_ir(builder, symbol_table) + .to_ir_expression(builder, symbol_table) .expect("Attempt to negate non-value expression"); match operand_as_ir { diff --git a/dmc-lib/src/ast/statement.rs b/dmc-lib/src/ast/statement.rs index 08d496c..59ebafc 100644 --- a/dmc-lib/src/ast/statement.rs +++ b/dmc-lib/src/ast/statement.rs @@ -66,7 +66,7 @@ impl Statement { expression_statement.to_ir(builder, symbol_table, should_return_value); } Statement::Assign(assign_statement) => { - todo!() + assign_statement.to_ir(builder, symbol_table); } } } diff --git a/dmc-lib/src/ast/subtract_expression.rs b/dmc-lib/src/ast/subtract_expression.rs index cc3dd03..b746859 100644 --- a/dmc-lib/src/ast/subtract_expression.rs +++ b/dmc-lib/src/ast/subtract_expression.rs @@ -105,11 +105,11 @@ impl SubtractExpression { ) -> IrSubtract { let lhs = self .lhs - .to_ir(builder, symbol_table) + .to_ir_expression(builder, symbol_table) .expect("Attempt to subtract non-expression"); let rhs = self .rhs - .to_ir(builder, symbol_table) + .to_ir_expression(builder, symbol_table) .expect("Attempt to subtract non-expression"); IrSubtract::new(lhs, rhs) } diff --git a/dmc-lib/src/ir/ir_assign.rs b/dmc-lib/src/ir/ir_assign.rs index b17c8e2..195ef82 100644 --- a/dmc-lib/src/ir/ir_assign.rs +++ b/dmc-lib/src/ir/ir_assign.rs @@ -58,12 +58,16 @@ impl VrUser for IrAssign { &mut self, assignments: &HashMap, ) { - let mut borrowed_destination = self.destination.borrow_mut(); - if let IrVariableDescriptor::VirtualRegister(vr_variable) = - borrowed_destination.descriptor_mut() + // Have to isolate the following, because when the destination and the value are the same, + // we get a BorrowMutError { - if assignments.contains_key(vr_variable) { - vr_variable.set_assigned_register(assignments[vr_variable]); + let mut borrowed_destination = self.destination.borrow_mut(); + if let IrVariableDescriptor::VirtualRegister(vr_variable) = + borrowed_destination.descriptor_mut() + { + if assignments.contains_key(vr_variable) { + vr_variable.set_assigned_register(assignments[vr_variable]); + } } } self.initializer.propagate_register_assignments(assignments); diff --git a/dmc-lib/src/symbol/expressible_symbol.rs b/dmc-lib/src/symbol/expressible_symbol.rs index 9231ed8..c1c5aec 100644 --- a/dmc-lib/src/symbol/expressible_symbol.rs +++ b/dmc-lib/src/symbol/expressible_symbol.rs @@ -1,7 +1,7 @@ use crate::ast::ir_builder::IrBuilder; +use crate::ast::ir_util::get_or_init_field_pointer_variable; use crate::ir::ir_assign::IrAssign; use crate::ir::ir_expression::IrExpression; -use crate::ir::ir_get_field_ref::IrGetFieldRef; use crate::ir::ir_operation::IrOperation; use crate::ir::ir_read_field::IrReadField; use crate::ir::ir_statement::IrStatement; @@ -79,34 +79,6 @@ impl ExpressibleSymbol { todo!() } ExpressibleSymbol::Field(field_symbol) => { - // This following should work because blocks are flat in the ir; if a variable is - // defined in the ir block from this point forward, it's available to all subsequent - // blocks. - if !builder - .field_pointer_variables() - .contains_key(field_symbol.borrow().declared_name()) - { - let field_ref_variable = IrVariable::new_vr( - builder.new_t_var().into(), - builder.current_block().id(), - field_symbol.borrow().type_info(), - ); - let as_rc = Rc::new(RefCell::new(field_ref_variable)); - let to_insert = as_rc.clone(); - let self_parameter_or_variable = builder.self_parameter_or_variable().clone(); - builder - .current_block_mut() - .add_statement(IrStatement::Assign(IrAssign::new( - as_rc, - IrOperation::GetFieldRef(IrGetFieldRef::new( - self_parameter_or_variable.clone(), - field_symbol.borrow().field_index(), - )), - ))); - builder - .field_pointer_variables_mut() - .insert(field_symbol.borrow().declared_name_owned(), to_insert); - } // now we need to read the field into a variable and return an expression pointing // to that variable let read_destination = IrVariable::new_vr( @@ -116,11 +88,7 @@ impl ExpressibleSymbol { ); let read_destination_as_rc = Rc::new(RefCell::new(read_destination)); let ir_read_field = IrReadField::new( - builder - .field_pointer_variables() - .get(field_symbol.borrow().declared_name()) - .unwrap() - .clone(), + get_or_init_field_pointer_variable(builder, field_symbol).clone(), ); builder .current_block_mut() diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 7c0c060..fecff9b 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -248,6 +248,48 @@ mod e2e_tests { assert_eq!(o.fields().len(), 1); assert_eq!(o.fields()[0].unwrap_int(), 42); } + + #[test] + fn simple_assign() { + assert_result( + " + fn assign() -> Int + let mut x = 21 + x = x + x + x + end + ", + "assign", + &vec![], + Value::Int(42), + ); + } + + #[test] + fn assign_field() { + let context = prepare_context( + " + class Foo + mut bar = 21 + + ctor(_bar: Int) + bar = _bar + end + end + + fn foo() -> Foo + Foo(42) + end + ", + ); + let result = get_result(&context, "foo", &vec![]); + assert!(result.is_some()); + let value = result.unwrap(); + assert!(matches!(value, Value::Object(_))); + let o = value.unwrap_object().borrow(); + assert_eq!(o.fields().len(), 1); + assert_eq!(o.fields()[0].unwrap_int(), 42); + } } #[cfg(test)] diff --git a/examples/assign_field.dm b/examples/assign_field.dm new file mode 100644 index 0000000..2dcb164 --- /dev/null +++ b/examples/assign_field.dm @@ -0,0 +1,11 @@ +class Foo + mut bar = 21 + + ctor(_bar: Int) + bar = _bar + end +end + +fn main() -> Foo + Foo(42) +end