diff --git a/lib/edlang_check/src/lib.rs b/lib/edlang_check/src/lib.rs index c551dd09c..a004c51b4 100644 --- a/lib/edlang_check/src/lib.rs +++ b/lib/edlang_check/src/lib.rs @@ -201,16 +201,28 @@ pub fn lowering_error_to_report( ) .finish() } - LoweringError::NotMutable { span, file_id } => { + LoweringError::NotMutable { + span, + declare_span, + file_id, + } => { let path = session.file_paths[file_id].display().to_string(); - Report::build(ReportKind::Error, path.clone(), span.lo) + let mut report = Report::build(ReportKind::Error, path.clone(), span.lo) .with_code("NotMutable") .with_label( - Label::new((path, span.into())) - .with_message("can't mutate this value because it's not declared mutable") + Label::new((path.clone(), span.into())) + .with_message("can't mutate this variable because it's not mutable") .with_color(colors.next()), - ) - .finish() + ); + + if let Some(declare_span) = declare_span { + report = report.with_label( + Label::new((path, declare_span.into())) + .with_message("variable declared here") + .with_color(colors.next()), + ); + } + report.finish() } LoweringError::NotMutableSelf { span, @@ -232,5 +244,28 @@ pub fn lowering_error_to_report( ) .finish() } + LoweringError::CantTakeMutableBorrow { + span, + declare_span, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); + let mut report = Report::build(ReportKind::Error, path.clone(), span.lo) + .with_code("CantTakeMutableBorrow") + .with_label( + Label::new((path.clone(), span.into())) + .with_message("can't take a mutate borrow to this variable because it's not declared mutable") + .with_color(colors.next()), + ); + + if let Some(declare_span) = declare_span { + report = report.with_label( + Label::new((path, declare_span.into())) + .with_message("variable declared here") + .with_color(colors.next()), + ); + } + report.finish() + } } } diff --git a/lib/edlang_driver/tests/programs/if_if_false.ed b/lib/edlang_driver/tests/programs/if_if_false.ed index c3fd5354e..7c43f1ad9 100644 --- a/lib/edlang_driver/tests/programs/if_if_false.ed +++ b/lib/edlang_driver/tests/programs/if_if_false.ed @@ -1,6 +1,6 @@ fn main() -> i32 { - let foo: i32 = 7; + let mut foo: i32 = 7; if true { if false { diff --git a/lib/edlang_ir/src/lib.rs b/lib/edlang_ir/src/lib.rs index 45e762ef0..15285d101 100644 --- a/lib/edlang_ir/src/lib.rs +++ b/lib/edlang_ir/src/lib.rs @@ -178,6 +178,18 @@ impl Local { mutable: false, } } + + pub fn is_mutable(&self) -> bool { + if self.mutable { + return true; + } + + match self.ty.kind { + TypeKind::Ptr(is_mut, _) => is_mut, + TypeKind::Ref(is_mut, _) => is_mut, + _ => false, + } + } } #[derive(Debug, Clone, Copy)] @@ -424,6 +436,19 @@ pub enum RValue { Cast(Operand, TypeInfo, Span), } +impl RValue { + pub fn get_local(&self) -> Option { + match self { + RValue::Use(op, _) => op.get_local(), + RValue::Ref(_, op, _) => op.get_local(), + RValue::BinOp(_, _, _, _) => None, + RValue::LogicOp(_, _, _, _) => None, + RValue::UnOp(_, _, _) => None, + RValue::Cast(op, _, _) => op.get_local(), + } + } +} + #[derive(Debug, Clone)] pub enum Operand { Copy(Place), @@ -431,6 +456,16 @@ pub enum Operand { Constant(ConstData), } +impl Operand { + pub fn get_local(&self) -> Option { + match self { + Operand::Copy(place) => Some(place.local), + Operand::Move(place) => Some(place.local), + Operand::Constant(_) => None, + } + } +} + #[derive(Debug, Clone)] pub struct Place { pub local: usize, diff --git a/lib/edlang_lowering/src/errors.rs b/lib/edlang_lowering/src/errors.rs index 693b7b988..30db479f9 100644 --- a/lib/edlang_lowering/src/errors.rs +++ b/lib/edlang_lowering/src/errors.rs @@ -71,7 +71,17 @@ pub enum LoweringError { file_id: usize, }, #[error("can't mutate this value because it's not declared mutable")] - NotMutable { span: Span, file_id: usize }, + NotMutable { + span: Span, + declare_span: Option, + file_id: usize, + }, + #[error("can't take a mutable borrow to this value because it's not declared mutable")] + CantTakeMutableBorrow { + span: Span, + declare_span: Option, + file_id: usize, + }, #[error("this method requires a mutable 'self'")] NotMutableSelf { span: Span, diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index 839d38dbd..e5bb74e3f 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -536,6 +536,14 @@ fn lower_assign(builder: &mut BodyBuilder, info: &ast::AssignStmt) -> Result<(), kind: ty, }; + if !builder.body.locals[place.local].is_mutable() { + return Err(LoweringError::NotMutable { + span: info.span, + declare_span: builder.body.locals[place.local].span, + file_id: builder.file_id, + }); + } + for _ in 0..info.deref_times { match &path_ty.kind { TypeKind::Ptr(is_mut, inner) => { @@ -687,7 +695,7 @@ fn lower_expr( let ty = match ty { TypeKind::Ptr(_, inner) => *inner, TypeKind::Ref(_, inner) => *inner, - _ => todo!("proepr error here"), + _ => todo!("proper error here"), }; ( @@ -706,6 +714,16 @@ fn lower_expr( }; let (mut value, ty, _span) = lower_expr(builder, inner, type_hint)?; + if let Some(local) = value.get_local() { + if *mutable && !builder.body.locals[local].mutable { + return Err(LoweringError::CantTakeMutableBorrow { + span: *as_ref_span, + declare_span: builder.body.locals[local].span, + file_id: builder.file_id, + }); + } + } + // check if its a use directly, to avoid a temporary. value = match value { RValue::Use(op, _span) => RValue::Ref(*mutable, op, *as_ref_span),