mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge branch 'int-float-size-gen' into temp_numbers
This commit is contained in:
commit
c33cab57f2
14 changed files with 493 additions and 85 deletions
|
@ -212,7 +212,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
|||
/// Num.maxInt : Int
|
||||
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let int_var = var_store.fresh();
|
||||
let body = Int(int_var, i64::MAX);
|
||||
let int_percision_var = var_store.fresh();
|
||||
let body = Int(int_var, int_percision_var, i64::MAX);
|
||||
|
||||
Def {
|
||||
annotation: None,
|
||||
|
@ -226,7 +227,8 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
/// Num.minInt : Int
|
||||
fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let int_var = var_store.fresh();
|
||||
let body = Int(int_var, i64::MIN);
|
||||
let int_percision_var = var_store.fresh();
|
||||
let body = Int(int_var, int_percision_var, i64::MIN);
|
||||
|
||||
Def {
|
||||
annotation: None,
|
||||
|
@ -846,7 +848,7 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let body = RunLowLevel {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(arg_var, Int(var_store.fresh(), 1)),
|
||||
(arg_var, Int(var_store.fresh(), var_store.fresh(), 1)),
|
||||
(
|
||||
arg_var,
|
||||
RunLowLevel {
|
||||
|
@ -930,6 +932,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let bool_var = var_store.fresh();
|
||||
let float_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
let percision_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
let body = If {
|
||||
|
@ -943,7 +946,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(float_var, Var(Symbol::ARG_1)),
|
||||
(float_var, Float(unbound_zero_var, 0.0)),
|
||||
(float_var, Float(unbound_zero_var, percision_var, 0.0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
|
@ -1896,6 +1899,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let bool_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
let percision_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
let body = If {
|
||||
|
@ -1909,7 +1913,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, Float(unbound_zero_var, 0.0)),
|
||||
(num_var, Float(unbound_zero_var, percision_var, 0.0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
|
@ -1958,6 +1962,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let bool_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
let unbound_zero_percision_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
let body = If {
|
||||
|
@ -1971,7 +1976,10 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, Int(unbound_zero_var, 0)),
|
||||
(
|
||||
num_var,
|
||||
Int(unbound_zero_var, unbound_zero_percision_var, 0),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
|
@ -2025,6 +2033,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let zero_var = var_store.fresh();
|
||||
let zero_percision_var = var_store.fresh();
|
||||
let list_elem_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
|
@ -2039,7 +2048,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, Int(zero_var, 0)),
|
||||
(len_var, Int(zero_var, zero_percision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
|
@ -2061,7 +2070,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
// List.#getUnsafe list 0
|
||||
RunLowLevel {
|
||||
op: LowLevel::ListGetUnsafe,
|
||||
args: vec![(list_var, Var(Symbol::ARG_1)), (len_var, Int(zero_var, 0))],
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(len_var, Int(zero_var, zero_percision_var, 0)),
|
||||
],
|
||||
ret_var: list_elem_var,
|
||||
},
|
||||
],
|
||||
|
@ -2102,6 +2114,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let num_percision_var = var_store.fresh();
|
||||
let list_elem_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
|
@ -2116,7 +2129,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, Int(num_var, 0)),
|
||||
(len_var, Int(num_var, num_percision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
|
@ -2155,7 +2168,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
ret_var: len_var,
|
||||
},
|
||||
),
|
||||
(arg_var, Int(num_var, 1)),
|
||||
(arg_var, Int(num_var, num_percision_var, 1)),
|
||||
],
|
||||
ret_var: len_var,
|
||||
},
|
||||
|
|
|
@ -56,8 +56,8 @@ pub enum Expr {
|
|||
Num(Variable, i64),
|
||||
|
||||
// Int and Float store a variable to generate better error messages
|
||||
Int(Variable, i64),
|
||||
Float(Variable, f64),
|
||||
Int(Variable, Variable, i64),
|
||||
Float(Variable, Variable, f64),
|
||||
Str(InlinableString),
|
||||
List {
|
||||
list_var: Variable, // required for uniqueness of the list
|
||||
|
@ -1170,8 +1170,8 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||
// stored in Int and Float below, which is strictly for better error messages
|
||||
other @ Num(_, _)
|
||||
| other @ Int(_, _)
|
||||
| other @ Float(_, _)
|
||||
| other @ Int(_, _, _)
|
||||
| other @ Float(_, _, _)
|
||||
| other @ Str { .. }
|
||||
| other @ RuntimeError(_)
|
||||
| other @ EmptyRecord
|
||||
|
|
|
@ -414,8 +414,8 @@ fn fix_values_captured_in_closure_expr(
|
|||
}
|
||||
|
||||
Num(_, _)
|
||||
| Int(_, _)
|
||||
| Float(_, _)
|
||||
| Int(_, _, _)
|
||||
| Float(_, _, _)
|
||||
| Str(_)
|
||||
| Var(_)
|
||||
| EmptyRecord
|
||||
|
|
|
@ -45,7 +45,7 @@ pub fn int_expr_from_result(
|
|||
) -> Expr {
|
||||
// Int stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(int) => Expr::Int(var_store.fresh(), int),
|
||||
Ok(int) => Expr::Int(var_store.fresh(), var_store.fresh(), int),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidInt(error, base, region, raw.into());
|
||||
|
||||
|
@ -65,7 +65,7 @@ pub fn float_expr_from_result(
|
|||
) -> Expr {
|
||||
// Float stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(float) => Expr::Float(var_store.fresh(), float),
|
||||
Ok(float) => Expr::Float(var_store.fresh(), var_store.fresh(), float),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidFloat(error, region, raw.into());
|
||||
|
||||
|
|
|
@ -11,30 +11,50 @@ use roc_types::types::Reason;
|
|||
use roc_types::types::Type::{self, *};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
pub fn int_literal(
|
||||
num_var: Variable,
|
||||
percision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::IntLiteral;
|
||||
let expected_literal = ForReason(reason, num_int(Type::Variable(num_var)), region);
|
||||
|
||||
exists(
|
||||
vec![num_var],
|
||||
And(vec![
|
||||
Eq(num_type.clone(), expected_literal, Category::Int, region),
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_int(Type::Variable(percision_var)), region),
|
||||
Category::Int,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Int, region),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn float_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
pub fn float_literal(
|
||||
num_var: Variable,
|
||||
percision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::FloatLiteral;
|
||||
let expected_literal = ForReason(reason, num_float(Type::Variable(num_var)), region);
|
||||
|
||||
dbg!(&expected);
|
||||
|
||||
exists(
|
||||
vec![num_var],
|
||||
And(vec![
|
||||
Eq(num_type.clone(), expected_literal, Category::Float, region),
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(percision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Float, region),
|
||||
]),
|
||||
)
|
||||
|
|
|
@ -96,7 +96,7 @@ pub fn constrain_expr(
|
|||
expected: Expected<Type>,
|
||||
) -> Constraint {
|
||||
match expr {
|
||||
Int(var, _) => int_literal(*var, expected, region),
|
||||
Int(var, percision, _) => int_literal(*var, *percision, expected, region),
|
||||
Num(var, _) => exists(
|
||||
vec![*var],
|
||||
Eq(
|
||||
|
@ -106,7 +106,7 @@ pub fn constrain_expr(
|
|||
region,
|
||||
),
|
||||
),
|
||||
Float(var, _) => float_literal(*var, expected, region),
|
||||
Float(var, percision, _) => float_literal(*var, *percision, expected, region),
|
||||
EmptyRecord => constrain_empty_record(region, expected),
|
||||
Expr::Record { record_var, fields } => {
|
||||
if fields.is_empty() {
|
||||
|
|
|
@ -426,6 +426,7 @@ fn unique_int(
|
|||
let num_uvar1 = var_store.fresh();
|
||||
let num_uvar2 = var_store.fresh();
|
||||
let num_uvar3 = var_store.fresh();
|
||||
let num_uvar4 = var_store.fresh();
|
||||
|
||||
let inner_type = Type::Variable(inner_var);
|
||||
let attr_inner_type = attr_type(Bool::variable(num_uvar1), inner_type);
|
||||
|
@ -444,6 +445,7 @@ fn unique_float(
|
|||
let num_uvar1 = var_store.fresh();
|
||||
let num_uvar2 = var_store.fresh();
|
||||
let num_uvar3 = var_store.fresh();
|
||||
let num_uvar4 = var_store.fresh();
|
||||
|
||||
let inner_type = Type::Variable(inner_var);
|
||||
let attr_inner_type = attr_type(Bool::variable(num_uvar1), inner_type);
|
||||
|
@ -484,7 +486,7 @@ pub fn constrain_expr(
|
|||
]),
|
||||
)
|
||||
}
|
||||
Int(var, _) => {
|
||||
Int(var, _, _) => {
|
||||
let (a, b, c, num_type) = unique_int(*var, var_store);
|
||||
|
||||
exists(
|
||||
|
@ -500,7 +502,7 @@ pub fn constrain_expr(
|
|||
]),
|
||||
)
|
||||
}
|
||||
Float(var, _) => {
|
||||
Float(var, _, _) => {
|
||||
let (a, b, c, num_type) = unique_float(*var, var_store);
|
||||
|
||||
exists(
|
||||
|
|
|
@ -477,13 +477,27 @@ fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace {
|
|||
|
||||
pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'_>,
|
||||
literal: &roc_mono::ir::Literal<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use roc_mono::ir::Literal::*;
|
||||
|
||||
match literal {
|
||||
Int(num) => env.context.i64_type().const_int(*num as u64, true).into(),
|
||||
Float(num) => env.context.f64_type().const_float(*num).into(),
|
||||
Int(int) =>
|
||||
(match layout {
|
||||
Layout::Builtin(Builtin::Int128) => env.context.i128_type(), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */
|
||||
Layout::Builtin(Builtin::Int64) => env.context.i64_type(),
|
||||
Layout::Builtin(Builtin::Int32) => env.context.i32_type(),
|
||||
Layout::Builtin(Builtin::Int16) => env.context.i16_type(),
|
||||
Layout::Builtin(Builtin::Int8) => env.context.i8_type(),
|
||||
_ => panic!("Invalid layout for int literal = {:?}", layout),
|
||||
}).const_int(*int as u64, false).into(),
|
||||
Float(num) =>
|
||||
(match layout {
|
||||
Layout::Builtin(Builtin::Float64) => env.context.f64_type(),
|
||||
Layout::Builtin(Builtin::Float32) => env.context.f32_type(),
|
||||
_ => panic!("Invalid layout for float literal = {:?}", layout),
|
||||
}).const_float(*num).into(),
|
||||
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
||||
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
||||
Str(str_literal) => {
|
||||
|
@ -622,7 +636,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
use roc_mono::ir::Expr::*;
|
||||
|
||||
match expr {
|
||||
Literal(literal) => build_exp_literal(env, literal),
|
||||
Literal(literal) => build_exp_literal(env, layout, literal),
|
||||
RunLowLevel(op, symbols) => {
|
||||
run_low_level(env, layout_ids, scope, parent, layout, *op, symbols)
|
||||
}
|
||||
|
@ -1298,6 +1312,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
Let(first_symbol, first_expr, first_layout, mut cont) => {
|
||||
let mut queue = Vec::new_in(env.arena);
|
||||
|
||||
|
||||
queue.push((first_symbol, first_expr, first_layout));
|
||||
|
||||
while let Let(symbol, expr, layout, new_cont) = cont {
|
||||
|
@ -1691,6 +1706,15 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
|||
.build_bitcast(full_cond, env.context.i64_type(), "")
|
||||
.into_int_value()
|
||||
}
|
||||
Layout::Builtin(Builtin::Float32) => {
|
||||
// float matches are done on the bit pattern
|
||||
cond_layout = Layout::Builtin(Builtin::Int32);
|
||||
let full_cond = load_symbol(env, scope, cond_symbol);
|
||||
|
||||
builder
|
||||
.build_bitcast(full_cond, env.context.i32_type(), "")
|
||||
.into_int_value()
|
||||
}
|
||||
Layout::Union(_) => {
|
||||
// we match on the discriminant, not the whole Tag
|
||||
cond_layout = Layout::Builtin(Builtin::Int64);
|
||||
|
@ -3622,6 +3646,7 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
|||
int_abs_raise_on_overflow(env, arg, arg_layout)
|
||||
}
|
||||
NumToFloat => {
|
||||
// TODO: Handle differnt sized numbers
|
||||
// This is an Int, so we need to convert it.
|
||||
bd.build_cast(
|
||||
InstructionOpcode::SIToFP,
|
||||
|
@ -3748,6 +3773,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
|||
|
||||
let bd = env.builder;
|
||||
|
||||
// TODO: Handle differnt sized floats
|
||||
match op {
|
||||
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
|
||||
NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]),
|
||||
|
|
|
@ -15,6 +15,113 @@ mod helpers;
|
|||
mod gen_num {
|
||||
use roc_std::RocOrder;
|
||||
|
||||
#[test]
|
||||
fn i32_to_hex() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : I32
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
i32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i128_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : I128
|
||||
i = 15
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
15,
|
||||
i128
|
||||
);
|
||||
}
|
||||
// TODO: Use indoc
|
||||
#[test]
|
||||
fn i64_signed_int_alias() {
|
||||
assert_evals_to!("15 : I64", 15, i64);
|
||||
}
|
||||
#[test]
|
||||
fn i32_signed_int_alias() {
|
||||
assert_evals_to!("15 : I32", 15, i32);
|
||||
}
|
||||
#[test]
|
||||
fn i16_signed_int_alias() {
|
||||
assert_evals_to!("15 : I16", 15, i16);
|
||||
}
|
||||
#[test]
|
||||
fn i8_signed_int_alias() {
|
||||
assert_evals_to!("15 : I8", 15, i8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i8_signed_int_alias_overflow() {
|
||||
// TODO: How/where do we handle overflow
|
||||
assert_evals_to!("(127 + 1): I8", -128, i8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u128_unsigned_int_alias() {
|
||||
assert_evals_to!("15 : U128", 15, u128);
|
||||
}
|
||||
#[test]
|
||||
fn u64_unsigned_int_alias() {
|
||||
assert_evals_to!("15 : U64", 15, u64);
|
||||
}
|
||||
#[test]
|
||||
fn u32_unsigned_int_alias() {
|
||||
assert_evals_to!("15 : U32", 15, u32);
|
||||
}
|
||||
#[test]
|
||||
fn u16_unsigned_int_alias() {
|
||||
assert_evals_to!("15 : U16", 15, u16);
|
||||
}
|
||||
#[test]
|
||||
fn u8_unsigned_int_alias() {
|
||||
assert_evals_to!("15 : u8", 15, u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_float_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : F64
|
||||
f = 3.6
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
3.6,
|
||||
f64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn f32_float_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : F32
|
||||
f = 3.6
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
3.6,
|
||||
f32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_sqrt() {
|
||||
// FIXME this works with normal types, but fails when checking uniqueness types
|
||||
|
|
|
@ -451,7 +451,7 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
|
|||
IntLiteral(v) => {
|
||||
all_tests.push(guarded(IsInt(*v)));
|
||||
}
|
||||
FloatLiteral(v) => {
|
||||
FloatLiteral(_, v) => {
|
||||
all_tests.push(IsFloat(*v));
|
||||
}
|
||||
StrLiteral(v) => {
|
||||
|
@ -655,7 +655,7 @@ fn to_relevant_branch_help<'a>(
|
|||
_ => None,
|
||||
},
|
||||
|
||||
FloatLiteral(float) => match test {
|
||||
FloatLiteral(_, float) => match test {
|
||||
IsFloat(test_float) if float == *test_float => {
|
||||
start.extend(end);
|
||||
Some(Branch {
|
||||
|
@ -749,7 +749,7 @@ fn needs_tests<'a>(pattern: &Pattern<'a>) -> bool {
|
|||
| BitLiteral { .. }
|
||||
| EnumLiteral { .. }
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| FloatLiteral(_, _)
|
||||
| StrLiteral(_) => true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ fn simplify<'a>(pattern: &crate::ir::Pattern<'a>) -> Pattern {
|
|||
|
||||
match pattern {
|
||||
IntLiteral(v) => Literal(Literal::Int(*v)),
|
||||
FloatLiteral(v) => Literal(Literal::Float(*v)),
|
||||
FloatLiteral(_, v) => Literal(Literal::Float(*v)),
|
||||
StrLiteral(v) => Literal(Literal::Str(v.clone())),
|
||||
|
||||
// To make sure these are exhaustive, we have to "fake" a union here
|
||||
|
|
|
@ -14,6 +14,26 @@ use roc_types::subs::{Content, FlatType, Subs, Variable};
|
|||
use std::collections::HashMap;
|
||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||
|
||||
// Function return type does not match operand type of return inst!
|
||||
// ret i64 %f1, !dbg !521
|
||||
// i32define private fastcc i32 @"#UserApp_main_1"() !dbg !517 {
|
||||
// entry:
|
||||
// %f = alloca i64
|
||||
// store i64 291, i64* %f, !dbg !521
|
||||
// %f1 = load i64, i64* %f, !dbg !521
|
||||
// ret i64 %f1, !dbg !521
|
||||
// }
|
||||
|
||||
// Function return type does not match operand type of return inst!
|
||||
// ret i32 %f1, !dbg !521
|
||||
// i64define private fastcc i64 @"#UserApp_main_1"() !dbg !517 {
|
||||
// entry:
|
||||
// %f = alloca i32
|
||||
// store i32 291, i32* %f, !dbg !521
|
||||
// %f1 = load i32, i32* %f, !dbg !521
|
||||
// ret i32 %f1, !dbg !521
|
||||
// }
|
||||
|
||||
pub const PRETTY_PRINT_IR_SYMBOLS: bool = false;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -2245,19 +2265,37 @@ pub fn with_hole<'a>(
|
|||
let arena = env.arena;
|
||||
|
||||
match can_expr {
|
||||
Int(_, num) => Stmt::Let(
|
||||
Int(_, precision, num) => match num_argument_to_int_or_float(env.subs, precision) {
|
||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num)),
|
||||
Layout::Builtin(Builtin::Int64),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
|
||||
Float(_, num) => Stmt::Let(
|
||||
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num)),
|
||||
Layout::Builtin(Builtin::Float64),
|
||||
Expr::Literal(Literal::Int(num)),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
_ => unreachable!("unexpected float precision for integer"),
|
||||
},
|
||||
|
||||
Float(_, precision, num) => match num_argument_to_int_or_float(env.subs, precision) {
|
||||
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
_ => unreachable!("unexpected float precision for integer"),
|
||||
},
|
||||
|
||||
Str(string) => Stmt::Let(
|
||||
assigned,
|
||||
|
@ -2267,16 +2305,28 @@ pub fn with_hole<'a>(
|
|||
),
|
||||
|
||||
Num(var, num) => match num_argument_to_int_or_float(env.subs, var) {
|
||||
IntOrFloat::IntType => Stmt::Let(
|
||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num)),
|
||||
Layout::Builtin(Builtin::Int64),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::FloatType => Stmt::Let(
|
||||
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num)),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(Builtin::Float64),
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
},
|
||||
|
@ -4583,7 +4633,7 @@ fn store_pattern<'a>(
|
|||
// do nothing
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| FloatLiteral(_, _)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
|
@ -4623,7 +4673,7 @@ fn store_pattern<'a>(
|
|||
// ignore
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| FloatLiteral(_, _)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
|
@ -4737,7 +4787,7 @@ fn store_record_destruct<'a>(
|
|||
// internally. But `y` is never used, so we must make sure it't not stored/loaded.
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| FloatLiteral(_, _)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
|
@ -5408,7 +5458,7 @@ pub enum Pattern<'a> {
|
|||
Underscore,
|
||||
|
||||
IntLiteral(i64),
|
||||
FloatLiteral(u64),
|
||||
FloatLiteral(Variable, u64),
|
||||
BitLiteral {
|
||||
value: bool,
|
||||
tag_name: TagName,
|
||||
|
@ -5469,7 +5519,7 @@ pub fn from_can_pattern<'a>(
|
|||
Underscore => Pattern::Underscore,
|
||||
Identifier(symbol) => Pattern::Identifier(*symbol),
|
||||
IntLiteral(_, v) => Pattern::IntLiteral(*v),
|
||||
FloatLiteral(_, v) => Pattern::FloatLiteral(f64::to_bits(*v)),
|
||||
FloatLiteral(var, float) => Pattern::FloatLiteral(*var, f64::to_bits(*float)),
|
||||
StrLiteral(v) => Pattern::StrLiteral(v.clone()),
|
||||
Shadowed(region, ident) => Pattern::Shadowed(*region, ident.clone()),
|
||||
UnsupportedPattern(region) => Pattern::UnsupportedPattern(*region),
|
||||
|
@ -5478,8 +5528,10 @@ pub fn from_can_pattern<'a>(
|
|||
Pattern::UnsupportedPattern(*region)
|
||||
}
|
||||
NumLiteral(var, num) => match num_argument_to_int_or_float(env.subs, *var) {
|
||||
IntOrFloat::IntType => Pattern::IntLiteral(*num),
|
||||
IntOrFloat::FloatType => Pattern::FloatLiteral(*num as u64),
|
||||
IntOrFloat::SignedIntType(_) => Pattern::IntLiteral(*num),
|
||||
IntOrFloat::UnsignedIntType(_) => Pattern::IntLiteral(*num),
|
||||
IntOrFloat::BinaryFloatType(_) => Pattern::FloatLiteral(*var, *num as u64),
|
||||
IntOrFloat::DecimalFloatType(_) => Pattern::FloatLiteral(*var, *num as u64),
|
||||
},
|
||||
|
||||
AppliedTag {
|
||||
|
@ -5854,29 +5906,100 @@ fn optimize_low_level(
|
|||
}
|
||||
}
|
||||
|
||||
pub enum IntPrecision {
|
||||
I128,
|
||||
I64,
|
||||
I32,
|
||||
I16,
|
||||
I8,
|
||||
}
|
||||
|
||||
pub enum FloatPrecision {
|
||||
F64,
|
||||
F32,
|
||||
}
|
||||
|
||||
pub enum IntOrFloat {
|
||||
IntType,
|
||||
FloatType,
|
||||
SignedIntType(IntPrecision),
|
||||
UnsignedIntType(IntPrecision),
|
||||
BinaryFloatType(FloatPrecision),
|
||||
DecimalFloatType(FloatPrecision),
|
||||
}
|
||||
|
||||
fn float_precision_to_builtin(precision: FloatPrecision) -> Builtin<'static> {
|
||||
use FloatPrecision::*;
|
||||
match precision {
|
||||
F64 => Builtin::Float64,
|
||||
F32 => Builtin::Float32,
|
||||
}
|
||||
}
|
||||
|
||||
fn int_precision_to_builtin(precision: IntPrecision) -> Builtin<'static> {
|
||||
use IntPrecision::*;
|
||||
match precision {
|
||||
I128 => Builtin::Int128,
|
||||
I64 => Builtin::Int64,
|
||||
I32 => Builtin::Int32,
|
||||
I16 => Builtin::Int16,
|
||||
I8 => Builtin::Int8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the `a` in `Num a`, determines whether it's an int or a float
|
||||
pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
||||
match subs.get_without_compacting(var).content {
|
||||
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
|
||||
// TODO: we probably need to match on the type of the arg
|
||||
IntOrFloat::IntType
|
||||
Content::FlexVar(_) => IntOrFloat::SignedIntType(IntPrecision::I64), // We default (Num *) to I64
|
||||
Content::Alias(Symbol::NUM_I128, _, _)
|
||||
| Content::Alias(Symbol::NUM_SIGNED128, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_SIGNED128, _, _) => {
|
||||
IntOrFloat::SignedIntType(IntPrecision::I128)
|
||||
}
|
||||
Content::FlexVar(_) => {
|
||||
// If this was still a (Num *), assume compiling it to an Int
|
||||
IntOrFloat::IntType
|
||||
Content::Alias(Symbol::NUM_INTEGER, _, _) // We default Integer to I64
|
||||
| Content::Alias(Symbol::NUM_I64, _, _)
|
||||
| Content::Alias(Symbol::NUM_SIGNED64, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_SIGNED64, _, _) => {
|
||||
IntOrFloat::SignedIntType(IntPrecision::I64)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
|
||||
// TODO: we probably need to match on the type of the arg
|
||||
IntOrFloat::FloatType
|
||||
// TODO: Add ors for aall NUM_SIGNED, NUM_BINARY (maybe NUM_AT_*)
|
||||
Content::Alias(Symbol::NUM_I32, _, _)
|
||||
| Content::Alias(Symbol::NUM_SIGNED32, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_SIGNED32, _, _) => {
|
||||
IntOrFloat::SignedIntType(IntPrecision::I32)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_I16, _, _)
|
||||
| Content::Alias(Symbol::NUM_SIGNED16, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_SIGNED16, _, _) => {
|
||||
IntOrFloat::SignedIntType(IntPrecision::I16)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_I8, _, _)
|
||||
| Content::Alias(Symbol::NUM_SIGNED8, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_SIGNED8, _, _) => {
|
||||
IntOrFloat::SignedIntType(IntPrecision::I8)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_U128, _, _)
|
||||
| Content::Alias(Symbol::NUM_UNSIGNED128, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_UNSIGNED128, _, _) => {
|
||||
IntOrFloat::UnsignedIntType(IntPrecision::I128)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_U64, _, _)
|
||||
| Content::Alias(Symbol::NUM_UNSIGNED64, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_UNSIGNED64, _, _) => {
|
||||
IntOrFloat::UnsignedIntType(IntPrecision::I64)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_U32, _, _)
|
||||
| Content::Alias(Symbol::NUM_UNSIGNED32, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_UNSIGNED32, _, _) => {
|
||||
IntOrFloat::UnsignedIntType(IntPrecision::I32)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_U16, _, _)
|
||||
| Content::Alias(Symbol::NUM_UNSIGNED16, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_UNSIGNED16, _, _) => {
|
||||
IntOrFloat::UnsignedIntType(IntPrecision::I16)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_U8, _, _)
|
||||
| Content::Alias(Symbol::NUM_UNSIGNED8, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_UNSIGNED8, _, _) => {
|
||||
IntOrFloat::UnsignedIntType(IntPrecision::I8)
|
||||
}
|
||||
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => {
|
||||
debug_assert!(attr_args.len() == 2);
|
||||
|
@ -5884,10 +6007,16 @@ pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
|||
// Recurse on the second argument
|
||||
num_argument_to_int_or_float(subs, attr_args[1])
|
||||
}
|
||||
Content::Alias(Symbol::NUM_F64, args, _) | Content::Alias(Symbol::NUM_F32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
|
||||
IntOrFloat::FloatType
|
||||
Content::Alias(Symbol::NUM_FLOATINGPOINT, _, _) // We default FloatingPoint to F64
|
||||
| Content::Alias(Symbol::NUM_F64, _, _)
|
||||
| Content::Alias(Symbol::NUM_BINARY64, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_BINARY64, _, _) => {
|
||||
IntOrFloat::BinaryFloatType(FloatPrecision::F64)
|
||||
}
|
||||
Content::Alias(Symbol::NUM_F32, _, _)
|
||||
| Content::Alias(Symbol::NUM_BINARY32, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_BINARY32, _, _) => {
|
||||
IntOrFloat::BinaryFloatType(FloatPrecision::F32)
|
||||
}
|
||||
other => {
|
||||
panic!(
|
||||
|
|
|
@ -362,14 +362,60 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
Structure(flat_type) => layout_from_flat_type(env, flat_type),
|
||||
|
||||
Alias(Symbol::NUM_INT, args, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
// Ints
|
||||
Alias(Symbol::NUM_I128, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Alias(Symbol::NUM_I64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Alias(Symbol::NUM_FLOAT, args, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
Alias(Symbol::NUM_I32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int32))
|
||||
}
|
||||
Alias(Symbol::NUM_I16, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int16))
|
||||
}
|
||||
Alias(Symbol::NUM_I8, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int8))
|
||||
}
|
||||
|
||||
// I think unsigned and signed use the same layout
|
||||
Alias(Symbol::NUM_U128, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Alias(Symbol::NUM_U64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Alias(Symbol::NUM_U32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int32))
|
||||
}
|
||||
Alias(Symbol::NUM_U16, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int16))
|
||||
}
|
||||
Alias(Symbol::NUM_U8, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int8))
|
||||
}
|
||||
|
||||
// Floats
|
||||
Alias(Symbol::NUM_F64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
}
|
||||
Alias(Symbol::NUM_F32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Float32))
|
||||
}
|
||||
|
||||
Alias(_, _, var) => Self::from_var(env, var),
|
||||
Error => Err(LayoutProblem::Erroneous),
|
||||
}
|
||||
|
@ -763,14 +809,60 @@ fn layout_from_flat_type<'a>(
|
|||
match flat_type {
|
||||
Apply(symbol, args) => {
|
||||
match symbol {
|
||||
Symbol::NUM_INT => {
|
||||
// Ints
|
||||
Symbol::NUM_I128 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Symbol::NUM_I64 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_FLOAT => {
|
||||
Symbol::NUM_I32 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_I16 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_I8 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
|
||||
// I think unsigned and signed use the same layout
|
||||
Symbol::NUM_U128 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Symbol::NUM_U64 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_U32 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_U16 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_U8 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
|
||||
// Floats
|
||||
Symbol::NUM_F64 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
}
|
||||
Symbol::NUM_F32 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Float32))
|
||||
}
|
||||
|
||||
Symbol::NUM_NUM | Symbol::NUM_AT_NUM => {
|
||||
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
@ -780,6 +872,7 @@ fn layout_from_flat_type<'a>(
|
|||
|
||||
layout_from_num_content(content)
|
||||
}
|
||||
|
||||
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
|
||||
Symbol::LIST_LIST => list_layout_from_elem(env, args[0]),
|
||||
Symbol::ATTR_ATTR => {
|
||||
|
@ -1263,8 +1356,26 @@ fn layout_from_num_content<'a>(content: Content) -> Result<Layout<'a>, LayoutPro
|
|||
Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN))
|
||||
}
|
||||
Structure(Apply(symbol, args)) => match symbol {
|
||||
// Ints
|
||||
Symbol::NUM_INTEGER => Ok(Layout::Builtin(Builtin::Int64)),
|
||||
Symbol::NUM_I128 => Ok(Layout::Builtin(Builtin::Int128)),
|
||||
Symbol::NUM_I64 => Ok(Layout::Builtin(Builtin::Int64)),
|
||||
Symbol::NUM_I32 => Ok(Layout::Builtin(Builtin::Int32)),
|
||||
Symbol::NUM_I16 => Ok(Layout::Builtin(Builtin::Int16)),
|
||||
Symbol::NUM_I8 => Ok(Layout::Builtin(Builtin::Int8)),
|
||||
|
||||
// I think unsigned and signed use the same layout
|
||||
Symbol::NUM_U128 => Ok(Layout::Builtin(Builtin::Int128)),
|
||||
Symbol::NUM_U64 => Ok(Layout::Builtin(Builtin::Int64)),
|
||||
Symbol::NUM_U32 => Ok(Layout::Builtin(Builtin::Int32)),
|
||||
Symbol::NUM_U16 => Ok(Layout::Builtin(Builtin::Int16)),
|
||||
Symbol::NUM_U8 => Ok(Layout::Builtin(Builtin::Int8)),
|
||||
|
||||
// Floats
|
||||
Symbol::NUM_FLOATINGPOINT => Ok(Layout::Builtin(Builtin::Float64)),
|
||||
Symbol::NUM_F64 => Ok(Layout::Builtin(Builtin::Float64)),
|
||||
Symbol::NUM_F32 => Ok(Layout::Builtin(Builtin::Float32)),
|
||||
|
||||
_ => {
|
||||
panic!(
|
||||
"Invalid Num.Num type application: {:?}",
|
||||
|
|
|
@ -785,8 +785,8 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
|||
match expr {
|
||||
RuntimeError(_)
|
||||
| Num(_, _)
|
||||
| Int(_, _)
|
||||
| Float(_, _)
|
||||
| Int(_, _, _)
|
||||
| Float(_, _, _)
|
||||
| Str { .. }
|
||||
| EmptyRecord
|
||||
| Accessor { .. }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue