Add records to the dev backend

This commit is contained in:
Brendan Hansknecht 2021-05-15 20:18:04 -07:00
parent 0ff7c98def
commit 950d380ea0
3 changed files with 1046 additions and 47 deletions

View file

@ -2,7 +2,7 @@ use crate::{Backend, Env, Relocation};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::{BranchInfo, Literal, Stmt}; use roc_mono::ir::{BranchInfo, Literal, Stmt, Wrapped};
use roc_mono::layout::{Builtin, Layout}; use roc_mono::layout::{Builtin, Layout};
use std::marker::PhantomData; use std::marker::PhantomData;
use target_lexicon::Triple; use target_lexicon::Triple;
@ -10,6 +10,8 @@ use target_lexicon::Triple;
pub mod aarch64; pub mod aarch64;
pub mod x86_64; pub mod x86_64;
const PTR_SIZE: u32 = 64;
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> { pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
const GENERAL_PARAM_REGS: &'static [GeneralReg]; const GENERAL_PARAM_REGS: &'static [GeneralReg];
const GENERAL_RETURN_REGS: &'static [GeneralReg]; const GENERAL_RETURN_REGS: &'static [GeneralReg];
@ -145,8 +147,6 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[allow(dead_code)] #[allow(dead_code)]
pub enum SymbolStorage<GeneralReg: RegTrait, FloatReg: RegTrait> { pub enum SymbolStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
// These may need layout, but I am not sure.
// I think whenever a symbol would be used, we specify layout anyways.
GeneralReg(GeneralReg), GeneralReg(GeneralReg),
FloatReg(FloatReg), FloatReg(FloatReg),
Base(i32), Base(i32),
@ -171,7 +171,7 @@ pub struct Backend64Bit<
last_seen_map: MutMap<Symbol, *const Stmt<'a>>, last_seen_map: MutMap<Symbol, *const Stmt<'a>>,
free_map: MutMap<*const Stmt<'a>, Vec<'a, Symbol>>, free_map: MutMap<*const Stmt<'a>, Vec<'a, Symbol>>,
symbols_map: MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>, symbol_storage_map: MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
literal_map: MutMap<Symbol, Literal<'a>>, literal_map: MutMap<Symbol, Literal<'a>>,
// This should probably be smarter than a vec. // This should probably be smarter than a vec.
@ -211,7 +211,7 @@ impl<
relocs: bumpalo::vec!(in env.arena), relocs: bumpalo::vec!(in env.arena),
last_seen_map: MutMap::default(), last_seen_map: MutMap::default(),
free_map: MutMap::default(), free_map: MutMap::default(),
symbols_map: MutMap::default(), symbol_storage_map: MutMap::default(),
literal_map: MutMap::default(), literal_map: MutMap::default(),
general_free_regs: bumpalo::vec![in env.arena], general_free_regs: bumpalo::vec![in env.arena],
general_used_regs: bumpalo::vec![in env.arena], general_used_regs: bumpalo::vec![in env.arena],
@ -233,7 +233,7 @@ impl<
self.fn_call_stack_size = 0; self.fn_call_stack_size = 0;
self.last_seen_map.clear(); self.last_seen_map.clear();
self.free_map.clear(); self.free_map.clear();
self.symbols_map.clear(); self.symbol_storage_map.clear();
self.buf.clear(); self.buf.clear();
self.general_used_callee_saved_regs.clear(); self.general_used_callee_saved_regs.clear();
self.general_free_regs.clear(); self.general_free_regs.clear();
@ -310,9 +310,9 @@ impl<
} }
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String> { fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String> {
CC::load_args(&mut self.symbols_map, args)?; CC::load_args(&mut self.symbol_storage_map, args)?;
// Update used and free regs. // Update used and free regs.
for (sym, storage) in &self.symbols_map { for (sym, storage) in &self.symbol_storage_map {
match storage { match storage {
SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg(reg, _) => { SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg(reg, _) => {
self.general_free_regs.retain(|r| *r != *reg); self.general_free_regs.retain(|r| *r != *reg);
@ -363,7 +363,7 @@ impl<
// Put values in param regs or on top of the stack. // Put values in param regs or on top of the stack.
let tmp_stack_size = CC::store_args( let tmp_stack_size = CC::store_args(
&mut self.buf, &mut self.buf,
&self.symbols_map, &self.symbol_storage_map,
args, args,
arg_layouts, arg_layouts,
ret_layout, ret_layout,
@ -398,7 +398,7 @@ impl<
_cond_layout: &Layout<'a>, // cond_layout must be a integer due to potential jump table optimizations. _cond_layout: &Layout<'a>, // cond_layout must be a integer due to potential jump table optimizations.
branches: &'a [(u64, BranchInfo<'a>, Stmt<'a>)], branches: &'a [(u64, BranchInfo<'a>, Stmt<'a>)],
default_branch: &(BranchInfo<'a>, &'a Stmt<'a>), default_branch: &(BranchInfo<'a>, &'a Stmt<'a>),
_ret_layout: &Layout<'a>, ret_layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
// Switches are a little complex due to keeping track of jumps. // Switches are a little complex due to keeping track of jumps.
// In general I am trying to not have to loop over things multiple times or waste memory. // In general I am trying to not have to loop over things multiple times or waste memory.
@ -416,7 +416,7 @@ impl<
let start_offset = ASM::jne_reg64_imm64_imm32(&mut self.buf, cond_reg, *val, 0); let start_offset = ASM::jne_reg64_imm64_imm32(&mut self.buf, cond_reg, *val, 0);
// Build all statements in this branch. // Build all statements in this branch.
self.build_stmt(stmt)?; self.build_stmt(stmt, ret_layout)?;
// Build unconditional jump to the end of this switch. // Build unconditional jump to the end of this switch.
// Since we don't know the offset yet, set it to 0 and overwrite later. // Since we don't know the offset yet, set it to 0 and overwrite later.
@ -440,7 +440,7 @@ impl<
} }
let (branch_info, stmt) = default_branch; let (branch_info, stmt) = default_branch;
if let BranchInfo::None = branch_info { if let BranchInfo::None = branch_info {
self.build_stmt(stmt)?; self.build_stmt(stmt, ret_layout)?;
// Update all return jumps to jump past the default case. // Update all return jumps to jump past the default case.
let ret_offset = self.buf.len(); let ret_offset = self.buf.len();
@ -515,6 +515,52 @@ impl<
Ok(()) Ok(())
} }
fn create_struct(
&mut self,
sym: &Symbol,
layout: &Layout<'a>,
fields: &'a [Symbol],
) -> Result<(), String> {
if let Layout::Struct(field_layouts) = layout {
let struct_size = layout.stack_size(PTR_SIZE);
let offset = self.increase_stack_size(struct_size)?;
self.symbol_storage_map
.insert(*sym, SymbolStorage::Base(offset));
let mut current_offset = offset;
for (field, field_layout) in fields.iter().zip(field_layouts.iter()) {
self.copy_symbol_to_stack_offset(current_offset, field, field_layout)?;
let field_size = field_layout.stack_size(PTR_SIZE);
current_offset += field_size as i32;
}
Ok(())
} else {
Err(format!("struct has invalid layout: {:?}", layout))
}
}
fn load_access_at_index(
&mut self,
sym: &Symbol,
structure: &Symbol,
index: u64,
field_layouts: &'a [Layout<'a>],
_wrapped: &Wrapped,
) -> Result<(), String> {
if let Some(SymbolStorage::Base(struct_offset)) = self.symbol_storage_map.get(structure) {
let mut data_offset = *struct_offset;
for i in 0..index {
let field_size = field_layouts[i as usize].stack_size(PTR_SIZE);
data_offset += field_size as i32;
}
self.symbol_storage_map
.insert(*sym, SymbolStorage::Base(data_offset));
Ok(())
} else {
Err(format!("unknown struct: {:?}", structure))
}
}
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String> { fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String> {
match lit { match lit {
Literal::Int(x) => { Literal::Int(x) => {
@ -534,7 +580,7 @@ impl<
} }
fn free_symbol(&mut self, sym: &Symbol) { fn free_symbol(&mut self, sym: &Symbol) {
self.symbols_map.remove(sym); self.symbol_storage_map.remove(sym);
for i in 0..self.general_used_regs.len() { for i in 0..self.general_used_regs.len() {
let (reg, saved_sym) = self.general_used_regs[i]; let (reg, saved_sym) = self.general_used_regs[i];
if saved_sym == *sym { if saved_sym == *sym {
@ -545,8 +591,8 @@ impl<
} }
} }
fn return_symbol(&mut self, sym: &Symbol) -> Result<(), String> { fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String> {
let val = self.symbols_map.get(sym); let val = self.symbol_storage_map.get(sym);
match val { match val {
Some(SymbolStorage::GeneralReg(reg)) if *reg == CC::GENERAL_RETURN_REGS[0] => Ok(()), Some(SymbolStorage::GeneralReg(reg)) if *reg == CC::GENERAL_RETURN_REGS[0] => Ok(()),
Some(SymbolStorage::GeneralReg(reg)) => { Some(SymbolStorage::GeneralReg(reg)) => {
@ -560,6 +606,20 @@ impl<
ASM::mov_freg64_freg64(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *reg); ASM::mov_freg64_freg64(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *reg);
Ok(()) Ok(())
} }
Some(SymbolStorage::Base(offset)) => match layout {
Layout::Builtin(Builtin::Int64) => {
ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset);
Ok(())
}
Layout::Builtin(Builtin::Float64) => {
ASM::mov_freg64_base32(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *offset);
Ok(())
}
x => Err(format!(
"returning symbol with layout, {:?}, is not yet implemented",
x
)),
},
Some(x) => Err(format!( Some(x) => Err(format!(
"returning symbol storage, {:?}, is not yet implemented", "returning symbol storage, {:?}, is not yet implemented",
x x
@ -595,7 +655,7 @@ impl<
}?; }?;
self.general_used_regs.push((reg, *sym)); self.general_used_regs.push((reg, *sym));
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::GeneralReg(reg)); .insert(*sym, SymbolStorage::GeneralReg(reg));
Ok(reg) Ok(reg)
} }
@ -616,27 +676,28 @@ impl<
}?; }?;
self.float_used_regs.push((reg, *sym)); self.float_used_regs.push((reg, *sym));
self.symbols_map.insert(*sym, SymbolStorage::FloatReg(reg)); self.symbol_storage_map
.insert(*sym, SymbolStorage::FloatReg(reg));
Ok(reg) Ok(reg)
} }
fn load_to_general_reg(&mut self, sym: &Symbol) -> Result<GeneralReg, String> { fn load_to_general_reg(&mut self, sym: &Symbol) -> Result<GeneralReg, String> {
let val = self.symbols_map.remove(sym); let val = self.symbol_storage_map.remove(sym);
match val { match val {
Some(SymbolStorage::GeneralReg(reg)) => { Some(SymbolStorage::GeneralReg(reg)) => {
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::GeneralReg(reg)); .insert(*sym, SymbolStorage::GeneralReg(reg));
Ok(reg) Ok(reg)
} }
Some(SymbolStorage::Base(offset)) => { Some(SymbolStorage::Base(offset)) => {
let reg = self.claim_general_reg(sym)?; let reg = self.claim_general_reg(sym)?;
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset)); .insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32); ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32);
Ok(reg) Ok(reg)
} }
Some(SymbolStorage::BaseAndGeneralReg(reg, offset)) => { Some(SymbolStorage::BaseAndGeneralReg(reg, offset)) => {
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset)); .insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
Ok(reg) Ok(reg)
} }
@ -648,21 +709,22 @@ impl<
} }
fn load_to_float_reg(&mut self, sym: &Symbol) -> Result<FloatReg, String> { fn load_to_float_reg(&mut self, sym: &Symbol) -> Result<FloatReg, String> {
let val = self.symbols_map.remove(sym); let val = self.symbol_storage_map.remove(sym);
match val { match val {
Some(SymbolStorage::FloatReg(reg)) => { Some(SymbolStorage::FloatReg(reg)) => {
self.symbols_map.insert(*sym, SymbolStorage::FloatReg(reg)); self.symbol_storage_map
.insert(*sym, SymbolStorage::FloatReg(reg));
Ok(reg) Ok(reg)
} }
Some(SymbolStorage::Base(offset)) => { Some(SymbolStorage::Base(offset)) => {
let reg = self.claim_float_reg(sym)?; let reg = self.claim_float_reg(sym)?;
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset)); .insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32); ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32);
Ok(reg) Ok(reg)
} }
Some(SymbolStorage::BaseAndFloatReg(reg, offset)) => { Some(SymbolStorage::BaseAndFloatReg(reg, offset)) => {
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset)); .insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
Ok(reg) Ok(reg)
} }
@ -674,54 +736,77 @@ impl<
} }
fn free_to_stack(&mut self, sym: &Symbol) -> Result<(), String> { fn free_to_stack(&mut self, sym: &Symbol) -> Result<(), String> {
let val = self.symbols_map.remove(sym); let val = self.symbol_storage_map.remove(sym);
match val { match val {
Some(SymbolStorage::GeneralReg(reg)) => { Some(SymbolStorage::GeneralReg(reg)) => {
let offset = self.increase_stack_size(8)? as i32; let offset = self.increase_stack_size(8)?;
// For base addresssing, use the negative offset - 8. // For base addresssing, use the negative offset - 8.
ASM::mov_base32_reg64(&mut self.buf, -offset - 8, reg); ASM::mov_base32_reg64(&mut self.buf, offset, reg);
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::Base(-offset - 8)); .insert(*sym, SymbolStorage::Base(offset));
Ok(()) Ok(())
} }
Some(SymbolStorage::FloatReg(reg)) => { Some(SymbolStorage::FloatReg(reg)) => {
let offset = self.increase_stack_size(8)? as i32; let offset = self.increase_stack_size(8)?;
// For base addresssing, use the negative offset. // For base addresssing, use the negative offset.
ASM::mov_base32_freg64(&mut self.buf, -offset - 8, reg); ASM::mov_base32_freg64(&mut self.buf, offset, reg);
self.symbols_map self.symbol_storage_map
.insert(*sym, SymbolStorage::Base(-offset - 8)); .insert(*sym, SymbolStorage::Base(offset));
Ok(()) Ok(())
} }
Some(SymbolStorage::Base(offset)) => { Some(SymbolStorage::Base(offset)) => {
self.symbols_map.insert(*sym, SymbolStorage::Base(offset)); self.symbol_storage_map
.insert(*sym, SymbolStorage::Base(offset));
Ok(()) Ok(())
} }
Some(SymbolStorage::BaseAndGeneralReg(_, offset)) => { Some(SymbolStorage::BaseAndGeneralReg(_, offset)) => {
self.symbols_map.insert(*sym, SymbolStorage::Base(offset)); self.symbol_storage_map
.insert(*sym, SymbolStorage::Base(offset));
Ok(()) Ok(())
} }
Some(SymbolStorage::BaseAndFloatReg(_, offset)) => { Some(SymbolStorage::BaseAndFloatReg(_, offset)) => {
self.symbols_map.insert(*sym, SymbolStorage::Base(offset)); self.symbol_storage_map
.insert(*sym, SymbolStorage::Base(offset));
Ok(()) Ok(())
} }
None => Err(format!("Unknown symbol: {}", sym)), None => Err(format!("Unknown symbol: {}", sym)),
} }
} }
/// increase_stack_size increase the current stack size and returns the offset of the stack. /// increase_stack_size increase the current stack size `amount` bytes.
fn increase_stack_size(&mut self, amount: u32) -> Result<u32, String> { /// It returns base pointer relative offset of the new data.
fn increase_stack_size(&mut self, amount: u32) -> Result<i32, String> {
debug_assert!(amount > 0); debug_assert!(amount > 0);
let offset = self.stack_size;
if let Some(new_size) = self.stack_size.checked_add(amount) { if let Some(new_size) = self.stack_size.checked_add(amount) {
// Since stack size is u32, but the max offset is i32, if we pass i32 max, we have overflowed. // Since stack size is u32, but the max offset is i32, if we pass i32 max, we have overflowed.
if new_size > i32::MAX as u32 { if new_size > i32::MAX as u32 {
Err("Ran out of stack space".to_string()) Err("Ran out of stack space".to_string())
} else { } else {
self.stack_size = new_size; self.stack_size = new_size;
let offset = -(self.stack_size as i32);
Ok(offset) Ok(offset)
} }
} else { } else {
Err("Ran out of stack space".to_string()) Err("Ran out of stack space".to_string())
} }
} }
fn copy_symbol_to_stack_offset(
&mut self,
offset: i32,
sym: &Symbol,
layout: &Layout<'a>,
) -> Result<(), String> {
match layout {
Layout::Builtin(Builtin::Int64) => {
let reg = self.load_to_general_reg(sym)?;
ASM::mov_base32_reg64(&mut self.buf, offset, reg);
Ok(())
}
x => Err(format!(
"copying data to the stack with layout, {:?}, not implemented yet",
x
)),
}
}
} }

View file

@ -8,7 +8,7 @@ use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::{ModuleName, TagName}; use roc_module::ident::{ModuleName, TagName};
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, Symbol}; use roc_module::symbol::{Interns, Symbol};
use roc_mono::ir::{BranchInfo, CallType, Expr, JoinPointId, Literal, Proc, Stmt}; use roc_mono::ir::{BranchInfo, CallType, Expr, JoinPointId, Literal, Proc, Stmt, Wrapped};
use roc_mono::layout::{Builtin, Layout, LayoutIds}; use roc_mono::layout::{Builtin, Layout, LayoutIds};
use target_lexicon::Triple; use target_lexicon::Triple;
@ -77,22 +77,22 @@ where
// let duration = start.elapsed(); // let duration = start.elapsed();
// println!("Time to calculate lifetimes: {:?}", duration); // println!("Time to calculate lifetimes: {:?}", duration);
// println!("{:?}", self.last_seen_map()); // println!("{:?}", self.last_seen_map());
self.build_stmt(&proc.body)?; self.build_stmt(&proc.body, &proc.ret_layout)?;
self.finalize() self.finalize()
} }
/// build_stmt builds a statement and outputs at the end of the buffer. /// build_stmt builds a statement and outputs at the end of the buffer.
fn build_stmt(&mut self, stmt: &Stmt<'a>) -> Result<(), String> { fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) -> Result<(), String> {
match stmt { match stmt {
Stmt::Let(sym, expr, layout, following) => { Stmt::Let(sym, expr, layout, following) => {
self.build_expr(sym, expr, layout)?; self.build_expr(sym, expr, layout)?;
self.free_symbols(stmt); self.free_symbols(stmt);
self.build_stmt(following)?; self.build_stmt(following, ret_layout)?;
Ok(()) Ok(())
} }
Stmt::Ret(sym) => { Stmt::Ret(sym) => {
self.load_literal_symbols(&[*sym])?; self.load_literal_symbols(&[*sym])?;
self.return_symbol(sym)?; self.return_symbol(sym, ret_layout)?;
self.free_symbols(stmt); self.free_symbols(stmt);
Ok(()) Ok(())
} }
@ -106,7 +106,7 @@ where
// for now, treat invoke as a normal call // for now, treat invoke as a normal call
let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass); let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass);
self.build_stmt(&stmt) self.build_stmt(&stmt, ret_layout)
} }
Stmt::Switch { Stmt::Switch {
cond_symbol, cond_symbol,
@ -217,6 +217,16 @@ where
x => Err(format!("the call type, {:?}, is not yet implemented", x)), x => Err(format!("the call type, {:?}, is not yet implemented", x)),
} }
} }
Expr::Struct(fields) => {
self.load_literal_symbols(fields)?;
self.create_struct(sym, layout, fields)
}
Expr::AccessAtIndex {
index,
field_layouts,
structure,
wrapped,
} => self.load_access_at_index(sym, structure, *index, field_layouts, wrapped),
x => Err(format!("the expression, {:?}, is not yet implemented", x)), x => Err(format!("the expression, {:?}, is not yet implemented", x)),
} }
} }
@ -347,11 +357,29 @@ where
Ok(()) Ok(())
} }
/// create_struct creates a struct with the elements specified loaded into it as data.
fn create_struct(
&mut self,
sym: &Symbol,
layout: &Layout<'a>,
fields: &'a [Symbol],
) -> Result<(), String>;
/// load_access_at_index loads into `sym` the value at `index` in `structure`.
fn load_access_at_index(
&mut self,
sym: &Symbol,
structure: &Symbol,
index: u64,
field_layouts: &'a [Layout<'a>],
wrapped: &Wrapped,
) -> Result<(), String>;
/// load_literal sets a symbol to be equal to a literal. /// load_literal sets a symbol to be equal to a literal.
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String>; fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String>;
/// return_symbol moves a symbol to the correct return location for the backend. /// return_symbol moves a symbol to the correct return location for the backend.
fn return_symbol(&mut self, sym: &Symbol) -> Result<(), String>; fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String>;
/// free_symbols will free all symbols for the given statement. /// free_symbols will free all symbols for the given statement.
fn free_symbols(&mut self, stmt: &Stmt<'a>) { fn free_symbols(&mut self, stmt: &Stmt<'a>) {

View file

@ -0,0 +1,886 @@
#[macro_use]
extern crate indoc;
#[macro_use]
mod helpers;
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
mod gen_record {
#[test]
fn basic_record() {
assert_evals_to!(
indoc!(
r#"
{ y: 17, x: 15, z: 19 }.x
"#
),
15,
i64
);
assert_evals_to!(
indoc!(
r#"
{ x: 15, y: 17, z: 19 }.y
"#
),
17,
i64
);
assert_evals_to!(
indoc!(
r#"
{ x: 15, y: 17, z: 19 }.z
"#
),
19,
i64
);
}
// #[test]
// fn f64_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
// rec.x
// "#
// ),
// 15.1,
// f64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
// rec.y
// "#
// ),
// 17.2,
// f64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17.2, x: 15.1, z: 19.3 }
// rec.z
// "#
// ),
// 19.3,
// f64
// );
// }
// #[test]
// fn fn_record() {
// assert_evals_to!(
// indoc!(
// r#"
// getRec = \x -> { y: 17, x, z: 19 }
// (getRec 15).x
// "#
// ),
// 15,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.y
// "#
// ),
// 17,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.z
// "#
// ),
// 19,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.z + rec.x
// "#
// ),
// 34,
// i64
// );
// }
// #[test]
// fn def_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { y: 17, x: 15, z: 19 }
// rec.x
// "#
// ),
// 15,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.y
// "#
// ),
// 17,
// i64
// );
// assert_evals_to!(
// indoc!(
// r#"
// rec = { x: 15, y: 17, z: 19 }
// rec.z
// "#
// ),
// 19,
// i64
// );
// }
// #[test]
// fn when_on_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2 } is
// { x } -> x + 3
// "#
// ),
// 5,
// i64
// );
// }
// #[test]
// fn when_record_with_guard_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2, y: 3.14 } is
// { x: var } -> var + 3
// "#
// ),
// 5,
// i64
// );
// }
// #[test]
// fn let_with_record_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// { x } = { x: 0x2, y: 3.14 }
// x
// "#
// ),
// 2,
// i64
// );
// }
// #[test]
// fn record_guard_pattern() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x: 0x2, y: 3.14 } is
// { x: 0x4 } -> 5
// { x } -> x + 3
// "#
// ),
// 5,
// i64
// );
// }
// #[test]
// fn twice_record_access() {
// assert_evals_to!(
// indoc!(
// r#"
// x = {a: 0x2, b: 0x3 }
// x.a + x.b
// "#
// ),
// 5,
// i64
// );
// }
// #[test]
// fn empty_record() {
// assert_evals_to!(
// indoc!(
// r#"
// v = {}
// v
// "#
// ),
// (),
// ()
// );
// }
// #[test]
// fn i64_record2_literal() {
// assert_evals_to!(
// indoc!(
// r#"
// { x: 3, y: 5 }
// "#
// ),
// (3, 5),
// (i64, i64)
// );
// }
// // #[test]
// // fn i64_record3_literal() {
// // assert_evals_to!(
// // indoc!(
// // r#"
// // { x: 3, y: 5, z: 17 }
// // "#
// // ),
// // (3, 5, 17),
// // (i64, i64, i64)
// // );
// // }
// #[test]
// fn f64_record2_literal() {
// assert_evals_to!(
// indoc!(
// r#"
// { x: 3.1, y: 5.1 }
// "#
// ),
// (3.1, 5.1),
// (f64, f64)
// );
// }
// // #[test]
// // fn f64_record3_literal() {
// // assert_evals_to!(
// // indoc!(
// // r#"
// // { x: 3.1, y: 5.1, z: 17.1 }
// // "#
// // ),
// // (3.1, 5.1, 17.1),
// // (f64, f64, f64)
// // );
// // }
// // #[test]
// // fn bool_record4_literal() {
// // assert_evals_to!(
// // indoc!(
// // r#"
// // record : { a : Bool, b : Bool, c : Bool, d : Bool }
// // record = { a: True, b: True, c : True, d : Bool }
// // record
// // "#
// // ),
// // (true, false, false, true),
// // (bool, bool, bool, bool)
// // );
// // }
// #[test]
// fn i64_record1_literal() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 3 }
// "#
// ),
// 3,
// i64
// );
// }
// // #[test]
// // fn i64_record9_literal() {
// // assert_evals_to!(
// // indoc!(
// // r#"
// // { a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
// // "#
// // ),
// // (3, 5, 17, 1, 9, 12, 13, 14, 15),
// // (i64, i64, i64, i64, i64, i64, i64, i64, i64)
// // );
// // }
// // #[test]
// // fn f64_record3_literal() {
// // assert_evals_to!(
// // indoc!(
// // r#"
// // { x: 3.1, y: 5.1, z: 17.1 }
// // "#
// // ),
// // (3.1, 5.1, 17.1),
// // (f64, f64, f64)
// // );
// // }
// #[test]
// fn bool_literal() {
// assert_evals_to!(
// indoc!(
// r#"
// x : Bool
// x = True
// x
// "#
// ),
// true,
// bool
// );
// }
// #[test]
// fn return_record() {
// assert_evals_to!(
// indoc!(
// r#"
// x = 4
// y = 3
// { x, y }
// "#
// ),
// (4, 3),
// (i64, i64)
// );
// }
// #[test]
// fn optional_field_when_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// main =
// a = f { x: Blue, y: 7 }
// b = f { x: Blue }
// c = f { x: Red, y: 11 }
// d = f { x: Red }
// a * b * c * d
// "#
// ),
// 3 * 5 * 7 * 11,
// i64
// );
// }
// #[test]
// fn optional_field_when_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// a = f { x: Blue, y: 7 }
// b = f { x: Blue }
// c = f { x: Red, y: 11 }
// d = f { x: Red }
// a * b * c * d
// "#
// ),
// 3 * 5 * 7 * 11,
// i64
// );
// }
// #[test]
// fn optional_field_when_no_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_when_no_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// { x ? 10, y } = r
// x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_let_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { y: 9 }
// "#
// ),
// 19,
// i64
// );
// }
// #[test]
// fn optional_field_let_no_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \r ->
// { x ? 10, y } = r
// x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_let_no_use_default_nested() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// { x ? 10, y } = r
// x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_function_use_default() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \{ x ? 10, y } -> x + y
// f { y: 9 }
// "#
// ),
// 19,
// i64
// );
// }
// #[test]
// #[ignore]
// fn optional_field_function_no_use_default() {
// // blocked on https://github.com/rtfeldman/roc/issues/786
// assert_evals_to!(
// indoc!(
// r#"
// app "test" provides [ main ] to "./platform"
// f = \{ x ? 10, y } -> x + y
// main =
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// #[ignore]
// fn optional_field_function_no_use_default_nested() {
// // blocked on https://github.com/rtfeldman/roc/issues/786
// assert_evals_to!(
// indoc!(
// r#"
// f = \{ x ? 10, y } -> x + y
// f { x: 4, y: 9 }
// "#
// ),
// 13,
// i64
// );
// }
// #[test]
// fn optional_field_singleton_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { x : 4 } is
// { x ? 3 } -> x
// "#
// ),
// 4,
// i64
// );
// }
// #[test]
// fn optional_field_empty_record() {
// assert_evals_to!(
// indoc!(
// r#"
// when { } is
// { x ? 3 } -> x
// "#
// ),
// 3,
// i64
// );
// }
// #[test]
// fn return_record_2() {
// assert_evals_to!(
// indoc!(
// r#"
// { x: 3, y: 5 }
// "#
// ),
// [3, 5],
// [i64; 2]
// );
// }
// #[test]
// fn return_record_3() {
// assert_evals_to!(
// indoc!(
// r#"
// { x: 3, y: 5, z: 4 }
// "#
// ),
// (3, 5, 4),
// (i64, i64, i64)
// );
// }
// #[test]
// fn return_record_4() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 3, b: 5, c: 4, d: 2 }
// "#
// ),
// [3, 5, 4, 2],
// [i64; 4]
// );
// }
// #[test]
// fn return_record_5() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 3, b: 5, c: 4, d: 2, e: 1 }
// "#
// ),
// [3, 5, 4, 2, 1],
// [i64; 5]
// );
// }
// #[test]
// fn return_record_6() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
// "#
// ),
// [3, 5, 4, 2, 1, 7],
// [i64; 6]
// );
// }
// #[test]
// fn return_record_7() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
// "#
// ),
// [3, 5, 4, 2, 1, 7, 8],
// [i64; 7]
// );
// }
// #[test]
// fn return_record_float_int() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 3.14, b: 0x1 }
// "#
// ),
// (3.14, 0x1),
// (f64, i64)
// );
// }
// #[test]
// fn return_record_int_float() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 0x1, b: 3.14 }
// "#
// ),
// (0x1, 3.14),
// (i64, f64)
// );
// }
// #[test]
// fn return_record_float_float() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 6.28, b: 3.14 }
// "#
// ),
// (6.28, 3.14),
// (f64, f64)
// );
// }
// #[test]
// fn return_record_float_float_float() {
// assert_evals_to!(
// indoc!(
// r#"
// { a: 6.28, b: 3.14, c: 0.1 }
// "#
// ),
// (6.28, 3.14, 0.1),
// (f64, f64, f64)
// );
// }
// #[test]
// fn return_nested_record() {
// assert_evals_to!(
// indoc!(
// r#"
// { flag: 0x0, payload: { a: 6.28, b: 3.14, c: 0.1 } }
// "#
// ),
// (0x0, (6.28, 3.14, 0.1)),
// (i64, (f64, f64, f64))
// );
// }
// #[test]
// fn accessor() {
// assert_evals_to!(
// indoc!(
// r#"
// .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
// "#
// ),
// 7,
// i64
// );
// }
// #[test]
// fn accessor_single_element_record() {
// assert_evals_to!(
// indoc!(
// r#"
// .foo { foo: 4 }
// "#
// ),
// 4,
// i64
// );
// }
// #[test]
// fn update_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { foo: 42, bar: 6.28 }
// { rec & foo: rec.foo + 1 }
// "#
// ),
// (6.28, 43),
// (f64, i64)
// );
// }
// #[test]
// fn update_single_element_record() {
// assert_evals_to!(
// indoc!(
// r#"
// rec = { foo: 42}
// { rec & foo: rec.foo + 1 }
// "#
// ),
// 43,
// i64
// );
// }
// #[test]
// fn booleans_in_record() {
// assert_evals_to!(
// indoc!("{ x: 1 == 1, y: 1 == 1 }"),
// (true, true),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 != 1, y: 1 == 1 }"),
// (false, true),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 == 1, y: 1 != 1 }"),
// (true, false),
// (bool, bool)
// );
// assert_evals_to!(
// indoc!("{ x: 1 != 1, y: 1 != 1 }"),
// (false, false),
// (bool, bool)
// );
// }
// #[test]
// fn alignment_in_record() {
// assert_evals_to!(
// indoc!("{ c: 32, b: if True then Red else if True then Green else Blue, a: 1 == 1 }"),
// (32i64, true, 2u8),
// (i64, bool, u8)
// );
// }
// #[test]
// fn blue_and_present() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// f { x: Blue, y: 7 }
// "#
// ),
// 7,
// i64
// );
// }
// #[test]
// fn blue_and_absent() {
// assert_evals_to!(
// indoc!(
// r#"
// f = \r ->
// when r is
// { x: Blue, y ? 3 } -> y
// { x: Red, y ? 5 } -> y
// f { x: Blue }
// "#
// ),
// 3,
// i64
// );
// }
}