From 51e00e4efda7a9b6233fe83b2b40f7711af78f54 Mon Sep 17 00:00:00 2001 From: Alaina <68250402+alaidriel@users.noreply.github.com> Date: Fri, 2 Feb 2024 18:52:34 -0600 Subject: [PATCH] add record tests and fix some bugs --- ...kyac__pass__typecheck__tests__records.snap | 4 ++-- crates/kyac/src/ast/span.rs | 4 +++- .../kyac/src/backend/kyir/arch/amd64/mod.rs | 4 +++- .../src/backend/kyir/arch/amd64/red_zone.rs | 23 ++++++++++++++++++- crates/kyac/src/backend/kyir/translate/mod.rs | 12 +++++++--- crates/kyanite/tests/kyir/mod.rs | 21 +++++++++++++++++ examples/kyir/nested-records.kya | 22 ++++++++++++++++++ examples/kyir/record-with-addition.kya | 13 +++++++++++ examples/kyir/simple-record.kya | 13 +++++++++++ 9 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 examples/kyir/nested-records.kya create mode 100644 examples/kyir/record-with-addition.kya create mode 100644 examples/kyir/simple-record.kya diff --git a/crates/kyac/snapshots/kyac__pass__typecheck__tests__records.snap b/crates/kyac/snapshots/kyac__pass__typecheck__tests__records.snap index 4d8de0a..f6d93e9 100644 --- a/crates/kyac/snapshots/kyac__pass__typecheck__tests__records.snap +++ b/crates/kyac/snapshots/kyac__pass__typecheck__tests__records.snap @@ -1,5 +1,5 @@ --- -source: kyanite-core/src/pass/typecheck.rs +source: crates/kyac/src/pass/typecheck.rs expression: pass.errors --- [ @@ -32,7 +32,7 @@ expression: pass.errors span: Span { line: 28, column: 26, - length: 20, + length: 3, }, text: "expression of type Bar", }, diff --git a/crates/kyac/src/ast/span.rs b/crates/kyac/src/ast/span.rs index 4ac7083..d0e4451 100644 --- a/crates/kyac/src/ast/span.rs +++ b/crates/kyac/src/ast/span.rs @@ -5,6 +5,7 @@ use crate::{ pub trait Combined { fn span(&self) -> Span { + assert!(self.end() >= self.start(), "likely a multi-line span"); Span::new(self.line(), self.start(), self.end() - self.start()) } fn start(&self) -> usize; @@ -74,7 +75,8 @@ impl Combined for Expr { Expr::Int(i) => i.token.span.column + i.token.span.length, Expr::Float(f) => f.token.span.column + f.token.span.length, Expr::Bool(b) => b.token.span.column + b.token.span.length, - Expr::Init(init) => init.parens.1.span.column + 1, + // TODO: support multi-line spans + Expr::Init(init) => init.name.span.column + init.name.span.length, } } diff --git a/crates/kyac/src/backend/kyir/arch/amd64/mod.rs b/crates/kyac/src/backend/kyir/arch/amd64/mod.rs index 61f0d16..44787e3 100644 --- a/crates/kyac/src/backend/kyir/arch/amd64/mod.rs +++ b/crates/kyac/src/backend/kyir/arch/amd64/mod.rs @@ -108,7 +108,9 @@ impl Frame for Amd64 { self.offset -= i64::try_from(match ty { Some(Type::UserDefined(ty)) => { let rec = symbols.get(ty).unwrap().record(); - rec.fields.len() * Self::word_size() + // The magic +1 is here because the first word is used to + // store the address of the record in the frame. + (rec.fields.len() + 1) * Self::word_size() } _ => 8, }) diff --git a/crates/kyac/src/backend/kyir/arch/amd64/red_zone.rs b/crates/kyac/src/backend/kyir/arch/amd64/red_zone.rs index d80ef5d..68b5c29 100644 --- a/crates/kyac/src/backend/kyir/arch/amd64/red_zone.rs +++ b/crates/kyac/src/backend/kyir/arch/amd64/red_zone.rs @@ -28,7 +28,8 @@ pub fn run(codegen: &mut Codegen) { AsmInstr::new(Instr::oper( Opcode::Sub, F::registers().stack.into(), - "$16".into(), + // Not sure if we actually need to align the stack to a multiple of 16 bytes, but we do it anyway. + format!("${}", next_multiple_of(fun.offset().abs(), 16)), None, )), ); @@ -36,3 +37,23 @@ pub fn run(codegen: &mut Codegen) { } } } + +/// Stolen from until this is in stable. +fn next_multiple_of(n: i64, rhs: i64) -> i64 { + if rhs == -1 { + return n; + } + + let r = n % rhs; + let m = if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) { + r + rhs + } else { + r + }; + + if m == 0 { + n + } else { + n + (rhs - m) + } +} diff --git a/crates/kyac/src/backend/kyir/translate/mod.rs b/crates/kyac/src/backend/kyir/translate/mod.rs index 83b4137..36bb100 100644 --- a/crates/kyac/src/backend/kyir/translate/mod.rs +++ b/crates/kyac/src/backend/kyir/translate/mod.rs @@ -167,9 +167,10 @@ impl Translate for ast::node::Access { let last = self.chain.last().unwrap().ident().name.to_string(); let (index, _) = flat .iter() + .rev() .enumerate() .find(|(_, (name, _))| name == &last) - .unwrap(); + .expect("field access for non-terminal fields is not yet supported"); frame.get(&parent, Some(temp), Some(index)) } } @@ -183,7 +184,9 @@ impl Translate for ast::node::Init { let id = translator.function.unwrap(); let frame = translator.functions.get_mut(&id).unwrap(); frame.allocate(translator.symbols, &name, Some(&ty)); - let begin = frame.get_offset(&name); + // We begin at the current frame's offset - one word size (since the current frame's offset is used + // to store the start address of the actual fields) + let begin = frame.get_offset(&name) - i64::try_from(F::word_size()).unwrap(); let end = begin - i64::try_from((initializers.len() - 1) * F::word_size()).unwrap(); let stmts: Vec = initializers .into_iter() @@ -199,7 +202,10 @@ impl Translate for ast::node::Init { ) }) .collect(); - // Evaluate the initializers, then return start address of initialized memory for record + // Evaluate the initializers, then return start address of initialized memory for record. + // This doesn't technically need to exist since the frame already stores this information. + // [-8, -16, -32, ...] + // [ start address, field 1, field 2, ...] ESeq::wrapped(Stmt::from(&stmts[..]), Const::::int(begin)) } } diff --git a/crates/kyanite/tests/kyir/mod.rs b/crates/kyanite/tests/kyir/mod.rs index e73abe8..0f58c06 100644 --- a/crates/kyanite/tests/kyir/mod.rs +++ b/crates/kyanite/tests/kyir/mod.rs @@ -61,3 +61,24 @@ fn called_nested_loop() -> Result<(), Box> { assert_eq!(res.output, "5\n6\n7\n8\n9\n"); Ok(()) } + +#[test] +fn simple_record() -> Result<(), Box> { + let res = run("kyir/simple-record.kya")?; + assert_eq!(res.output, "3\n"); + Ok(()) +} + +#[test] +fn record_with_addition() -> Result<(), Box> { + let res = run("kyir/record-with-addition.kya")?; + assert_eq!(res.output, "7\n"); + Ok(()) +} + +#[test] +fn nested_records() -> Result<(), Box> { + let res = run("kyir/nested-records.kya")?; + assert_eq!(res.output, "1\n2\ntrue\n"); + Ok(()) +} diff --git a/examples/kyir/nested-records.kya b/examples/kyir/nested-records.kya new file mode 100644 index 0000000..ce8f093 --- /dev/null +++ b/examples/kyir/nested-records.kya @@ -0,0 +1,22 @@ +rec Coordinate { + pos: Position, + is_fun: bool +} + +rec Position { + x: int, + y: int +} + +fun main() { + let c: Coordinate = Coordinate:init( + pos: Position:init(x: 1, y: 2), + is_fun: true + ); + let x: int = c.pos.x; + let y: int = c.pos.y; + println_int(x); + println_int(y); + let is_fun: bool = c.is_fun; + println_bool(is_fun); +} \ No newline at end of file diff --git a/examples/kyir/record-with-addition.kya b/examples/kyir/record-with-addition.kya new file mode 100644 index 0000000..8deddd4 --- /dev/null +++ b/examples/kyir/record-with-addition.kya @@ -0,0 +1,13 @@ +rec Coordinate { + x: int, + y: int +} + +fun main() { + let c: Coordinate = Coordinate:init( + x: 3, + y: 4 + ); + let sum: int = c.x + c.y; + println_int(sum); +} \ No newline at end of file diff --git a/examples/kyir/simple-record.kya b/examples/kyir/simple-record.kya new file mode 100644 index 0000000..d4b8e5b --- /dev/null +++ b/examples/kyir/simple-record.kya @@ -0,0 +1,13 @@ +rec Coordinate { + x: int, + y: int +} + +fun main() { + let c: Coordinate = Coordinate:init( + x: 3, + y: 4 + ); + let z: int = c.x; + println_int(z); +} \ No newline at end of file