mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge pull request #1623 from rtfeldman/gen-dev/records-base
This commit is contained in:
commit
e07057388b
6 changed files with 1261 additions and 49 deletions
|
@ -228,6 +228,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||||
_args: &'a [(Layout<'a>, Symbol)],
|
_args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
_ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
Err("Loading args not yet implemented for AArch64".to_string())
|
Err("Loading args not yet implemented for AArch64".to_string())
|
||||||
}
|
}
|
||||||
|
@ -242,6 +243,20 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
) -> Result<u32, String> {
|
) -> Result<u32, String> {
|
||||||
Err("Storing args not yet implemented for AArch64".to_string())
|
Err("Storing args not yet implemented for AArch64".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_struct<'a>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_struct_offset: i32,
|
||||||
|
_struct_size: u32,
|
||||||
|
_field_layouts: &[Layout<'a>],
|
||||||
|
_ret_reg: Option<AArch64GeneralReg>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("Returning structs not yet implemented for AArch64".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn returns_via_arg_pointer(_ret_layout: &Layout) -> Result<bool, String> {
|
||||||
|
Err("Returning via arg pointer not yet implemented for AArch64".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
|
|
|
@ -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];
|
||||||
|
@ -49,6 +51,8 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||||
args: &'a [(Layout<'a>, Symbol)],
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String>;
|
) -> Result<(), String>;
|
||||||
|
|
||||||
// store_args stores the args in registers and on the stack for function calling.
|
// store_args stores the args in registers and on the stack for function calling.
|
||||||
|
@ -61,6 +65,19 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<u32, String>;
|
) -> Result<u32, String>;
|
||||||
|
|
||||||
|
// return_struct returns a struct currently on the stack at `struct_offset`.
|
||||||
|
// It does so using registers and stack as necessary.
|
||||||
|
fn return_struct<'a>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
struct_offset: i32,
|
||||||
|
struct_size: u32,
|
||||||
|
field_layouts: &[Layout<'a>],
|
||||||
|
ret_reg: Option<GeneralReg>,
|
||||||
|
) -> Result<(), String>;
|
||||||
|
|
||||||
|
// returns true if the layout should be returned via an argument pointer.
|
||||||
|
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assembler contains calls to the backend assembly generator.
|
/// Assembler contains calls to the backend assembly generator.
|
||||||
|
@ -160,8 +177,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),
|
||||||
|
@ -186,7 +201,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.
|
||||||
|
@ -226,7 +241,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],
|
||||||
|
@ -248,7 +263,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();
|
||||||
|
@ -324,10 +339,14 @@ impl<
|
||||||
Ok((out.into_bump_slice(), out_relocs.into_bump_slice()))
|
Ok((out.into_bump_slice(), out_relocs.into_bump_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String> {
|
fn load_args(
|
||||||
CC::load_args(&mut self.symbols_map, args)?;
|
&mut self,
|
||||||
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
CC::load_args(&mut self.symbol_storage_map, args, ret_layout)?;
|
||||||
// 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);
|
||||||
|
@ -386,7 +405,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,
|
||||||
|
@ -421,7 +440,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.
|
||||||
|
@ -439,7 +458,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.
|
||||||
|
@ -463,7 +482,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();
|
||||||
|
@ -560,6 +579,61 @@ 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);
|
||||||
|
if struct_size > 0 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.symbol_storage_map.insert(*sym, SymbolStorage::Base(0));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// This is a single element struct. Just copy the single field to the stack.
|
||||||
|
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));
|
||||||
|
self.copy_symbol_to_stack_offset(offset, &fields[0], layout)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_struct_at_index(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
structure: &Symbol,
|
||||||
|
index: u64,
|
||||||
|
field_layouts: &'a [Layout<'a>],
|
||||||
|
) -> 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) => {
|
||||||
|
@ -579,7 +653,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 {
|
||||||
|
@ -590,8 +664,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)) => {
|
||||||
|
@ -605,6 +679,48 @@ 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(())
|
||||||
|
}
|
||||||
|
Layout::Struct(field_layouts) => {
|
||||||
|
let struct_size = layout.stack_size(PTR_SIZE);
|
||||||
|
if struct_size > 0 {
|
||||||
|
let struct_offset = if let Some(SymbolStorage::Base(offset)) =
|
||||||
|
self.symbol_storage_map.get(sym)
|
||||||
|
{
|
||||||
|
Ok(*offset)
|
||||||
|
} else {
|
||||||
|
Err(format!("unknown struct: {:?}", sym))
|
||||||
|
}?;
|
||||||
|
let ret_reg = if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER)
|
||||||
|
{
|
||||||
|
Some(self.load_to_general_reg(&Symbol::RET_POINTER)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
CC::return_struct(
|
||||||
|
&mut self.buf,
|
||||||
|
struct_offset,
|
||||||
|
struct_size,
|
||||||
|
field_layouts,
|
||||||
|
ret_reg,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Nothing to do for empty struct
|
||||||
|
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
|
||||||
|
@ -624,6 +740,25 @@ impl<
|
||||||
CC: CallConv<GeneralReg, FloatReg>,
|
CC: CallConv<GeneralReg, FloatReg>,
|
||||||
> Backend64Bit<'a, GeneralReg, FloatReg, ASM, CC>
|
> Backend64Bit<'a, GeneralReg, FloatReg, ASM, CC>
|
||||||
{
|
{
|
||||||
|
fn get_tmp_general_reg(&mut self) -> Result<GeneralReg, String> {
|
||||||
|
if !self.general_free_regs.is_empty() {
|
||||||
|
let free_reg = *self
|
||||||
|
.general_free_regs
|
||||||
|
.get(self.general_free_regs.len() - 1)
|
||||||
|
.unwrap();
|
||||||
|
if CC::general_callee_saved(&free_reg) {
|
||||||
|
self.general_used_callee_saved_regs.insert(free_reg);
|
||||||
|
}
|
||||||
|
Ok(free_reg)
|
||||||
|
} else if !self.general_used_regs.is_empty() {
|
||||||
|
let (reg, sym) = self.general_used_regs.remove(0);
|
||||||
|
self.free_to_stack(&sym)?;
|
||||||
|
Ok(reg)
|
||||||
|
} else {
|
||||||
|
Err("completely out of general purpose registers".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn claim_general_reg(&mut self, sym: &Symbol) -> Result<GeneralReg, String> {
|
fn claim_general_reg(&mut self, sym: &Symbol) -> Result<GeneralReg, String> {
|
||||||
let reg = if !self.general_free_regs.is_empty() {
|
let reg = if !self.general_free_regs.is_empty() {
|
||||||
let free_reg = self.general_free_regs.pop().unwrap();
|
let free_reg = self.general_free_regs.pop().unwrap();
|
||||||
|
@ -640,7 +775,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)
|
||||||
}
|
}
|
||||||
|
@ -661,27 +796,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)
|
||||||
}
|
}
|
||||||
|
@ -693,21 +829,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)
|
||||||
}
|
}
|
||||||
|
@ -719,54 +856,94 @@ 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,
|
||||||
|
to_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, to_offset, reg);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
|
let reg = self.load_to_float_reg(sym)?;
|
||||||
|
ASM::mov_base32_freg64(&mut self.buf, to_offset, reg);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Layout::Struct(_) if layout.safe_to_memcpy() => {
|
||||||
|
let tmp_reg = self.get_tmp_general_reg()?;
|
||||||
|
if let Some(SymbolStorage::Base(from_offset)) = self.symbol_storage_map.get(sym) {
|
||||||
|
for i in 0..layout.stack_size(PTR_SIZE) as i32 {
|
||||||
|
ASM::mov_reg64_base32(&mut self.buf, tmp_reg, from_offset + i);
|
||||||
|
ASM::mov_base32_reg64(&mut self.buf, to_offset + i, tmp_reg);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!("unknown struct: {:?}", sym))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => Err(format!(
|
||||||
|
"copying data to the stack with layout, {:?}, not implemented yet",
|
||||||
|
x
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
|
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage, PTR_SIZE};
|
||||||
use crate::Relocation;
|
use crate::Relocation;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
|
@ -177,10 +177,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
args: &'a [(Layout<'a>, Symbol)],
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||||
let mut general_i = 0;
|
let mut general_i = 0;
|
||||||
let mut float_i = 0;
|
let mut float_i = 0;
|
||||||
|
if X86_64SystemV::returns_via_arg_pointer(ret_layout)? {
|
||||||
|
symbol_map.insert(
|
||||||
|
Symbol::RET_POINTER,
|
||||||
|
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[general_i]),
|
||||||
|
);
|
||||||
|
general_i += 1;
|
||||||
|
}
|
||||||
for (layout, sym) in args.iter() {
|
for (layout, sym) in args.iter() {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => {
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
@ -359,6 +367,22 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
}
|
}
|
||||||
Ok(stack_offset as u32)
|
Ok(stack_offset as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_struct<'a>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_struct_offset: i32,
|
||||||
|
_struct_size: u32,
|
||||||
|
_field_layouts: &[Layout<'a>],
|
||||||
|
_ret_reg: Option<X86_64GeneralReg>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("Returning structs not yet implemented for X86_64".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String> {
|
||||||
|
// TODO: This may need to be more complex/extended to fully support the calling convention.
|
||||||
|
// details here: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf
|
||||||
|
Ok(ret_layout.stack_size(PTR_SIZE) > 16)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
|
@ -477,9 +501,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
args: &'a [(Layout<'a>, Symbol)],
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||||
for (i, (layout, sym)) in args.iter().enumerate() {
|
let mut i = 0;
|
||||||
|
if X86_64WindowsFastcall::returns_via_arg_pointer(ret_layout)? {
|
||||||
|
symbol_map.insert(
|
||||||
|
Symbol::RET_POINTER,
|
||||||
|
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[i]),
|
||||||
|
);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
for (layout, sym) in args.iter() {
|
||||||
if i < Self::GENERAL_PARAM_REGS.len() {
|
if i < Self::GENERAL_PARAM_REGS.len() {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => {
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
@ -496,6 +529,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
base_offset += match layout {
|
base_offset += match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => 8,
|
Layout::Builtin(Builtin::Int64) => 8,
|
||||||
|
@ -653,6 +687,22 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
}
|
}
|
||||||
Ok(stack_offset as u32)
|
Ok(stack_offset as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_struct<'a>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_struct_offset: i32,
|
||||||
|
_struct_size: u32,
|
||||||
|
_field_layouts: &[Layout<'a>],
|
||||||
|
_ret_reg: Option<X86_64GeneralReg>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("Returning structs not yet implemented for X86_64WindowsFastCall".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String> {
|
||||||
|
// TODO: This is not fully correct there are some exceptions for "vector" types.
|
||||||
|
// details here: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160#return-values
|
||||||
|
Ok(ret_layout.stack_size(PTR_SIZE) > 8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -66,7 +66,11 @@ where
|
||||||
|
|
||||||
// load_args is used to let the backend know what the args are.
|
// load_args is used to let the backend know what the args are.
|
||||||
// The backend should track these args so it can use them as needed.
|
// The backend should track these args so it can use them as needed.
|
||||||
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String>;
|
fn load_args(
|
||||||
|
&mut self,
|
||||||
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<(), String>;
|
||||||
|
|
||||||
/// Used for generating wrappers for malloc/realloc/free
|
/// Used for generating wrappers for malloc/realloc/free
|
||||||
fn build_wrapped_jmp(&mut self) -> Result<(&'a [u8], u64), String>;
|
fn build_wrapped_jmp(&mut self) -> Result<(&'a [u8], u64), String>;
|
||||||
|
@ -74,29 +78,29 @@ where
|
||||||
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
||||||
fn build_proc(&mut self, proc: Proc<'a>) -> Result<(&'a [u8], &[Relocation]), String> {
|
fn build_proc(&mut self, proc: Proc<'a>) -> Result<(&'a [u8], &[Relocation]), String> {
|
||||||
self.reset();
|
self.reset();
|
||||||
self.load_args(proc.args)?;
|
self.load_args(proc.args, &proc.ret_layout)?;
|
||||||
// let start = std::time::Instant::now();
|
// let start = std::time::Instant::now();
|
||||||
self.scan_ast(&proc.body);
|
self.scan_ast(&proc.body);
|
||||||
self.create_free_map();
|
self.create_free_map();
|
||||||
// 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(())
|
||||||
}
|
}
|
||||||
|
@ -218,6 +222,15 @@ 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::StructAtIndex {
|
||||||
|
index,
|
||||||
|
field_layouts,
|
||||||
|
structure,
|
||||||
|
} => self.load_struct_at_index(sym, structure, *index, field_layouts),
|
||||||
x => Err(format!("the expression, {:?}, is not yet implemented", x)),
|
x => Err(format!("the expression, {:?}, is not yet implemented", x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,11 +390,28 @@ 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_struct_at_index loads into `sym` the value at `index` in `structure`.
|
||||||
|
fn load_struct_at_index(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
structure: &Symbol,
|
||||||
|
index: u64,
|
||||||
|
field_layouts: &'a [Layout<'a>],
|
||||||
|
) -> 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>) {
|
||||||
|
|
937
compiler/gen_dev/tests/dev_records.rs
Normal file
937
compiler/gen_dev/tests/dev_records.rs
Normal file
|
@ -0,0 +1,937 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate indoc;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
||||||
|
mod dev_records {
|
||||||
|
#[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 nested_record() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
15,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
12,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.b
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
15,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.c
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
2,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 15, y: { a: 12, b: 15, c: 2}, 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_record1_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[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 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 }
|
||||||
|
|
||||||
|
// { rec & foo: rec.foo + 1 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// (6, 43),
|
||||||
|
// (i64, 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
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
}
|
|
@ -786,6 +786,9 @@ define_builtins! {
|
||||||
|
|
||||||
// used to initialize parameters in borrow.rs
|
// used to initialize parameters in borrow.rs
|
||||||
22 EMPTY_PARAM: "#empty_param"
|
22 EMPTY_PARAM: "#empty_param"
|
||||||
|
|
||||||
|
// used by the dev backend to store the pointer to where to store large return types
|
||||||
|
23 RET_POINTER: "#ret_pointer"
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue