Assign statements ir and fixing some things.

This commit is contained in:
Jesse Brault 2026-03-14 19:53:06 -05:00
parent 7e613b1b90
commit 5a123419bd
18 changed files with 256 additions and 80 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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)]

View File

@ -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<IrExpression> = 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")

View File

@ -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::<Vec<_>>();
@ -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(),

View File

@ -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,

View File

@ -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()

View File

@ -42,6 +42,10 @@ impl Field {
&self.declared_name
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range
}

View File

@ -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<RefCell<FieldSymbol>>,
) -> &'a Rc<RefCell<IrVariable>> {
// 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<RefCell<FieldSymbol>>,
) -> &'a Rc<RefCell<IrVariable>> {
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()
}

View File

@ -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);

View File

@ -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;

View File

@ -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 {

View File

@ -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);
}
}
}

View File

@ -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)
}

View File

@ -58,12 +58,16 @@ impl VrUser for IrAssign {
&mut self,
assignments: &HashMap<IrVrVariableDescriptor, usize>,
) {
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);

View File

@ -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()

View File

@ -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)]

11
examples/assign_field.dm Normal file
View File

@ -0,0 +1,11 @@
class Foo
mut bar = 21
ctor(_bar: Int)
bar = _bar
end
end
fn main() -> Foo
Foo(42)
end