mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge remote-tracking branch 'origin/trunk' into array-wrappers
This commit is contained in:
commit
8d4331f903
41 changed files with 2057 additions and 723 deletions
|
@ -674,7 +674,12 @@ fn pattern_to_vars_by_symbol(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IntLiteral(_) | FloatLiteral(_) | StrLiteral(_) | Underscore | UnsupportedPattern(_) => {}
|
NumLiteral(_, _)
|
||||||
|
| IntLiteral(_)
|
||||||
|
| FloatLiteral(_)
|
||||||
|
| StrLiteral(_)
|
||||||
|
| Underscore
|
||||||
|
| UnsupportedPattern(_) => {}
|
||||||
|
|
||||||
Shadowed(_, _) => {}
|
Shadowed(_, _) => {}
|
||||||
}
|
}
|
||||||
|
@ -1321,7 +1326,7 @@ fn to_pending_def<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(_err) => panic!("TODO gracefully handle shadowing of type alias"),
|
Err(err) => panic!("TODO gracefully handle shadowing of type alias {:?}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::def::{can_defs_with_return, Def};
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::num::{
|
use crate::num::{
|
||||||
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
|
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
|
||||||
int_expr_from_result,
|
int_expr_from_result, num_expr_from_result,
|
||||||
};
|
};
|
||||||
use crate::pattern::{canonicalize_pattern, Pattern};
|
use crate::pattern::{canonicalize_pattern, Pattern};
|
||||||
use crate::procedure::References;
|
use crate::procedure::References;
|
||||||
|
@ -33,6 +33,12 @@ pub struct Output {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
// Literals
|
// Literals
|
||||||
|
|
||||||
|
// 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
|
||||||
|
Num(Variable, i64),
|
||||||
|
|
||||||
|
// Int and Float store a variable to generate better error messages
|
||||||
Int(Variable, i64),
|
Int(Variable, i64),
|
||||||
Float(Variable, f64),
|
Float(Variable, f64),
|
||||||
Str(Box<str>),
|
Str(Box<str>),
|
||||||
|
@ -144,8 +150,8 @@ pub fn canonicalize_expr<'a>(
|
||||||
use Expr::*;
|
use Expr::*;
|
||||||
|
|
||||||
let (expr, output) = match expr {
|
let (expr, output) = match expr {
|
||||||
ast::Expr::Int(string) => {
|
ast::Expr::Num(string) => {
|
||||||
let answer = int_expr_from_result(var_store, finish_parsing_int(*string), env);
|
let answer = num_expr_from_result(var_store, finish_parsing_int(*string), env);
|
||||||
|
|
||||||
(answer, Output::default())
|
(answer, Output::default())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,34 @@ use roc_problem::can::RuntimeError::*;
|
||||||
use roc_types::subs::VarStore;
|
use roc_types::subs::VarStore;
|
||||||
use std::i64;
|
use std::i64;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn num_expr_from_result(
|
||||||
|
var_store: &VarStore,
|
||||||
|
result: Result<i64, &str>,
|
||||||
|
env: &mut Env,
|
||||||
|
) -> Expr {
|
||||||
|
match result {
|
||||||
|
Ok(int) => Expr::Num(var_store.fresh(), int),
|
||||||
|
Err(raw) => {
|
||||||
|
// (Num *) compiles to Int if it doesn't
|
||||||
|
// get specialized to something else first,
|
||||||
|
// so use int's overflow bounds here.
|
||||||
|
let runtime_error = IntOutsideRange(raw.into());
|
||||||
|
|
||||||
|
env.problem(Problem::RuntimeError(runtime_error.clone()));
|
||||||
|
|
||||||
|
Expr::RuntimeError(runtime_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn int_expr_from_result(
|
pub fn int_expr_from_result(
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
result: Result<i64, &str>,
|
result: Result<i64, &str>,
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
|
// Int stores a variable to generate better error messages
|
||||||
match result {
|
match result {
|
||||||
Ok(int) => Expr::Int(var_store.fresh(), int),
|
Ok(int) => Expr::Int(var_store.fresh(), int),
|
||||||
Err(raw) => {
|
Err(raw) => {
|
||||||
|
@ -30,6 +52,7 @@ pub fn float_expr_from_result(
|
||||||
result: Result<f64, &str>,
|
result: Result<f64, &str>,
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
|
// Float stores a variable to generate better error messages
|
||||||
match result {
|
match result {
|
||||||
Ok(float) => Expr::Float(var_store.fresh(), float),
|
Ok(float) => Expr::Float(var_store.fresh(), float),
|
||||||
Err(raw) => {
|
Err(raw) => {
|
||||||
|
|
|
@ -62,8 +62,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
||||||
match &loc_expr.value {
|
match &loc_expr.value {
|
||||||
Float(_)
|
Float(_)
|
||||||
| Nested(Float(_))
|
| Nested(Float(_))
|
||||||
| Int(_)
|
| Num(_)
|
||||||
| Nested(Int(_))
|
| Nested(Num(_))
|
||||||
| NonBase10Int { .. }
|
| NonBase10Int { .. }
|
||||||
| Nested(NonBase10Int { .. })
|
| Nested(NonBase10Int { .. })
|
||||||
| Str(_)
|
| Str(_)
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub enum Pattern {
|
||||||
Identifier(Symbol),
|
Identifier(Symbol),
|
||||||
AppliedTag(Variable, TagName, Vec<(Variable, Located<Pattern>)>),
|
AppliedTag(Variable, TagName, Vec<(Variable, Located<Pattern>)>),
|
||||||
IntLiteral(i64),
|
IntLiteral(i64),
|
||||||
|
NumLiteral(Variable, i64),
|
||||||
FloatLiteral(f64),
|
FloatLiteral(f64),
|
||||||
StrLiteral(Box<str>),
|
StrLiteral(Box<str>),
|
||||||
RecordDestructure(Variable, Vec<Located<RecordDestruct>>),
|
RecordDestructure(Variable, Vec<Located<RecordDestruct>>),
|
||||||
|
@ -61,7 +62,12 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IntLiteral(_) | FloatLiteral(_) | StrLiteral(_) | Underscore | UnsupportedPattern(_) => {}
|
NumLiteral(_, _)
|
||||||
|
| IntLiteral(_)
|
||||||
|
| FloatLiteral(_)
|
||||||
|
| StrLiteral(_)
|
||||||
|
| Underscore
|
||||||
|
| UnsupportedPattern(_) => {}
|
||||||
|
|
||||||
Shadowed(_, _) => {}
|
Shadowed(_, _) => {}
|
||||||
}
|
}
|
||||||
|
@ -155,12 +161,12 @@ pub fn canonicalize_pattern<'a>(
|
||||||
ptype @ DefExpr | ptype @ TopLevelDef => unsupported_pattern(env, ptype, region),
|
ptype @ DefExpr | ptype @ TopLevelDef => unsupported_pattern(env, ptype, region),
|
||||||
},
|
},
|
||||||
|
|
||||||
IntLiteral(string) => match pattern_type {
|
NumLiteral(string) => match pattern_type {
|
||||||
WhenBranch => {
|
WhenBranch => {
|
||||||
let int = finish_parsing_int(string)
|
let int = finish_parsing_int(string)
|
||||||
.unwrap_or_else(|_| panic!("TODO handle malformed int pattern"));
|
.unwrap_or_else(|_| panic!("TODO handle malformed int pattern"));
|
||||||
|
|
||||||
Pattern::IntLiteral(int)
|
Pattern::NumLiteral(var_store.fresh(), int)
|
||||||
}
|
}
|
||||||
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
||||||
unsupported_pattern(env, ptype, region)
|
unsupported_pattern(env, ptype, region)
|
||||||
|
@ -353,7 +359,8 @@ fn add_bindings_from_patterns(
|
||||||
answer.push((*symbol, *region));
|
answer.push((*symbol, *region));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IntLiteral(_)
|
NumLiteral(_, _)
|
||||||
|
| IntLiteral(_)
|
||||||
| FloatLiteral(_)
|
| FloatLiteral(_)
|
||||||
| StrLiteral(_)
|
| StrLiteral(_)
|
||||||
| Underscore
|
| Underscore
|
||||||
|
|
|
@ -40,6 +40,7 @@ mod test_canonicalize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_can_int(input: &str, expected: i64) {
|
fn assert_can_int(input: &str, expected: i64) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||||
|
@ -54,6 +55,20 @@ mod test_canonicalize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_can_num(input: &str, expected: i64) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||||
|
|
||||||
|
match actual_out.loc_expr.value {
|
||||||
|
Expr::Num(_, actual) => {
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
actual => {
|
||||||
|
panic!("Expected a Num, but got: {:?}", actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NUMBER LITERALS
|
// NUMBER LITERALS
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -98,12 +113,12 @@ mod test_canonicalize {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zero() {
|
fn zero() {
|
||||||
assert_can_int("0", 0);
|
assert_can_num("0", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn minus_zero() {
|
fn minus_zero() {
|
||||||
assert_can_int("-0", 0);
|
assert_can_num("-0", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -10,7 +10,7 @@ use roc_can::expr::Expr::{self, *};
|
||||||
use roc_can::expr::Field;
|
use roc_can::expr::Field;
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_collections::all::{ImMap, SendMap};
|
use roc_collections::all::{ImMap, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
@ -80,6 +80,14 @@ pub fn constrain_expr(
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
match expr {
|
match expr {
|
||||||
Int(var, _) => int_literal(*var, expected, region),
|
Int(var, _) => int_literal(*var, expected, region),
|
||||||
|
Num(var, _) => exists(
|
||||||
|
vec![*var],
|
||||||
|
Eq(
|
||||||
|
Type::Apply(Symbol::NUM_NUM, vec![Type::Variable(*var)]),
|
||||||
|
expected,
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
),
|
||||||
Float(var, _) => float_literal(*var, expected, region),
|
Float(var, _) => float_literal(*var, expected, region),
|
||||||
EmptyRecord => constrain_empty_record(region, expected),
|
EmptyRecord => constrain_empty_record(region, expected),
|
||||||
Expr::Record(stored_var, fields) => {
|
Expr::Record(stored_var, fields) => {
|
||||||
|
@ -318,14 +326,7 @@ pub fn constrain_expr(
|
||||||
branches,
|
branches,
|
||||||
final_else,
|
final_else,
|
||||||
} => {
|
} => {
|
||||||
// TODO use Bool alias here, so we don't allocate this type every time
|
let bool_type = Type::Variable(Variable::BOOL);
|
||||||
let bool_type = Type::TagUnion(
|
|
||||||
vec![
|
|
||||||
(TagName::Global("True".into()), vec![]),
|
|
||||||
(TagName::Global("False".into()), vec![]),
|
|
||||||
],
|
|
||||||
Box::new(Type::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
let expect_bool = Expected::ForReason(Reason::IfCondition, bool_type, region);
|
let expect_bool = Expected::ForReason(Reason::IfCondition, bool_type, region);
|
||||||
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ fn headers_from_annotation_help(
|
||||||
Underscore
|
Underscore
|
||||||
| Shadowed(_, _)
|
| Shadowed(_, _)
|
||||||
| UnsupportedPattern(_)
|
| UnsupportedPattern(_)
|
||||||
|
| NumLiteral(_, _)
|
||||||
| IntLiteral(_)
|
| IntLiteral(_)
|
||||||
| FloatLiteral(_)
|
| FloatLiteral(_)
|
||||||
| StrLiteral(_) => true,
|
| StrLiteral(_) => true,
|
||||||
|
@ -124,10 +125,22 @@ pub fn constrain_pattern(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NumLiteral(var, _) => {
|
||||||
|
state.vars.push(*var);
|
||||||
|
|
||||||
|
state.constraints.push(Constraint::Pattern(
|
||||||
|
region,
|
||||||
|
PatternCategory::Num,
|
||||||
|
builtins::builtin_type(Symbol::NUM_NUM, vec![Type::Variable(*var)]),
|
||||||
|
expected,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
IntLiteral(_) => {
|
IntLiteral(_) => {
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(Constraint::Pattern(
|
||||||
region,
|
region,
|
||||||
PatternCategory::Int,
|
PatternCategory::Float,
|
||||||
builtins::builtin_type(Symbol::INT_INT, vec![]),
|
builtins::builtin_type(Symbol::INT_INT, vec![]),
|
||||||
expected,
|
expected,
|
||||||
));
|
));
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_can::expr::{Expr, Field};
|
use roc_can::expr::{Expr, Field};
|
||||||
use roc_can::pattern::{Pattern, RecordDestruct};
|
use roc_can::pattern::{Pattern, RecordDestruct};
|
||||||
use roc_collections::all::{ImMap, ImSet, SendMap};
|
use roc_collections::all::{ImMap, ImSet, SendMap};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase};
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::boolean_algebra::{Atom, Bool};
|
use roc_types::boolean_algebra::{Atom, Bool};
|
||||||
|
@ -154,6 +154,14 @@ fn constrain_pattern(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NumLiteral(inner_var, _) => {
|
||||||
|
let (num_uvar, val_uvar, num_type, num_var) = unique_unbound_num(*inner_var, var_store);
|
||||||
|
state.constraints.push(exists(
|
||||||
|
vec![val_uvar, num_uvar, num_var, *inner_var],
|
||||||
|
Constraint::Pattern(pattern.region, PatternCategory::Num, num_type, expected),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
IntLiteral(_) => {
|
IntLiteral(_) => {
|
||||||
let (num_uvar, int_uvar, num_type) = unique_int(var_store);
|
let (num_uvar, int_uvar, num_type) = unique_int(var_store);
|
||||||
state.constraints.push(exists(
|
state.constraints.push(exists(
|
||||||
|
@ -306,6 +314,23 @@ fn constrain_pattern(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unique_unbound_num(
|
||||||
|
inner_var: Variable,
|
||||||
|
var_store: &VarStore,
|
||||||
|
) -> (Variable, Variable, Type, Variable) {
|
||||||
|
let num_var = var_store.fresh();
|
||||||
|
let num_uvar = var_store.fresh();
|
||||||
|
let val_uvar = var_store.fresh();
|
||||||
|
|
||||||
|
let val_type = Type::Variable(inner_var);
|
||||||
|
let val_utype = attr_type(Bool::variable(val_uvar), val_type);
|
||||||
|
|
||||||
|
let num_utype = Type::Apply(Symbol::NUM_NUM, vec![val_utype]);
|
||||||
|
let num_type = attr_type(Bool::variable(num_uvar), num_utype);
|
||||||
|
|
||||||
|
(num_uvar, val_uvar, num_type, num_var)
|
||||||
|
}
|
||||||
|
|
||||||
fn unique_num(var_store: &VarStore, symbol: Symbol) -> (Variable, Variable, Type) {
|
fn unique_num(var_store: &VarStore, symbol: Symbol) -> (Variable, Variable, Type) {
|
||||||
let num_uvar = var_store.fresh();
|
let num_uvar = var_store.fresh();
|
||||||
let val_uvar = var_store.fresh();
|
let val_uvar = var_store.fresh();
|
||||||
|
@ -339,6 +364,22 @@ pub fn constrain_expr(
|
||||||
pub use roc_can::expr::Expr::*;
|
pub use roc_can::expr::Expr::*;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
|
Num(inner_var, _) => {
|
||||||
|
let var = var_store.fresh();
|
||||||
|
let (num_uvar, val_uvar, num_type, num_var) = unique_unbound_num(*inner_var, var_store);
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vec![var, *inner_var, val_uvar, num_uvar, num_var],
|
||||||
|
And(vec![
|
||||||
|
Eq(
|
||||||
|
Type::Variable(var),
|
||||||
|
Expected::ForReason(Reason::NumLiteral, num_type, region),
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
Eq(Type::Variable(var), expected, region),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
Int(var, _) => {
|
Int(var, _) => {
|
||||||
let (num_uvar, int_uvar, num_type) = unique_int(var_store);
|
let (num_uvar, int_uvar, num_type) = unique_int(var_store);
|
||||||
|
|
||||||
|
@ -761,14 +802,7 @@ pub fn constrain_expr(
|
||||||
final_else,
|
final_else,
|
||||||
} => {
|
} => {
|
||||||
// TODO use Bool alias here, so we don't allocate this type every time
|
// TODO use Bool alias here, so we don't allocate this type every time
|
||||||
let bool_type = Type::TagUnion(
|
let bool_type = Type::Variable(Variable::BOOL);
|
||||||
vec![
|
|
||||||
(TagName::Global("True".into()), vec![]),
|
|
||||||
(TagName::Global("False".into()), vec![]),
|
|
||||||
],
|
|
||||||
Box::new(Type::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
||||||
let mut cond_uniq_vars = Vec::with_capacity(branches.len() + 2);
|
let mut cond_uniq_vars = Vec::with_capacity(branches.len() + 2);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ pub fn fmt_expr<'a>(
|
||||||
}
|
}
|
||||||
buf.push_str("\"\"\"");
|
buf.push_str("\"\"\"");
|
||||||
}
|
}
|
||||||
Int(string) | Float(string) | GlobalTag(string) | PrivateTag(string) => {
|
Num(string) | Float(string) | GlobalTag(string) | PrivateTag(string) => {
|
||||||
buf.push_str(string)
|
buf.push_str(string)
|
||||||
}
|
}
|
||||||
NonBase10Int {
|
NonBase10Int {
|
||||||
|
@ -432,7 +432,7 @@ pub fn is_multiline_pattern<'a>(pattern: &'a Pattern<'a>) -> bool {
|
||||||
| Pattern::Apply(_, _)
|
| Pattern::Apply(_, _)
|
||||||
| Pattern::RecordDestructure(_)
|
| Pattern::RecordDestructure(_)
|
||||||
| Pattern::RecordField(_, _)
|
| Pattern::RecordField(_, _)
|
||||||
| Pattern::IntLiteral(_)
|
| Pattern::NumLiteral(_)
|
||||||
| Pattern::NonBase10Literal { .. }
|
| Pattern::NonBase10Literal { .. }
|
||||||
| Pattern::FloatLiteral(_)
|
| Pattern::FloatLiteral(_)
|
||||||
| Pattern::StrLiteral(_)
|
| Pattern::StrLiteral(_)
|
||||||
|
@ -456,7 +456,7 @@ pub fn is_multiline_expr<'a>(expr: &'a Expr<'a>) -> bool {
|
||||||
|
|
||||||
// These expressions never have newlines
|
// These expressions never have newlines
|
||||||
Float(_)
|
Float(_)
|
||||||
| Int(_)
|
| Num(_)
|
||||||
| NonBase10Int { .. }
|
| NonBase10Int { .. }
|
||||||
| Str(_)
|
| Str(_)
|
||||||
| Access(_, _)
|
| Access(_, _)
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub fn fmt_pattern<'a>(
|
||||||
fmt_pattern(buf, &loc_pattern.value, indent, true, only_comments);
|
fmt_pattern(buf, &loc_pattern.value, indent, true, only_comments);
|
||||||
}
|
}
|
||||||
|
|
||||||
IntLiteral(string) => buf.push_str(string),
|
NumLiteral(string) => buf.push_str(string),
|
||||||
NonBase10Literal {
|
NonBase10Literal {
|
||||||
base,
|
base,
|
||||||
string,
|
string,
|
||||||
|
|
|
@ -120,9 +120,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
let fn_id = match scope.get(name) {
|
let fn_id = match scope.get(name) {
|
||||||
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
|
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
|
||||||
other => panic!(
|
other => panic!(
|
||||||
"FunctionPointer could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
|
"FunctionPointer could not find function named {:?} declared in scope (and it was not special-cased in crane::build as a builtin); instead, found {:?} in scope {:?}", name, other, scope),
|
||||||
name, other, scope
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let func_ref = module.declare_func_in_func(fn_id, &mut builder.func);
|
let func_ref = module.declare_func_in_func(fn_id, &mut builder.func);
|
||||||
|
@ -161,7 +159,10 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
Some(ScopeEntry::Func { .. }) => {
|
Some(ScopeEntry::Func { .. }) => {
|
||||||
panic!("TODO I don't yet know how to return fn pointers")
|
panic!("TODO I don't yet know how to return fn pointers")
|
||||||
}
|
}
|
||||||
None => panic!("Could not find a var for {:?} in scope {:?}", name, scope),
|
None => panic!(
|
||||||
|
"Could not resolve lookup for {:?} because no ScopeEntry was found for {:?} in scope {:?}",
|
||||||
|
name, name, scope
|
||||||
|
),
|
||||||
},
|
},
|
||||||
Struct { layout, fields } => {
|
Struct { layout, fields } => {
|
||||||
let cfg = env.cfg;
|
let cfg = env.cfg;
|
||||||
|
@ -194,16 +195,59 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
builder.ins().stack_store(val, slot, Offset32::new(offset));
|
builder.ins().stack_store(val, slot, Offset32::new(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ir_type = type_from_layout(cfg, layout);
|
builder
|
||||||
builder.ins().stack_addr(ir_type, slot, Offset32::new(0))
|
.ins()
|
||||||
|
.stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
|
||||||
|
}
|
||||||
|
Access {
|
||||||
|
label,
|
||||||
|
field_layout,
|
||||||
|
struct_layout: Layout::Struct(fields),
|
||||||
|
record,
|
||||||
|
} => {
|
||||||
|
let cfg = env.cfg;
|
||||||
|
|
||||||
|
// Reconstruct the struct to determine the combined layout
|
||||||
|
// TODO get rid of clones
|
||||||
|
let mut reconstructed_struct_layout =
|
||||||
|
Vec::with_capacity_in(fields.len() + 1, env.arena);
|
||||||
|
for field in fields.iter() {
|
||||||
|
reconstructed_struct_layout.push(field.clone());
|
||||||
|
}
|
||||||
|
reconstructed_struct_layout.push((label.clone(), field_layout.clone()));
|
||||||
|
reconstructed_struct_layout.sort_by(|a, b| {
|
||||||
|
a.0.partial_cmp(&b.0)
|
||||||
|
.expect("TODO: failed to sort struct fields in crane access")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find the offset we are trying to access
|
||||||
|
let mut offset = 0;
|
||||||
|
for (local_label, layout) in reconstructed_struct_layout.iter() {
|
||||||
|
if local_label == label {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let field_size = match layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => std::mem::size_of::<i64>(),
|
||||||
|
_ => panic!(
|
||||||
|
"Missing struct field size in offset calculation for struct access for {:?}",
|
||||||
|
layout
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
offset += field_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = i32::try_from(offset)
|
||||||
|
.expect("TODO gracefully handle usize -> i32 conversion in struct access");
|
||||||
|
|
||||||
|
let mem_flags = MemFlags::new();
|
||||||
|
let record = build_expr(env, scope, module, builder, record, procs);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.load(cfg.pointer_type(), mem_flags, record, Offset32::new(offset))
|
||||||
}
|
}
|
||||||
// Access {
|
|
||||||
// label,
|
|
||||||
// field_layout,
|
|
||||||
// struct_layout,
|
|
||||||
// } => {
|
|
||||||
// panic!("I don't yet know how to crane build {:?}", expr);
|
|
||||||
// }
|
|
||||||
Str(str_literal) => {
|
Str(str_literal) => {
|
||||||
if str_literal.is_empty() {
|
if str_literal.is_empty() {
|
||||||
panic!("TODO build an empty string in Crane");
|
panic!("TODO build an empty string in Crane");
|
||||||
|
@ -572,20 +616,34 @@ fn call_by_name<'a, B: Backend>(
|
||||||
procs: &Procs<'a>,
|
procs: &Procs<'a>,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_ADD => {
|
Symbol::INT_ADD | Symbol::NUM_ADD => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
builder.ins().iadd(a, b)
|
builder.ins().iadd(a, b)
|
||||||
}
|
}
|
||||||
Symbol::NUM_SUB => {
|
Symbol::FLOAT_ADD => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
|
builder.ins().fadd(a, b)
|
||||||
|
}
|
||||||
|
Symbol::INT_SUB | Symbol::NUM_SUB => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
builder.ins().isub(a, b)
|
builder.ins().isub(a, b)
|
||||||
}
|
}
|
||||||
|
Symbol::FLOAT_SUB => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
|
builder.ins().fsub(a, b)
|
||||||
|
}
|
||||||
Symbol::NUM_MUL => {
|
Symbol::NUM_MUL => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
|
|
@ -10,23 +10,12 @@ pub fn type_from_layout(cfg: TargetFrontendConfig, layout: &Layout<'_>) -> Type
|
||||||
use roc_mono::layout::Layout::*;
|
use roc_mono::layout::Layout::*;
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
Pointer(_) | FunctionPointer(_, _) => cfg.pointer_type(),
|
Pointer(_) | FunctionPointer(_, _) | Struct(_) => cfg.pointer_type(),
|
||||||
Struct(fields) => {
|
|
||||||
// This will change as we add more fields and field types to the tests
|
|
||||||
let naive_all_ints = fields.iter().all(|ref field| match field.1 {
|
|
||||||
Builtin(Int64) => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if naive_all_ints && fields.len() == 3 {
|
|
||||||
types::I64.by(4).unwrap()
|
|
||||||
} else {
|
|
||||||
panic!("TODO layout_to_crane_type for Struct");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
Int64 => types::I64,
|
Int64 => types::I64,
|
||||||
Float64 => types::F64,
|
Float64 => types::F64,
|
||||||
|
Bool(_, _) => types::B1,
|
||||||
|
Byte(_) => types::I8,
|
||||||
Str | EmptyStr | Map(_, _) | EmptyMap | Set(_) | EmptySet | List(_) | EmptyList => {
|
Str | EmptyStr | Map(_, _) | EmptyMap | Set(_) | EmptySet | List(_) | EmptyList => {
|
||||||
cfg.pointer_type()
|
cfg.pointer_type()
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,6 +284,80 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Struct { fields, .. } => {
|
||||||
|
let ctx = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Sort the fields
|
||||||
|
let mut sorted_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
|
for field in fields.iter() {
|
||||||
|
sorted_fields.push(field);
|
||||||
|
}
|
||||||
|
sorted_fields.sort_by_key(|k| &k.0);
|
||||||
|
|
||||||
|
// Determine types
|
||||||
|
let mut field_types = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
|
let mut field_vals = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
|
|
||||||
|
for (_, ref inner_expr) in sorted_fields.iter() {
|
||||||
|
let val = build_expr(env, &scope, parent, inner_expr, procs);
|
||||||
|
|
||||||
|
let field_type = match inner_expr {
|
||||||
|
Int(_) => BasicTypeEnum::IntType(ctx.i64_type()),
|
||||||
|
_ => panic!("I don't yet know how to get Inkwell type for {:?}", val),
|
||||||
|
};
|
||||||
|
|
||||||
|
field_types.push(field_type);
|
||||||
|
field_vals.push(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the struct_type
|
||||||
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
let mut struct_val = struct_type.const_zero().into();
|
||||||
|
|
||||||
|
// Insert field exprs into struct_val
|
||||||
|
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||||
|
struct_val = builder
|
||||||
|
.build_insert_value(struct_val, field_val, index as u32, "insert_field")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||||
|
}
|
||||||
|
Access {
|
||||||
|
label,
|
||||||
|
field_layout,
|
||||||
|
struct_layout: Layout::Struct(fields),
|
||||||
|
record,
|
||||||
|
} => {
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Reconstruct struct layout
|
||||||
|
let mut reconstructed_struct_layout =
|
||||||
|
Vec::with_capacity_in(fields.len() + 1, env.arena);
|
||||||
|
for field in fields.iter() {
|
||||||
|
reconstructed_struct_layout.push(field.clone());
|
||||||
|
}
|
||||||
|
reconstructed_struct_layout.push((label.clone(), field_layout.clone()));
|
||||||
|
reconstructed_struct_layout.sort_by(|a, b| {
|
||||||
|
a.0.partial_cmp(&b.0)
|
||||||
|
.expect("TODO: failed to sort struct fields in crane access")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get index
|
||||||
|
let index = reconstructed_struct_layout
|
||||||
|
.iter()
|
||||||
|
.position(|(local_label, _)| local_label == label)
|
||||||
|
.unwrap() as u32; // TODO
|
||||||
|
|
||||||
|
// Get Struct val
|
||||||
|
let struct_val = build_expr(env, &scope, parent, record, procs).into_struct_value();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.build_extract_value(struct_val, index, "field_access")
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("I don't yet know how to LLVM build {:?}", expr);
|
panic!("I don't yet know how to LLVM build {:?}", expr);
|
||||||
}
|
}
|
||||||
|
@ -576,35 +650,57 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_ADD => {
|
Symbol::INT_ADD | Symbol::NUM_ADD => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_add(
|
let int_val = env.builder.build_int_add(
|
||||||
args[0].into_int_value(),
|
args[0].into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].into_int_value(),
|
||||||
"ADD_I64",
|
"add_i64",
|
||||||
);
|
);
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
Symbol::NUM_SUB => {
|
Symbol::FLOAT_ADD => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
|
let float_val = env.builder.build_float_add(
|
||||||
|
args[0].into_float_value(),
|
||||||
|
args[1].into_float_value(),
|
||||||
|
"add_f64",
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::FloatValue(float_val)
|
||||||
|
}
|
||||||
|
Symbol::INT_SUB | Symbol::NUM_SUB => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_sub(
|
let int_val = env.builder.build_int_sub(
|
||||||
args[0].into_int_value(),
|
args[0].into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].into_int_value(),
|
||||||
"SUB_I64",
|
"sub_I64",
|
||||||
);
|
);
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
|
Symbol::FLOAT_SUB => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
|
let float_val = env.builder.build_float_sub(
|
||||||
|
args[0].into_float_value(),
|
||||||
|
args[1].into_float_value(),
|
||||||
|
"sub_f64",
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::FloatValue(float_val)
|
||||||
|
}
|
||||||
Symbol::NUM_MUL => {
|
Symbol::NUM_MUL => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_mul(
|
let int_val = env.builder.build_int_mul(
|
||||||
args[0].into_int_value(),
|
args[0].into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].into_int_value(),
|
||||||
"MUL_I64",
|
"mul_i64",
|
||||||
);
|
);
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
|
@ -614,7 +710,7 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let int_val = env
|
let int_val = env
|
||||||
.builder
|
.builder
|
||||||
.build_int_neg(args[0].into_int_value(), "NEGATE_I64");
|
.build_int_neg(args[0].into_int_value(), "negate_i64");
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,8 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
Int64 => context.i64_type().as_basic_type_enum(),
|
Int64 => context.i64_type().as_basic_type_enum(),
|
||||||
Float64 => context.f64_type().as_basic_type_enum(),
|
Float64 => context.f64_type().as_basic_type_enum(),
|
||||||
|
Bool(_, _) => context.bool_type().as_basic_type_enum(),
|
||||||
|
Byte(_) => context.i8_type().as_basic_type_enum(),
|
||||||
Str | EmptyStr => context
|
Str | EmptyStr => context
|
||||||
.i8_type()
|
.i8_type()
|
||||||
.ptr_type(AddressSpace::Generic)
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
|
|
@ -24,26 +24,29 @@ mod test_gen {
|
||||||
use inkwell::passes::PassManager;
|
use inkwell::passes::PassManager;
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use roc_collections::all::{ImMap, MutMap};
|
use roc_collections::all::ImMap;
|
||||||
use roc_gen::crane::build::{declare_proc, define_proc_body, ScopeEntry};
|
use roc_gen::crane::build::{declare_proc, define_proc_body, ScopeEntry};
|
||||||
use roc_gen::crane::convert::type_from_layout;
|
use roc_gen::crane::convert::type_from_layout;
|
||||||
use roc_gen::crane::imports::define_malloc;
|
use roc_gen::crane::imports::define_malloc;
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
||||||
use roc_gen::llvm::convert::basic_type_from_layout;
|
use roc_gen::llvm::convert::basic_type_from_layout;
|
||||||
use roc_mono::expr::Expr;
|
use roc_mono::expr::{Expr, Procs};
|
||||||
use roc_mono::layout::Layout;
|
use roc_mono::layout::Layout;
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
// Pointer size on 64-bit platforms
|
||||||
|
const POINTER_SIZE: u32 = std::mem::size_of::<u64>() as u32;
|
||||||
|
|
||||||
macro_rules! assert_crane_evals_to {
|
macro_rules! assert_crane_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
||||||
let subs = Subs::new(var_store.into());
|
let subs = Subs::new(var_store.into());
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
let shared_builder = settings::builder();
|
let shared_builder = settings::builder();
|
||||||
let shared_flags = settings::Flags::new(shared_builder);
|
let shared_flags = settings::Flags::new(shared_builder);
|
||||||
let mut module: Module<SimpleJITBackend> =
|
let mut module: Module<SimpleJITBackend> =
|
||||||
|
@ -57,12 +60,12 @@ mod test_gen {
|
||||||
let main_fn_name = "$Test.main";
|
let main_fn_name = "$Test.main";
|
||||||
|
|
||||||
// Compute main_fn_ret_type before moving subs to Env
|
// Compute main_fn_ret_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert content to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert content to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_ret_type = type_from_layout(cfg, &layout);
|
let main_ret_type = type_from_layout(cfg, &layout);
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut env = roc_gen::crane::build::Env {
|
let mut env = roc_gen::crane::build::Env {
|
||||||
arena: &arena,
|
arena: &arena,
|
||||||
interns,
|
interns,
|
||||||
|
@ -72,7 +75,7 @@ mod test_gen {
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||||
let mono_expr = Expr::new(&arena, &subs, loc_expr.value, &mut procs, home, &mut ident_ids);
|
let mono_expr = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns
|
// Put this module's ident_ids back in the interns
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
@ -82,9 +85,9 @@ mod test_gen {
|
||||||
|
|
||||||
// Declare all the Procs, then insert them into scope so their bodies
|
// Declare all the Procs, then insert them into scope so their bodies
|
||||||
// can look up their Funcs in scope later when calling each other by value.
|
// can look up their Funcs in scope later when calling each other by value.
|
||||||
for (name, opt_proc) in procs.iter() {
|
for (name, opt_proc) in procs.as_map().into_iter() {
|
||||||
if let Some(proc) = opt_proc {
|
if let Some(proc) = opt_proc {
|
||||||
let (func_id, sig) = declare_proc(&env, &mut module, name.clone(), proc);
|
let (func_id, sig) = declare_proc(&env, &mut module, name, &proc);
|
||||||
|
|
||||||
declared.push((proc.clone(), sig.clone(), func_id));
|
declared.push((proc.clone(), sig.clone(), func_id));
|
||||||
|
|
||||||
|
@ -171,7 +174,7 @@ mod test_gen {
|
||||||
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
||||||
let subs = Subs::new(var_store.into());
|
let subs = Subs::new(var_store.into());
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let module = context.create_module("app");
|
let module = context.create_module("app");
|
||||||
|
@ -195,7 +198,7 @@ mod test_gen {
|
||||||
fpm.initialize();
|
fpm.initialize();
|
||||||
|
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_fn_type = basic_type_from_layout(&context, &layout)
|
let main_fn_type = basic_type_from_layout(&context, &layout)
|
||||||
.fn_type(&[], false);
|
.fn_type(&[], false);
|
||||||
|
@ -217,11 +220,11 @@ mod test_gen {
|
||||||
module: arena.alloc(module),
|
module: arena.alloc(module),
|
||||||
pointer_bytes
|
pointer_bytes
|
||||||
};
|
};
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// Populate Procs and get the low-level Expr from the canonical Expr
|
// Populate Procs and get the low-level Expr from the canonical Expr
|
||||||
let main_body = Expr::new(&arena, &subs, loc_expr.value, &mut procs, home, &mut ident_ids);
|
let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
@ -231,7 +234,7 @@ mod test_gen {
|
||||||
// Add all the Proc headers to the module.
|
// Add all the Proc headers to the module.
|
||||||
// We have to do this in a separate pass first,
|
// We have to do this in a separate pass first,
|
||||||
// because their bodies may reference each other.
|
// because their bodies may reference each other.
|
||||||
for (symbol, opt_proc) in procs.clone().into_iter() {
|
for (symbol, opt_proc) in procs.as_map().into_iter() {
|
||||||
if let Some(proc) = opt_proc {
|
if let Some(proc) = opt_proc {
|
||||||
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
||||||
|
|
||||||
|
@ -268,7 +271,7 @@ mod test_gen {
|
||||||
&ImMap::default(),
|
&ImMap::default(),
|
||||||
main_fn,
|
main_fn,
|
||||||
&main_body,
|
&main_body,
|
||||||
&mut MutMap::default(),
|
&mut Procs::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.build_return(Some(&ret));
|
builder.build_return(Some(&ret));
|
||||||
|
@ -306,7 +309,7 @@ mod test_gen {
|
||||||
let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr($src);
|
let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr($src);
|
||||||
|
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let module = context.create_module("app");
|
let module = context.create_module("app");
|
||||||
|
@ -330,7 +333,7 @@ mod test_gen {
|
||||||
fpm.initialize();
|
fpm.initialize();
|
||||||
|
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_fn_type = basic_type_from_layout(&context, &layout)
|
let main_fn_type = basic_type_from_layout(&context, &layout)
|
||||||
.fn_type(&[], false);
|
.fn_type(&[], false);
|
||||||
|
@ -352,11 +355,11 @@ mod test_gen {
|
||||||
module: arena.alloc(module),
|
module: arena.alloc(module),
|
||||||
pointer_bytes
|
pointer_bytes
|
||||||
};
|
};
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// Populate Procs and get the low-level Expr from the canonical Expr
|
// Populate Procs and get the low-level Expr from the canonical Expr
|
||||||
let main_body = Expr::new(&arena, &subs, loc_expr.value, &mut procs, home, &mut ident_ids);
|
let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
@ -366,12 +369,13 @@ mod test_gen {
|
||||||
// Add all the Proc headers to the module.
|
// Add all the Proc headers to the module.
|
||||||
// We have to do this in a separate pass first,
|
// We have to do this in a separate pass first,
|
||||||
// because their bodies may reference each other.
|
// because their bodies may reference each other.
|
||||||
for (symbol, opt_proc) in procs.clone().into_iter() {
|
for (symbol, opt_proc) in procs.as_map().into_iter() {
|
||||||
if let Some(proc) = opt_proc {
|
if let Some(proc) = opt_proc {
|
||||||
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
||||||
|
|
||||||
headers.push((proc, fn_val, arg_basic_types));
|
headers.push((proc, fn_val, arg_basic_types));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build each proc using its header info.
|
// Build each proc using its header info.
|
||||||
|
@ -403,7 +407,7 @@ mod test_gen {
|
||||||
&ImMap::default(),
|
&ImMap::default(),
|
||||||
main_fn,
|
main_fn,
|
||||||
&main_body,
|
&main_body,
|
||||||
&mut MutMap::default(),
|
&mut Procs::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.build_return(Some(&ret));
|
builder.build_return(Some(&ret));
|
||||||
|
@ -832,6 +836,21 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apply_identity_() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
identity = \a -> a
|
||||||
|
|
||||||
|
identity 5
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
5,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_unnamed_fn() {
|
fn apply_unnamed_fn() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -845,6 +864,19 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_add_f64() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
1.1 + 2.4 + 3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
6.5,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_add_i64() {
|
fn gen_add_i64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -858,6 +890,19 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_sub_f64() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
1.5 - 2.4 - 3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
-3.9,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_sub_i64() {
|
fn gen_sub_i64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -914,41 +959,36 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn basic_record() {
|
fn basic_record() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// point = { x: 15, y: 17, z: 19 }
|
{ y: 17, x: 15, z: 19 }.x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
15,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
// point.x
|
assert_evals_to!(
|
||||||
// "#
|
indoc!(
|
||||||
// ),
|
r#"
|
||||||
// 15,
|
{ x: 15, y: 17, z: 19 }.y
|
||||||
// i64
|
"#
|
||||||
// );
|
),
|
||||||
|
17,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// point = { x: 15, y: 17, z: 19 }
|
{ x: 15, y: 17, z: 19 }.z
|
||||||
|
"#
|
||||||
// point.y
|
),
|
||||||
// "#
|
19,
|
||||||
// ),
|
i64
|
||||||
// 17,
|
);
|
||||||
// i64
|
}
|
||||||
// );
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// point = { x: 15, y: 17, z: 19 }
|
|
||||||
|
|
||||||
// point.z
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1048,8 +1048,11 @@ fn parse_and_constrain(
|
||||||
|
|
||||||
(module, ident_ids, constraint, problems)
|
(module, ident_ids, constraint, problems)
|
||||||
}
|
}
|
||||||
Err(_runtime_error) => {
|
Err(runtime_error) => {
|
||||||
panic!("TODO gracefully handle module canonicalization error");
|
panic!(
|
||||||
|
"TODO gracefully handle module canonicalization error {:?}",
|
||||||
|
runtime_error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
interface WithBuiltins
|
interface WithBuiltins
|
||||||
exposes [ floatTest, divisionFn, divisionTest, intTest, constantInt, fromDep2, divDep1ByDep2 ]
|
exposes [ floatTest, divisionFn, divisionTest, intTest, constantNum, fromDep2, divDep1ByDep2 ]
|
||||||
imports [ Dep1, Dep2.{ two } ]
|
imports [ Dep1, Dep2.{ two } ]
|
||||||
|
|
||||||
floatTest = Float.highest
|
floatTest = Float.highest
|
||||||
|
@ -12,7 +12,7 @@ divisionTest = Float.highest / x
|
||||||
|
|
||||||
intTest = Int.highest
|
intTest = Int.highest
|
||||||
|
|
||||||
constantInt = 5
|
constantNum = 5
|
||||||
|
|
||||||
fromDep2 = Dep2.two
|
fromDep2 = Dep2.two
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ mod test_load {
|
||||||
"divisionTest" => "Float",
|
"divisionTest" => "Float",
|
||||||
"intTest" => "Int",
|
"intTest" => "Int",
|
||||||
"x" => "Float",
|
"x" => "Float",
|
||||||
"constantInt" => "Int",
|
"constantNum" => "Num *",
|
||||||
"divDep1ByDep2" => "Float",
|
"divDep1ByDep2" => "Float",
|
||||||
"fromDep2" => "Float",
|
"fromDep2" => "Float",
|
||||||
},
|
},
|
||||||
|
|
|
@ -208,7 +208,7 @@ mod test_uniq_load {
|
||||||
"divisionTest" => "Attr * Float",
|
"divisionTest" => "Attr * Float",
|
||||||
"intTest" => "Attr * Int",
|
"intTest" => "Attr * Int",
|
||||||
"x" => "Attr * Float",
|
"x" => "Attr * Float",
|
||||||
"constantInt" => "Attr * Int",
|
"constantNum" => "Attr * (Num (Attr * *))",
|
||||||
"divDep1ByDep2" => "Attr * Float",
|
"divDep1ByDep2" => "Attr * Float",
|
||||||
"fromDep2" => "Attr * Float",
|
"fromDep2" => "Attr * Float",
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,14 @@ use std::{fmt, u32};
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Symbol(u64);
|
pub struct Symbol(u64);
|
||||||
|
|
||||||
|
// When this is `true` (which it normally should be), Symbol's Debug::fmt implementation
|
||||||
|
// attempts to pretty print debug symbols using interns recorded using
|
||||||
|
// register_debug_idents calls (which should be made in debug mode).
|
||||||
|
// Set it to false if you want to see the raw ModuleId and IdentId ints,
|
||||||
|
// but please set it back to true before checking in the result!
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
const PRETTY_PRINT_DEBUG_SYMBOLS: bool = true;
|
||||||
|
|
||||||
/// In Debug builds only, Symbol has a name() method that lets
|
/// In Debug builds only, Symbol has a name() method that lets
|
||||||
/// you look up its name in a global intern table. This table is
|
/// you look up its name in a global intern table. This table is
|
||||||
/// behind a mutex, so it is neither populated nor available in release builds.
|
/// behind a mutex, so it is neither populated nor available in release builds.
|
||||||
|
@ -101,26 +109,30 @@ impl Symbol {
|
||||||
impl fmt::Debug for Symbol {
|
impl fmt::Debug for Symbol {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let module_id = self.module_id();
|
if PRETTY_PRINT_DEBUG_SYMBOLS {
|
||||||
let ident_id = self.ident_id();
|
let module_id = self.module_id();
|
||||||
|
let ident_id = self.ident_id();
|
||||||
|
|
||||||
match DEBUG_IDENT_IDS_BY_MODULE_ID.lock() {
|
match DEBUG_IDENT_IDS_BY_MODULE_ID.lock() {
|
||||||
Ok(names) => match &names.get(&module_id.0) {
|
Ok(names) => match &names.get(&module_id.0) {
|
||||||
Some(ident_ids) => match ident_ids.get_name(ident_id) {
|
Some(ident_ids) => match ident_ids.get_name(ident_id) {
|
||||||
Some(ident_str) => write!(f, "`{:?}.{}`", module_id, ident_str),
|
Some(ident_str) => write!(f, "`{:?}.{}`", module_id, ident_str),
|
||||||
|
None => fallback_debug_fmt(*self, f),
|
||||||
|
},
|
||||||
None => fallback_debug_fmt(*self, f),
|
None => fallback_debug_fmt(*self, f),
|
||||||
},
|
},
|
||||||
None => fallback_debug_fmt(*self, f),
|
Err(err) => {
|
||||||
},
|
// Print and return Err rather than panicking, because this
|
||||||
Err(err) => {
|
// might be used in a panic error message, and if we panick
|
||||||
// Print and return Err rather than panicking, because this
|
// while we're already panicking it'll kill the process
|
||||||
// might be used in a panic error message, and if we panick
|
// without printing any of the errors!
|
||||||
// while we're already panicking it'll kill the process
|
println!("DEBUG INFO: Failed to acquire lock for Debug reading from DEBUG_IDENT_IDS_BY_MODULE_ID, presumably because a thread panicked: {:?}", err);
|
||||||
// without printing any of the errors!
|
|
||||||
println!("DEBUG INFO: Failed to acquire lock for Debug reading from DEBUG_IDENT_IDS_BY_MODULE_ID, presumably because a thread panicked: {:?}", err);
|
|
||||||
|
|
||||||
fallback_debug_fmt(*self, f)
|
fallback_debug_fmt(*self, f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fallback_debug_fmt(*self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,11 +559,13 @@ macro_rules! define_builtins {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: Some of these builtins have a # at the beginning of their names.
|
||||||
|
// This is because they are for compiler use only, and should not cause
|
||||||
|
// namespace conflicts with userspace!
|
||||||
define_builtins! {
|
define_builtins! {
|
||||||
0 ATTR: "Attr" => {
|
0 ATTR: "#Attr" => {
|
||||||
0 UNDERSCORE: "_" // the _ used in pattern matches. This is Symbol 0.
|
0 UNDERSCORE: "_" // the _ used in pattern matches. This is Symbol 0.
|
||||||
1 ATTR_ATTR: "Attr" // the Attr.Attr type alias, used in uniqueness types
|
1 ATTR_ATTR: "Attr" // the #Attr.Attr type alias, used in uniqueness types.
|
||||||
2 ATTR_AT_ATTR: "@Attr" // the Attr.@Attr private tag
|
|
||||||
}
|
}
|
||||||
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
|
||||||
|
@ -575,6 +589,8 @@ define_builtins! {
|
||||||
4 INT_MOD: "mod"
|
4 INT_MOD: "mod"
|
||||||
5 INT_HIGHEST: "highest"
|
5 INT_HIGHEST: "highest"
|
||||||
6 INT_LOWEST: "lowest"
|
6 INT_LOWEST: "lowest"
|
||||||
|
7 INT_ADD: "#add"
|
||||||
|
8 INT_SUB: "#sub"
|
||||||
}
|
}
|
||||||
3 FLOAT: "Float" => {
|
3 FLOAT: "Float" => {
|
||||||
0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias
|
0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias
|
||||||
|
@ -585,6 +601,8 @@ define_builtins! {
|
||||||
5 FLOAT_SQRT: "sqrt"
|
5 FLOAT_SQRT: "sqrt"
|
||||||
6 FLOAT_HIGHEST: "highest"
|
6 FLOAT_HIGHEST: "highest"
|
||||||
7 FLOAT_LOWEST: "lowest"
|
7 FLOAT_LOWEST: "lowest"
|
||||||
|
8 FLOAT_ADD: "#add"
|
||||||
|
9 FLOAT_SUB: "#sub"
|
||||||
}
|
}
|
||||||
4 BOOL: "Bool" => {
|
4 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||||
|
@ -606,7 +624,7 @@ define_builtins! {
|
||||||
2 LIST_IS_EMPTY: "isEmpty"
|
2 LIST_IS_EMPTY: "isEmpty"
|
||||||
3 LIST_GET: "get"
|
3 LIST_GET: "get"
|
||||||
4 LIST_SET: "set"
|
4 LIST_SET: "set"
|
||||||
5 LIST_SET_IN_PLACE: "set_in_place"
|
5 LIST_SET_IN_PLACE: "#setInPlace"
|
||||||
6 LIST_PUSH: "push"
|
6 LIST_PUSH: "push"
|
||||||
7 LIST_MAP: "map"
|
7 LIST_MAP: "map"
|
||||||
8 LIST_LEN: "len"
|
8 LIST_LEN: "len"
|
||||||
|
|
|
@ -3,16 +3,95 @@ use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can;
|
use roc_can;
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
use roc_types::subs::{Content, ContentHash, FlatType, Subs, Variable};
|
||||||
|
|
||||||
pub type Procs<'a> = MutMap<Symbol, Option<Proc<'a>>>;
|
#[derive(Clone, Debug, PartialEq, Default)]
|
||||||
|
pub struct Procs<'a> {
|
||||||
|
user_defined: MutMap<Symbol, PartialProc<'a>>,
|
||||||
|
anonymous: MutMap<Symbol, Option<Proc<'a>>>,
|
||||||
|
builtin: MutSet<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Procs<'a> {
|
||||||
|
fn insert_user_defined(&mut self, symbol: Symbol, partial_proc: PartialProc<'a>) {
|
||||||
|
self.user_defined.insert(symbol, partial_proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_anonymous(&mut self, symbol: Symbol, proc: Option<Proc<'a>>) {
|
||||||
|
self.anonymous.insert(symbol, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_specialization(
|
||||||
|
&mut self,
|
||||||
|
symbol: Symbol,
|
||||||
|
hash: ContentHash,
|
||||||
|
spec_name: Symbol,
|
||||||
|
proc: Option<Proc<'a>>,
|
||||||
|
) {
|
||||||
|
self.user_defined
|
||||||
|
.get_mut(&symbol)
|
||||||
|
.map(|partial_proc| partial_proc.specializations.insert(hash, (spec_name, proc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_user_defined(&self, symbol: Symbol) -> Option<&PartialProc<'a>> {
|
||||||
|
self.user_defined.get(&symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
let anonymous: usize = self.anonymous.len();
|
||||||
|
let user_defined: usize = self
|
||||||
|
.user_defined
|
||||||
|
.values()
|
||||||
|
.map(|v| v.specializations.len())
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
anonymous + user_defined
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_builtin(&mut self, symbol: Symbol) {
|
||||||
|
self.builtin.insert(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_map(&self) -> MutMap<Symbol, Option<Proc<'a>>> {
|
||||||
|
let mut result = MutMap::default();
|
||||||
|
|
||||||
|
for partial_proc in self.user_defined.values() {
|
||||||
|
for (_, (symbol, opt_proc)) in partial_proc.specializations.clone().into_iter() {
|
||||||
|
result.insert(symbol, opt_proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (symbol, proc) in self.anonymous.clone().into_iter() {
|
||||||
|
result.insert(symbol, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for symbol in self.builtin.iter() {
|
||||||
|
result.insert(*symbol, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct PartialProc<'a> {
|
||||||
|
pub annotation: Variable,
|
||||||
|
pub patterns: Vec<'a, Symbol>,
|
||||||
|
pub body: roc_can::expr::Expr,
|
||||||
|
pub specializations: MutMap<ContentHash, (Symbol, Option<Proc<'a>>)>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Proc<'a> {
|
pub struct Proc<'a> {
|
||||||
|
pub name: Symbol,
|
||||||
pub args: &'a [(Layout<'a>, Symbol)],
|
pub args: &'a [(Layout<'a>, Symbol)],
|
||||||
pub body: Expr<'a>,
|
pub body: Expr<'a>,
|
||||||
pub closes_over: Layout<'a>,
|
pub closes_over: Layout<'a>,
|
||||||
|
@ -21,9 +100,24 @@ pub struct Proc<'a> {
|
||||||
|
|
||||||
struct Env<'a, 'i> {
|
struct Env<'a, 'i> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
pub subs: &'a Subs,
|
pub subs: &'a mut Subs,
|
||||||
pub home: ModuleId,
|
pub home: ModuleId,
|
||||||
pub ident_ids: &'i mut IdentIds,
|
pub ident_ids: &'i mut IdentIds,
|
||||||
|
pub pointer_size: u32,
|
||||||
|
symbol_counter: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'i> Env<'a, 'i> {
|
||||||
|
pub fn fresh_symbol(&mut self) -> Symbol {
|
||||||
|
let ident_id = self
|
||||||
|
.ident_ids
|
||||||
|
.add(format!("_{}", self.symbol_counter).into());
|
||||||
|
self.symbol_counter += 1;
|
||||||
|
|
||||||
|
self.home.register_debug_idents(&self.ident_ids);
|
||||||
|
|
||||||
|
Symbol::new(self.home, ident_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -89,7 +183,6 @@ pub enum Expr<'a> {
|
||||||
},
|
},
|
||||||
Tag {
|
Tag {
|
||||||
tag_layout: Layout<'a>,
|
tag_layout: Layout<'a>,
|
||||||
ext_layout: Layout<'a>,
|
|
||||||
name: TagName,
|
name: TagName,
|
||||||
arguments: &'a [Expr<'a>],
|
arguments: &'a [Expr<'a>],
|
||||||
},
|
},
|
||||||
|
@ -101,6 +194,7 @@ pub enum Expr<'a> {
|
||||||
label: Lowercase,
|
label: Lowercase,
|
||||||
field_layout: Layout<'a>,
|
field_layout: Layout<'a>,
|
||||||
struct_layout: Layout<'a>,
|
struct_layout: Layout<'a>,
|
||||||
|
record: &'a Expr<'a>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Array {
|
Array {
|
||||||
|
@ -114,23 +208,156 @@ pub enum Expr<'a> {
|
||||||
impl<'a> Expr<'a> {
|
impl<'a> Expr<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
subs: &'a Subs,
|
subs: &'a mut Subs,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
ident_ids: &mut IdentIds,
|
ident_ids: &mut IdentIds,
|
||||||
|
pointer_size: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut env = Env {
|
let mut env = Env {
|
||||||
arena,
|
arena,
|
||||||
subs,
|
subs,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
pointer_size,
|
||||||
|
symbol_counter: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
from_can(&mut env, can_expr, procs, None)
|
from_can(&mut env, can_expr, procs, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum IntOrFloat {
|
||||||
|
IntType,
|
||||||
|
FloatType,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
||||||
|
match subs.get_without_compacting(var).content {
|
||||||
|
Content::Alias(Symbol::INT_INTEGER, args, _) => {
|
||||||
|
debug_assert!(args.is_empty());
|
||||||
|
IntOrFloat::IntType
|
||||||
|
}
|
||||||
|
Content::FlexVar(_) => {
|
||||||
|
// If this was still a (Num *), assume compiling it to an Int
|
||||||
|
IntOrFloat::IntType
|
||||||
|
}
|
||||||
|
Content::Alias(Symbol::FLOAT_FLOATINGPOINT, args, _) => {
|
||||||
|
debug_assert!(args.is_empty());
|
||||||
|
IntOrFloat::FloatType
|
||||||
|
}
|
||||||
|
Content::Alias(Symbol::NUM_NUM, args, _) => {
|
||||||
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
|
match subs.get_without_compacting(args[0].1).content {
|
||||||
|
Content::Alias(Symbol::INT_INTEGER, args, _) => {
|
||||||
|
debug_assert!(args.is_empty());
|
||||||
|
IntOrFloat::IntType
|
||||||
|
}
|
||||||
|
Content::FlexVar(_) => {
|
||||||
|
// If this was still a (Num *), assume compiling it to an Int
|
||||||
|
IntOrFloat::IntType
|
||||||
|
}
|
||||||
|
Content::Alias(Symbol::FLOAT_FLOATINGPOINT, args, _) => {
|
||||||
|
debug_assert!(args.is_empty());
|
||||||
|
IntOrFloat::FloatType
|
||||||
|
}
|
||||||
|
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => {
|
||||||
|
debug_assert!(attr_args.len() == 2);
|
||||||
|
|
||||||
|
// Recurse on the second argument
|
||||||
|
to_int_or_float(subs, attr_args[1])
|
||||||
|
}
|
||||||
|
other => panic!(
|
||||||
|
"Unrecognized Num.Num alias type argument Content: {:?}",
|
||||||
|
other
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => {
|
||||||
|
debug_assert!(attr_args.len() == 2);
|
||||||
|
|
||||||
|
// Recurse on the second argument
|
||||||
|
to_int_or_float(subs, attr_args[1])
|
||||||
|
}
|
||||||
|
other => panic!("Unrecognized Num type argument Content: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn patterns_to_when<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
patterns: std::vec::Vec<(Variable, Located<roc_can::pattern::Pattern>)>,
|
||||||
|
body_var: Variable,
|
||||||
|
mut body: Located<roc_can::expr::Expr>,
|
||||||
|
) -> (
|
||||||
|
Vec<'a, Variable>,
|
||||||
|
Vec<'a, Symbol>,
|
||||||
|
Located<roc_can::expr::Expr>,
|
||||||
|
) {
|
||||||
|
let mut arg_vars = Vec::with_capacity_in(patterns.len(), env.arena);
|
||||||
|
let mut symbols = Vec::with_capacity_in(patterns.len(), env.arena);
|
||||||
|
|
||||||
|
for (pattern_var, pattern) in patterns.into_iter().rev() {
|
||||||
|
let (new_symbol, new_body) = pattern_to_when(env, pattern_var, pattern, body_var, body);
|
||||||
|
body = new_body;
|
||||||
|
symbols.push(new_symbol);
|
||||||
|
arg_vars.push(pattern_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
(arg_vars, symbols, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// turn irrefutable patterns into when. For example
|
||||||
|
///
|
||||||
|
/// foo = \{ x } -> body
|
||||||
|
///
|
||||||
|
/// Assuming the above program typechecks, the pattern match cannot fail
|
||||||
|
/// (it is irrefutable). It becomes
|
||||||
|
///
|
||||||
|
/// foo = \r ->
|
||||||
|
/// when r is
|
||||||
|
/// { x } -> body
|
||||||
|
///
|
||||||
|
/// conversion of one-pattern when expressions will do the most optimal thing
|
||||||
|
fn pattern_to_when<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
pattern_var: Variable,
|
||||||
|
pattern: Located<roc_can::pattern::Pattern>,
|
||||||
|
body_var: Variable,
|
||||||
|
body: Located<roc_can::expr::Expr>,
|
||||||
|
) -> (Symbol, Located<roc_can::expr::Expr>) {
|
||||||
|
use roc_can::expr::Expr::*;
|
||||||
|
use roc_can::pattern::Pattern::*;
|
||||||
|
|
||||||
|
match &pattern.value {
|
||||||
|
Identifier(symbol) => (*symbol, body),
|
||||||
|
Underscore => {
|
||||||
|
// for underscore we generate a dummy Symbol
|
||||||
|
(env.fresh_symbol(), body)
|
||||||
|
}
|
||||||
|
|
||||||
|
AppliedTag(_, _, _) | RecordDestructure(_, _) | Shadowed(_, _) | UnsupportedPattern(_) => {
|
||||||
|
let symbol = env.fresh_symbol();
|
||||||
|
|
||||||
|
let wrapped_body = When {
|
||||||
|
cond_var: pattern_var,
|
||||||
|
expr_var: body_var,
|
||||||
|
loc_cond: Box::new(Located::at_zero(Var(symbol))),
|
||||||
|
branches: vec![(pattern, body)],
|
||||||
|
};
|
||||||
|
|
||||||
|
(symbol, Located::at_zero(wrapped_body))
|
||||||
|
}
|
||||||
|
|
||||||
|
// These patters are refutable, and thus should never occur outside a `when` expression
|
||||||
|
IntLiteral(_) | NumLiteral(_,_) | FloatLiteral(_) | StrLiteral(_) => {
|
||||||
|
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_can<'a>(
|
fn from_can<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
|
@ -141,8 +368,12 @@ fn from_can<'a>(
|
||||||
use roc_can::pattern::Pattern::*;
|
use roc_can::pattern::Pattern::*;
|
||||||
|
|
||||||
match can_expr {
|
match can_expr {
|
||||||
Int(_, val) => Expr::Int(val),
|
Num(var, num) => match to_int_or_float(env.subs, var) {
|
||||||
Float(_, val) => Expr::Float(val),
|
IntOrFloat::IntType => Expr::Int(num),
|
||||||
|
IntOrFloat::FloatType => Expr::Float(num as f64),
|
||||||
|
},
|
||||||
|
Int(_, num) => Expr::Int(num),
|
||||||
|
Float(_, num) => Expr::Float(num),
|
||||||
Str(string) | BlockStr(string) => Expr::Str(env.arena.alloc(string)),
|
Str(string) | BlockStr(string) => Expr::Str(env.arena.alloc(string)),
|
||||||
Var(symbol) => Expr::Load(symbol),
|
Var(symbol) => Expr::Load(symbol),
|
||||||
LetNonRec(def, ret_expr, _, _) => {
|
LetNonRec(def, ret_expr, _, _) => {
|
||||||
|
@ -192,22 +423,88 @@ fn from_can<'a>(
|
||||||
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure(_, _, _, loc_args, boxed_body) => {
|
Closure(annotation, _, _, loc_args, boxed_body) => {
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
let (loc_body, ret_var) = *boxed_body;
|
||||||
let symbol =
|
|
||||||
name.unwrap_or_else(|| gen_closure_name(procs, &mut env.ident_ids, env.home));
|
|
||||||
|
|
||||||
add_closure(env, symbol, loc_body.value, ret_var, &loc_args, procs)
|
// turn record/tag patterns into a when expression, e.g.
|
||||||
|
//
|
||||||
|
// foo = \{ x } -> body
|
||||||
|
//
|
||||||
|
// becomes
|
||||||
|
//
|
||||||
|
// foo = \r -> when r is { x } -> body
|
||||||
|
//
|
||||||
|
// conversion of one-pattern when expressions will do the most optimal thing
|
||||||
|
let (arg_vars, arg_symbols, body) = patterns_to_when(env, loc_args, ret_var, loc_body);
|
||||||
|
|
||||||
|
let symbol = match name {
|
||||||
|
Some(symbol) => {
|
||||||
|
// a named closure
|
||||||
|
procs.insert_user_defined(
|
||||||
|
symbol,
|
||||||
|
PartialProc {
|
||||||
|
annotation,
|
||||||
|
patterns: arg_symbols,
|
||||||
|
body: body.value,
|
||||||
|
specializations: MutMap::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// an anonymous closure. These will always be specialized already
|
||||||
|
// by the surrounding context
|
||||||
|
let symbol = env.fresh_symbol();
|
||||||
|
|
||||||
|
// Has the side-effect of monomorphizing record types
|
||||||
|
// turning the ext_var into EmptyRecord or EmptyTagUnion
|
||||||
|
let _ = ContentHash::from_var(annotation, env.subs);
|
||||||
|
|
||||||
|
let opt_proc = specialize_proc_body(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
annotation,
|
||||||
|
ret_var,
|
||||||
|
symbol,
|
||||||
|
&arg_vars,
|
||||||
|
&arg_symbols,
|
||||||
|
annotation,
|
||||||
|
body.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs.insert_anonymous(symbol, opt_proc);
|
||||||
|
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Expr::FunctionPointer(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(boxed, loc_args, _) => {
|
Call(boxed, loc_args, _) => {
|
||||||
let (fn_var, loc_expr, _) = *boxed;
|
use IntOrFloat::*;
|
||||||
|
|
||||||
|
let (fn_var, loc_expr, ret_var) = *boxed;
|
||||||
|
|
||||||
|
let specialize_builtin_functions = {
|
||||||
|
|symbol, subs: &Subs| match symbol {
|
||||||
|
Symbol::NUM_ADD => match to_int_or_float(subs, ret_var) {
|
||||||
|
FloatType => Symbol::FLOAT_ADD,
|
||||||
|
IntType => Symbol::INT_ADD,
|
||||||
|
},
|
||||||
|
Symbol::NUM_SUB => match to_int_or_float(subs, ret_var) {
|
||||||
|
FloatType => Symbol::FLOAT_SUB,
|
||||||
|
IntType => Symbol::INT_SUB,
|
||||||
|
},
|
||||||
|
_ => symbol,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match from_can(env, loc_expr.value, procs, None) {
|
match from_can(env, loc_expr.value, procs, None) {
|
||||||
Expr::Load(proc_name) => {
|
Expr::Load(proc_name) => {
|
||||||
// Some functions can potentially mutate in-place.
|
// Some functions can potentially mutate in-place.
|
||||||
// If we have one of those, switch to the in-place version if appropriate.
|
// If we have one of those, switch to the in-place version if appropriate.
|
||||||
match proc_name {
|
match specialize_builtin_functions(proc_name, &env.subs) {
|
||||||
Symbol::LIST_SET => {
|
Symbol::LIST_SET => {
|
||||||
let subs = &env.subs;
|
let subs = &env.subs;
|
||||||
// The first arg is the one with the List in it.
|
// The first arg is the one with the List in it.
|
||||||
|
@ -234,12 +531,19 @@ fn from_can<'a>(
|
||||||
Symbol::LIST_SET
|
Symbol::LIST_SET
|
||||||
};
|
};
|
||||||
|
|
||||||
call_by_name(env, procs, new_name, loc_args)
|
call_by_name(env, procs, fn_var, ret_var, new_name, loc_args)
|
||||||
}
|
}
|
||||||
_ => call_by_name(env, procs, proc_name, loc_args),
|
_ => call_by_name(env, procs, fn_var, ret_var, proc_name, loc_args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => call_by_name(env, procs, proc_name, loc_args),
|
specialized_proc_symbol => call_by_name(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
fn_var,
|
||||||
|
ret_var,
|
||||||
|
specialized_proc_symbol,
|
||||||
|
loc_args,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ptr => {
|
ptr => {
|
||||||
|
@ -256,8 +560,8 @@ fn from_can<'a>(
|
||||||
args.push(from_can(env, loc_arg.value, procs, None));
|
args.push(from_can(env, loc_arg.value, procs, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout =
|
let layout = Layout::from_var(env.arena, fn_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(env.arena, fn_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice(), layout)
|
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice(), layout)
|
||||||
|
@ -273,7 +577,6 @@ fn from_can<'a>(
|
||||||
} => from_can_when(env, cond_var, expr_var, *loc_cond, branches, procs),
|
} => from_can_when(env, cond_var, expr_var, *loc_cond, branches, procs),
|
||||||
|
|
||||||
Record(ext_var, fields) => {
|
Record(ext_var, fields) => {
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let mut field_bodies = Vec::with_capacity_in(fields.len(), arena);
|
let mut field_bodies = Vec::with_capacity_in(fields.len(), arena);
|
||||||
|
|
||||||
|
@ -283,7 +586,7 @@ fn from_can<'a>(
|
||||||
field_bodies.push((label, expr));
|
field_bodies.push((label, expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
let struct_layout = match Layout::from_var(arena, ext_var, subs) {
|
let struct_layout = match Layout::from_var(arena, ext_var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -297,16 +600,49 @@ fn from_can<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tag {
|
||||||
|
variant_var,
|
||||||
|
name,
|
||||||
|
arguments: args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let arena = env.arena;
|
||||||
|
|
||||||
|
match Layout::from_var(arena, variant_var, &env.subs, env.pointer_size) {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool(_smaller, larger))) => Expr::Bool(name == larger),
|
||||||
|
Ok(Layout::Builtin(Builtin::Byte(tags))) => match tags.get(&name) {
|
||||||
|
Some(v) => Expr::Byte(*v),
|
||||||
|
None => panic!("Tag name is not part of the type"),
|
||||||
|
},
|
||||||
|
Ok(layout) => {
|
||||||
|
let mut arguments = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
|
for (_, arg) in args {
|
||||||
|
arguments.push(from_can(env, arg.value, procs, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Tag {
|
||||||
|
tag_layout: layout,
|
||||||
|
name,
|
||||||
|
arguments: arguments.into_bump_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(()) => {
|
||||||
|
// Invalid field!
|
||||||
|
panic!("TODO gracefully handle Access with invalid struct_layout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Access {
|
Access {
|
||||||
ext_var,
|
ext_var,
|
||||||
field_var,
|
field_var,
|
||||||
field,
|
field,
|
||||||
..
|
loc_expr,
|
||||||
} => {
|
} => {
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
let struct_layout = match Layout::from_var(arena, ext_var, subs) {
|
let struct_layout = match Layout::from_var(arena, ext_var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -314,7 +650,8 @@ fn from_can<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_layout = match Layout::from_var(arena, field_var, subs) {
|
let field_layout = match Layout::from_var(arena, field_var, env.subs, env.pointer_size)
|
||||||
|
{
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -322,10 +659,13 @@ fn from_can<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let record = arena.alloc(from_can(env, loc_expr.value, procs, None));
|
||||||
|
|
||||||
Expr::Access {
|
Expr::Access {
|
||||||
label: field,
|
label: field,
|
||||||
field_layout,
|
field_layout,
|
||||||
struct_layout,
|
struct_layout,
|
||||||
|
record,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,9 +673,8 @@ fn from_can<'a>(
|
||||||
elem_var,
|
elem_var,
|
||||||
loc_elems,
|
loc_elems,
|
||||||
} => {
|
} => {
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let elem_layout = match Layout::from_var(arena, elem_var, subs) {
|
let elem_layout = match Layout::from_var(arena, elem_var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
panic!("TODO gracefully handle List with invalid element layout");
|
panic!("TODO gracefully handle List with invalid element layout");
|
||||||
|
@ -353,58 +692,10 @@ fn from_can<'a>(
|
||||||
elems: elems.into_bump_slice(),
|
elems: elems.into_bump_slice(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => panic!("TODO convert canonicalized {:?} to ll::Expr", other),
|
other => panic!("TODO convert canonicalized {:?} to mono::Expr", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_closure<'a>(
|
|
||||||
env: &mut Env<'a, '_>,
|
|
||||||
symbol: Symbol,
|
|
||||||
can_body: roc_can::expr::Expr,
|
|
||||||
ret_var: Variable,
|
|
||||||
loc_args: &[(Variable, Located<Pattern>)],
|
|
||||||
procs: &mut Procs<'a>,
|
|
||||||
) -> Expr<'a> {
|
|
||||||
let subs = &env.subs;
|
|
||||||
let arena = env.arena;
|
|
||||||
let mut proc_args = Vec::with_capacity_in(loc_args.len(), arena);
|
|
||||||
|
|
||||||
for (arg_var, loc_arg) in loc_args.iter() {
|
|
||||||
let layout = match Layout::from_var(arena, *arg_var, subs) {
|
|
||||||
Ok(layout) => layout,
|
|
||||||
Err(()) => {
|
|
||||||
// Invalid closure!
|
|
||||||
procs.insert(symbol, None);
|
|
||||||
|
|
||||||
return Expr::FunctionPointer(symbol);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg_name: Symbol = match &loc_arg.value {
|
|
||||||
Pattern::Identifier(symbol) => *symbol,
|
|
||||||
_ => {
|
|
||||||
panic!("TODO determine arg_name for pattern {:?}", loc_arg.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
proc_args.push((layout, arg_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret_layout = Layout::from_var(arena, ret_var, subs)
|
|
||||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
|
||||||
|
|
||||||
let proc = Proc {
|
|
||||||
args: proc_args.into_bump_slice(),
|
|
||||||
body: from_can(env, can_body, procs, None),
|
|
||||||
closes_over: Layout::Struct(&[]),
|
|
||||||
ret_layout,
|
|
||||||
};
|
|
||||||
|
|
||||||
procs.insert(symbol, Some(proc));
|
|
||||||
|
|
||||||
Expr::FunctionPointer(symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_pattern<'a>(
|
fn store_pattern<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_pat: Pattern,
|
can_pat: Pattern,
|
||||||
|
@ -415,7 +706,7 @@ fn store_pattern<'a>(
|
||||||
) {
|
) {
|
||||||
use roc_can::pattern::Pattern::*;
|
use roc_can::pattern::Pattern::*;
|
||||||
|
|
||||||
let layout = match Layout::from_var(env.arena, var, env.subs) {
|
let layout = match Layout::from_var(env.arena, var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
panic!("TODO gen a runtime error here");
|
panic!("TODO gen a runtime error here");
|
||||||
|
@ -451,12 +742,6 @@ fn store_pattern<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_closure_name(procs: &Procs<'_>, ident_ids: &mut IdentIds, home: ModuleId) -> Symbol {
|
|
||||||
let ident_id = ident_ids.add(format!("_{}", procs.len()).into());
|
|
||||||
|
|
||||||
Symbol::new(home, ident_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_can_when<'a>(
|
fn from_can_when<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
cond_var: Variable,
|
cond_var: Variable,
|
||||||
|
@ -504,13 +789,37 @@ fn from_can_when<'a>(
|
||||||
let (loc_when_pat2, loc_else) = iter.next().unwrap();
|
let (loc_when_pat2, loc_else) = iter.next().unwrap();
|
||||||
|
|
||||||
match (&loc_when_pat1.value, &loc_when_pat2.value) {
|
match (&loc_when_pat1.value, &loc_when_pat2.value) {
|
||||||
|
(NumLiteral(var, num), NumLiteral(_, _)) | (NumLiteral(var, num), Underscore) => {
|
||||||
|
let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None));
|
||||||
|
let (builtin, cond_rhs_expr) = match to_int_or_float(env.subs, *var) {
|
||||||
|
IntOrFloat::IntType => (Builtin::Int64, Expr::Int(*num)),
|
||||||
|
IntOrFloat::FloatType => (Builtin::Float64, Expr::Float(*num as f64)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let cond_rhs = arena.alloc(cond_rhs_expr);
|
||||||
|
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||||
|
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||||
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
panic!("TODO turn this into a RuntimeError {:?}", err)
|
||||||
|
});
|
||||||
|
|
||||||
|
Expr::Cond {
|
||||||
|
cond_layout: Layout::Builtin(builtin),
|
||||||
|
cond_lhs,
|
||||||
|
cond_rhs,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
ret_layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
(IntLiteral(int), IntLiteral(_)) | (IntLiteral(int), Underscore) => {
|
(IntLiteral(int), IntLiteral(_)) | (IntLiteral(int), Underscore) => {
|
||||||
let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None));
|
let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None));
|
||||||
let cond_rhs = arena.alloc(Expr::Int(*int));
|
let cond_rhs = arena.alloc(Expr::Int(*int));
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||||
let ret_layout =
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
panic!("TODO turn this into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -528,8 +837,8 @@ fn from_can_when<'a>(
|
||||||
let cond_rhs = arena.alloc(Expr::Float(*float));
|
let cond_rhs = arena.alloc(Expr::Float(*float));
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||||
let ret_layout =
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
panic!("TODO turn this into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -552,7 +861,7 @@ fn from_can_when<'a>(
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let cond = from_can(env, loc_cond.value, procs, None);
|
let cond = from_can(env, loc_cond.value, procs, None);
|
||||||
let subs = &env.subs;
|
let subs = &env.subs;
|
||||||
let layout = Layout::from_var(arena, cond_var, subs)
|
let layout = Layout::from_var(arena, cond_var, subs, env.pointer_size)
|
||||||
.unwrap_or_else(|_| panic!("TODO generate a runtime error in from_can_when here!"));
|
.unwrap_or_else(|_| panic!("TODO generate a runtime error in from_can_when here!"));
|
||||||
|
|
||||||
// We can Switch on integers and tags, because they both have
|
// We can Switch on integers and tags, because they both have
|
||||||
|
@ -579,6 +888,22 @@ fn from_can_when<'a>(
|
||||||
let mono_expr = from_can(env, loc_expr.value, procs, None);
|
let mono_expr = from_can(env, loc_expr.value, procs, None);
|
||||||
|
|
||||||
match &loc_when_pat.value {
|
match &loc_when_pat.value {
|
||||||
|
NumLiteral(var, num) => {
|
||||||
|
// This is jumpable iff it's an int
|
||||||
|
match to_int_or_float(env.subs, *var) {
|
||||||
|
IntOrFloat::IntType => {
|
||||||
|
jumpable_branches.push((*num as u64, mono_expr));
|
||||||
|
}
|
||||||
|
IntOrFloat::FloatType => {
|
||||||
|
// The type checker should have converted these mismatches into RuntimeErrors already!
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
panic!("A type mismatch in a pattern was not converted to a runtime error: {:?}", loc_when_pat);
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
IntLiteral(int) => {
|
IntLiteral(int) => {
|
||||||
// Switch only compares the condition to the
|
// Switch only compares the condition to the
|
||||||
// alternatives based on their bit patterns,
|
// alternatives based on their bit patterns,
|
||||||
|
@ -633,12 +958,12 @@ fn from_can_when<'a>(
|
||||||
debug_assert!(opt_default_branch.is_some());
|
debug_assert!(opt_default_branch.is_some());
|
||||||
let default_branch = opt_default_branch.unwrap();
|
let default_branch = opt_default_branch.unwrap();
|
||||||
|
|
||||||
let cond_layout =
|
let cond_layout = Layout::from_var(arena, cond_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, cond_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn cond_layout into a RuntimeError {:?}", err)
|
panic!("TODO turn cond_layout into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
let ret_layout =
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn ret_layout into a RuntimeError {:?}", err)
|
panic!("TODO turn ret_layout into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -670,19 +995,131 @@ fn from_can_when<'a>(
|
||||||
fn call_by_name<'a>(
|
fn call_by_name<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
fn_var: Variable,
|
||||||
|
ret_var: Variable,
|
||||||
proc_name: Symbol,
|
proc_name: Symbol,
|
||||||
loc_args: std::vec::Vec<(Variable, Located<roc_can::expr::Expr>)>,
|
loc_args: std::vec::Vec<(Variable, Located<roc_can::expr::Expr>)>,
|
||||||
) -> Expr<'a> {
|
) -> Expr<'a> {
|
||||||
|
// create specialized procedure to call
|
||||||
|
|
||||||
|
// If we need to specialize the body, this will get populated with the info
|
||||||
|
// we need to do that. This is defined outside the procs.get_user_defined(...) call
|
||||||
|
// because if we tried to specialize the body inside that match, we would
|
||||||
|
// get a borrow checker error about trying to borrow `procs` as mutable
|
||||||
|
// while there is still an active immutable borrow.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
let opt_specialize_body: Option<(
|
||||||
|
ContentHash,
|
||||||
|
Variable,
|
||||||
|
roc_can::expr::Expr,
|
||||||
|
Vec<'a, Symbol>,
|
||||||
|
)>;
|
||||||
|
|
||||||
|
let specialized_proc_name = if let Some(partial_proc) = procs.get_user_defined(proc_name) {
|
||||||
|
let content_hash = ContentHash::from_var(fn_var, env.subs);
|
||||||
|
|
||||||
|
if let Some(specialization) = partial_proc.specializations.get(&content_hash) {
|
||||||
|
opt_specialize_body = None;
|
||||||
|
|
||||||
|
// a specialization with this type hash already exists, use its symbol
|
||||||
|
specialization.0
|
||||||
|
} else {
|
||||||
|
opt_specialize_body = Some((
|
||||||
|
content_hash,
|
||||||
|
partial_proc.annotation,
|
||||||
|
partial_proc.body.clone(),
|
||||||
|
partial_proc.patterns.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// generate a symbol for this specialization
|
||||||
|
env.fresh_symbol()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opt_specialize_body = None;
|
||||||
|
|
||||||
|
// This happens for built-in symbols (they are never defined as a Closure)
|
||||||
|
procs.insert_builtin(proc_name);
|
||||||
|
proc_name
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((content_hash, annotation, body, loc_patterns)) = opt_specialize_body {
|
||||||
|
// register proc, so specialization doesn't loop infinitely
|
||||||
|
procs.insert_specialization(proc_name, content_hash, specialized_proc_name, None);
|
||||||
|
|
||||||
|
let arg_vars = loc_args.iter().map(|v| v.0).collect::<std::vec::Vec<_>>();
|
||||||
|
|
||||||
|
let proc = specialize_proc_body(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
fn_var,
|
||||||
|
ret_var,
|
||||||
|
specialized_proc_name,
|
||||||
|
&arg_vars,
|
||||||
|
&loc_patterns,
|
||||||
|
annotation,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs.insert_specialization(proc_name, content_hash, specialized_proc_name, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate actual call
|
||||||
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
|
||||||
|
|
||||||
for (var, loc_arg) in loc_args {
|
for (var, loc_arg) in loc_args {
|
||||||
let layout = Layout::from_var(arena, var, subs)
|
let layout = Layout::from_var(&env.arena, var, &env.subs, env.pointer_size)
|
||||||
.unwrap_or_else(|err| panic!("TODO gracefully handle bad layout: {:?}", err));
|
.unwrap_or_else(|err| panic!("TODO gracefully handle bad layout: {:?}", err));
|
||||||
|
|
||||||
args.push((from_can(env, loc_arg.value, procs, None), layout));
|
args.push((from_can(env, loc_arg.value, procs, None), layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::CallByName(proc_name, args.into_bump_slice())
|
Expr::CallByName(specialized_proc_name, args.into_bump_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn specialize_proc_body<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
fn_var: Variable,
|
||||||
|
ret_var: Variable,
|
||||||
|
proc_name: Symbol,
|
||||||
|
loc_args: &[Variable],
|
||||||
|
pattern_symbols: &[Symbol],
|
||||||
|
annotation: Variable,
|
||||||
|
body: roc_can::expr::Expr,
|
||||||
|
) -> Option<Proc<'a>> {
|
||||||
|
// unify the called function with the specialized signature, then specialize the function body
|
||||||
|
let snapshot = env.subs.snapshot();
|
||||||
|
let unified = roc_unify::unify::unify(env.subs, annotation, fn_var);
|
||||||
|
debug_assert!(unified.mismatches.is_empty());
|
||||||
|
let specialized_body = from_can(env, body, procs, None);
|
||||||
|
// reset subs, so we don't get type errors when specializing for a different signature
|
||||||
|
env.subs.rollback_to(snapshot);
|
||||||
|
|
||||||
|
let mut proc_args = Vec::with_capacity_in(loc_args.len(), &env.arena);
|
||||||
|
|
||||||
|
for (arg_var, arg_name) in loc_args.iter().zip(pattern_symbols.iter()) {
|
||||||
|
let layout = match Layout::from_var(&env.arena, *arg_var, env.subs, env.pointer_size) {
|
||||||
|
Ok(layout) => layout,
|
||||||
|
Err(()) => {
|
||||||
|
// Invalid closure!
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
proc_args.push((layout, *arg_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_layout = Layout::from_var(&env.arena, ret_var, env.subs, env.pointer_size)
|
||||||
|
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
||||||
|
|
||||||
|
let proc = Proc {
|
||||||
|
name: proc_name,
|
||||||
|
args: proc_args.into_bump_slice(),
|
||||||
|
body: specialized_body,
|
||||||
|
closes_over: Layout::Struct(&[]),
|
||||||
|
ret_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(proc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ pub enum Layout<'a> {
|
||||||
pub enum Builtin<'a> {
|
pub enum Builtin<'a> {
|
||||||
Int64,
|
Int64,
|
||||||
Float64,
|
Float64,
|
||||||
|
Bool(TagName, TagName),
|
||||||
|
Byte(MutMap<TagName, u8>),
|
||||||
Str,
|
Str,
|
||||||
Map(&'a Layout<'a>, &'a Layout<'a>),
|
Map(&'a Layout<'a>, &'a Layout<'a>),
|
||||||
Set(&'a Layout<'a>),
|
Set(&'a Layout<'a>),
|
||||||
|
@ -33,20 +35,30 @@ impl<'a> Layout<'a> {
|
||||||
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
|
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
|
||||||
/// Panics if given a FlexVar or RigidVar, since those should have been
|
/// Panics if given a FlexVar or RigidVar, since those should have been
|
||||||
/// monomorphized away already!
|
/// monomorphized away already!
|
||||||
pub fn from_var(arena: &'a Bump, var: Variable, subs: &Subs) -> Result<Self, ()> {
|
pub fn from_var(
|
||||||
|
arena: &'a Bump,
|
||||||
|
var: Variable,
|
||||||
|
subs: &Subs,
|
||||||
|
pointer_size: u32,
|
||||||
|
) -> Result<Self, ()> {
|
||||||
let content = subs.get_without_compacting(var).content;
|
let content = subs.get_without_compacting(var).content;
|
||||||
|
|
||||||
Self::from_content(arena, content, subs)
|
Self::from_content(arena, content, subs, pointer_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_content(arena: &'a Bump, content: Content, subs: &Subs) -> Result<Self, ()> {
|
pub fn from_content(
|
||||||
|
arena: &'a Bump,
|
||||||
|
content: Content,
|
||||||
|
subs: &Subs,
|
||||||
|
pointer_size: u32,
|
||||||
|
) -> Result<Self, ()> {
|
||||||
use roc_types::subs::Content::*;
|
use roc_types::subs::Content::*;
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
var @ FlexVar(_) | var @ RigidVar(_) => {
|
var @ FlexVar(_) | var @ RigidVar(_) => {
|
||||||
panic!("Layout::from_content encountered an unresolved {:?}", var);
|
panic!("Layout::from_content encountered an unresolved {:?}", var);
|
||||||
}
|
}
|
||||||
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs),
|
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs, pointer_size),
|
||||||
|
|
||||||
Alias(Symbol::INT_INT, args, _) => {
|
Alias(Symbol::INT_INT, args, _) => {
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.is_empty());
|
||||||
|
@ -56,9 +68,12 @@ impl<'a> Layout<'a> {
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.is_empty());
|
||||||
Ok(Layout::Builtin(Builtin::Float64))
|
Ok(Layout::Builtin(Builtin::Float64))
|
||||||
}
|
}
|
||||||
Alias(_, _, var) => {
|
Alias(_, _, var) => Self::from_content(
|
||||||
Self::from_content(arena, subs.get_without_compacting(var).content, subs)
|
arena,
|
||||||
}
|
subs.get_without_compacting(var).content,
|
||||||
|
subs,
|
||||||
|
pointer_size,
|
||||||
|
),
|
||||||
Error => Err(()),
|
Error => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,6 +100,8 @@ impl<'a> Layout<'a> {
|
||||||
impl<'a> Builtin<'a> {
|
impl<'a> Builtin<'a> {
|
||||||
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
|
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
|
||||||
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
||||||
|
const BOOL_SIZE: u32 = std::mem::size_of::<bool>() as u32;
|
||||||
|
const BYTE_SIZE: u32 = std::mem::size_of::<u8>() as u32;
|
||||||
|
|
||||||
/// Number of machine words in an empty one of these
|
/// Number of machine words in an empty one of these
|
||||||
const STR_WORDS: u32 = 3;
|
const STR_WORDS: u32 = 3;
|
||||||
|
@ -103,6 +120,8 @@ impl<'a> Builtin<'a> {
|
||||||
match self {
|
match self {
|
||||||
Int64 => Builtin::I64_SIZE,
|
Int64 => Builtin::I64_SIZE,
|
||||||
Float64 => Builtin::F64_SIZE,
|
Float64 => Builtin::F64_SIZE,
|
||||||
|
Bool(_, _) => Builtin::BOOL_SIZE,
|
||||||
|
Byte(_) => Builtin::BYTE_SIZE,
|
||||||
Str | EmptyStr => Builtin::STR_WORDS * pointer_size,
|
Str | EmptyStr => Builtin::STR_WORDS * pointer_size,
|
||||||
Map(_, _) | EmptyMap => Builtin::MAP_WORDS * pointer_size,
|
Map(_, _) | EmptyMap => Builtin::MAP_WORDS * pointer_size,
|
||||||
Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size,
|
Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size,
|
||||||
|
@ -115,6 +134,7 @@ fn layout_from_flat_type<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
flat_type: FlatType,
|
flat_type: FlatType,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
|
pointer_size: u32,
|
||||||
) -> Result<Layout<'a>, ()> {
|
) -> Result<Layout<'a>, ()> {
|
||||||
use roc_types::subs::FlatType::*;
|
use roc_types::subs::FlatType::*;
|
||||||
|
|
||||||
|
@ -145,7 +165,8 @@ fn layout_from_flat_type<'a>(
|
||||||
match subs.get_without_compacting(args[0]).content {
|
match subs.get_without_compacting(args[0]).content {
|
||||||
FlexVar(_) | RigidVar(_) => Ok(Layout::Builtin(Builtin::EmptyList)),
|
FlexVar(_) | RigidVar(_) => Ok(Layout::Builtin(Builtin::EmptyList)),
|
||||||
content => {
|
content => {
|
||||||
let elem_layout = Layout::from_content(arena, content, subs)?;
|
let elem_layout =
|
||||||
|
Layout::from_content(arena, content, subs, pointer_size)?;
|
||||||
|
|
||||||
Ok(Layout::Builtin(Builtin::List(arena.alloc(elem_layout))))
|
Ok(Layout::Builtin(Builtin::List(arena.alloc(elem_layout))))
|
||||||
}
|
}
|
||||||
|
@ -161,7 +182,7 @@ fn layout_from_flat_type<'a>(
|
||||||
// For now, layout is unaffected by uniqueness.
|
// For now, layout is unaffected by uniqueness.
|
||||||
// (Incorporating refcounting may change this.)
|
// (Incorporating refcounting may change this.)
|
||||||
// Unwrap and continue
|
// Unwrap and continue
|
||||||
Layout::from_var(arena, wrapped_var, subs)
|
Layout::from_var(arena, wrapped_var, subs, pointer_size)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("TODO layout_from_flat_type for {:?}", Apply(symbol, args));
|
panic!("TODO layout_from_flat_type for {:?}", Apply(symbol, args));
|
||||||
|
@ -174,11 +195,16 @@ fn layout_from_flat_type<'a>(
|
||||||
for arg_var in args {
|
for arg_var in args {
|
||||||
let arg_content = subs.get_without_compacting(arg_var).content;
|
let arg_content = subs.get_without_compacting(arg_var).content;
|
||||||
|
|
||||||
fn_args.push(Layout::from_content(arena, arg_content, subs)?);
|
fn_args.push(Layout::from_content(
|
||||||
|
arena,
|
||||||
|
arg_content,
|
||||||
|
subs,
|
||||||
|
pointer_size,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_content = subs.get_without_compacting(ret_var).content;
|
let ret_content = subs.get_without_compacting(ret_var).content;
|
||||||
let ret = Layout::from_content(arena, ret_content, subs)?;
|
let ret = Layout::from_content(arena, ret_content, subs, pointer_size)?;
|
||||||
|
|
||||||
Ok(Layout::FunctionPointer(
|
Ok(Layout::FunctionPointer(
|
||||||
fn_args.into_bump_slice(),
|
fn_args.into_bump_slice(),
|
||||||
|
@ -188,7 +214,7 @@ fn layout_from_flat_type<'a>(
|
||||||
Record(mut fields, ext_var) => {
|
Record(mut fields, ext_var) => {
|
||||||
flatten_record(&mut fields, ext_var, subs);
|
flatten_record(&mut fields, ext_var, subs);
|
||||||
let ext_content = subs.get_without_compacting(ext_var).content;
|
let ext_content = subs.get_without_compacting(ext_var).content;
|
||||||
let ext_layout = match Layout::from_content(arena, ext_content, subs) {
|
let ext_layout = match Layout::from_content(arena, ext_content, subs, pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid record!
|
// Invalid record!
|
||||||
|
@ -216,13 +242,14 @@ fn layout_from_flat_type<'a>(
|
||||||
|
|
||||||
for (label, field_var) in fields {
|
for (label, field_var) in fields {
|
||||||
let field_content = subs.get_without_compacting(field_var).content;
|
let field_content = subs.get_without_compacting(field_var).content;
|
||||||
let field_layout = match Layout::from_content(arena, field_content, subs) {
|
let field_layout =
|
||||||
Ok(layout) => layout,
|
match Layout::from_content(arena, field_content, subs, pointer_size) {
|
||||||
Err(()) => {
|
Ok(layout) => layout,
|
||||||
// Invalid field!
|
Err(()) => {
|
||||||
panic!("TODO gracefully handle record with invalid field.var");
|
// Invalid field!
|
||||||
}
|
panic!("TODO gracefully handle record with invalid field.var");
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
field_layouts.push((label.clone(), field_layout));
|
field_layouts.push((label.clone(), field_layout));
|
||||||
}
|
}
|
||||||
|
@ -259,7 +286,50 @@ fn layout_from_flat_type<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("TODO handle a tag union with mutliple tags: {:?}", tags);
|
// Check if we can turn this tag union into an enum
|
||||||
|
// The arguments of all tags must have size 0.
|
||||||
|
// That is trivially the case when there are no arguments
|
||||||
|
//
|
||||||
|
// [ Orange, Apple, Banana ]
|
||||||
|
//
|
||||||
|
// But when one-tag tag unions are optimized away, we can also use an enum for
|
||||||
|
//
|
||||||
|
// [ Foo [ Unit ], Bar [ Unit ] ]
|
||||||
|
let arguments_have_size_0 = || {
|
||||||
|
tags.iter().all(|(_, args)| {
|
||||||
|
args.iter().all(|var| {
|
||||||
|
Layout::from_var(arena, *var, subs, pointer_size)
|
||||||
|
.map(|v| v.stack_size(pointer_size))
|
||||||
|
== Ok(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// up to 256 enum keys can be stored in a byte
|
||||||
|
if tags.len() <= std::u8::MAX as usize + 1 && arguments_have_size_0() {
|
||||||
|
if tags.len() <= 2 {
|
||||||
|
// Up to 2 enum tags can be stored (in theory) in one bit
|
||||||
|
let mut it = tags.keys();
|
||||||
|
let a: TagName = it.next().unwrap().clone();
|
||||||
|
let b: TagName = it.next().unwrap().clone();
|
||||||
|
|
||||||
|
if a < b {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool(a, b)))
|
||||||
|
} else {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool(b, a)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// up to 256 enum tags can be stored in a byte
|
||||||
|
let mut tag_to_u8 = MutMap::default();
|
||||||
|
|
||||||
|
for (counter, (name, _)) in tags.into_iter().enumerate() {
|
||||||
|
tag_to_u8.insert(name, counter as u8);
|
||||||
|
}
|
||||||
|
Ok(Layout::Builtin(Builtin::Byte(tag_to_u8)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("TODO handle a tag union with mutliple tags: {:?}", tags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,13 +387,15 @@ fn flatten_union(
|
||||||
|
|
||||||
match subs.get_without_compacting(ext_var).content {
|
match subs.get_without_compacting(ext_var).content {
|
||||||
Structure(EmptyTagUnion) => (),
|
Structure(EmptyTagUnion) => (),
|
||||||
Structure(TagUnion(new_tags, new_ext_var)) => {
|
Structure(TagUnion(new_tags, new_ext_var))
|
||||||
|
| Structure(RecursiveTagUnion(_, new_tags, new_ext_var)) => {
|
||||||
for (tag_name, vars) in new_tags {
|
for (tag_name, vars) in new_tags {
|
||||||
tags.insert(tag_name, vars);
|
tags.insert(tag_name, vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
flatten_union(tags, new_ext_var, subs)
|
flatten_union(tags, new_ext_var, subs)
|
||||||
}
|
}
|
||||||
|
Alias(_, _, actual) => flatten_union(tags, actual, subs),
|
||||||
invalid => {
|
invalid => {
|
||||||
panic!("Compiler error: flatten_union got an ext_var in a tag union that wasn't itself a tag union; instead, it was: {:?}", invalid);
|
panic!("Compiler error: flatten_union got an ext_var in a tag union that wasn't itself a tag union; instead, it was: {:?}", invalid);
|
||||||
}
|
}
|
||||||
|
@ -345,6 +417,7 @@ fn flatten_record(fields: &mut MutMap<Lowercase, Variable>, ext_var: Variable, s
|
||||||
|
|
||||||
flatten_record(fields, new_ext_var, subs)
|
flatten_record(fields, new_ext_var, subs)
|
||||||
}
|
}
|
||||||
|
Alias(_, _, actual) => flatten_record(fields, actual, subs),
|
||||||
invalid => {
|
invalid => {
|
||||||
panic!("Compiler error: flatten_record encountered an ext_var in a record that wasn't itself a record; instead, it was: {:?}", invalid);
|
panic!("Compiler error: flatten_record encountered an ext_var in a record that wasn't itself a record; instead, it was: {:?}", invalid);
|
||||||
}
|
}
|
||||||
|
@ -373,6 +446,10 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, ()> {
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.is_empty());
|
||||||
Ok(Layout::Builtin(Builtin::Float64))
|
Ok(Layout::Builtin(Builtin::Float64))
|
||||||
}
|
}
|
||||||
|
Content::FlexVar(_) => {
|
||||||
|
// If this was still a (Num *) then default to compiling it to i64
|
||||||
|
Ok(Layout::Builtin(Builtin::Int64))
|
||||||
|
}
|
||||||
other => {
|
other => {
|
||||||
panic!("TODO non structure Num.@Num flat_type {:?}", other);
|
panic!("TODO non structure Num.@Num flat_type {:?}", other);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,26 @@ mod helpers;
|
||||||
// Test monomorphization
|
// Test monomorphization
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_mono {
|
mod test_mono {
|
||||||
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, test_home, CanExprOut};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::ident::TagName::*;
|
||||||
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::expr::Expr::{self, *};
|
use roc_mono::expr::Expr::{self, *};
|
||||||
|
use roc_mono::expr::Procs;
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
fn compiles_to(src: &str, expected: Expr<'_>) {
|
fn compiles_to(src: &str, expected: Expr<'_>) {
|
||||||
|
compiles_to_with_interns(src, |_| expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compiles_to_with_interns<'a, F>(src: &str, get_expected: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(Interns) -> Expr<'a>,
|
||||||
|
{
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let CanExprOut {
|
let CanExprOut {
|
||||||
loc_expr,
|
loc_expr,
|
||||||
|
@ -34,23 +43,32 @@ mod test_mono {
|
||||||
} = can_expr(src);
|
} = can_expr(src);
|
||||||
let subs = Subs::new(var_store.into());
|
let subs = Subs::new(var_store.into());
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (_content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
|
// assume 64-bit pointers
|
||||||
|
let pointer_size = std::mem::size_of::<u64>() as u32;
|
||||||
|
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||||
let mono_expr = Expr::new(
|
let mono_expr = Expr::new(
|
||||||
&arena,
|
&arena,
|
||||||
&subs,
|
&mut subs,
|
||||||
loc_expr.value,
|
loc_expr.value,
|
||||||
&mut procs,
|
&mut procs,
|
||||||
home,
|
home,
|
||||||
&mut ident_ids,
|
&mut ident_ids,
|
||||||
|
pointer_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(mono_expr, expected);
|
dbg!(&procs);
|
||||||
|
|
||||||
|
// Put this module's ident_ids back in the interns
|
||||||
|
interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
|
||||||
|
assert_eq!(mono_expr, get_expected(interns));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -64,9 +82,279 @@ mod test_mono {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_unique_int_list() {
|
fn float_addition() {
|
||||||
compiles_to(
|
compiles_to(
|
||||||
"List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1",
|
"3.0 + 4",
|
||||||
|
CallByName(
|
||||||
|
Symbol::FLOAT_ADD,
|
||||||
|
&[
|
||||||
|
(Float(3.0), Layout::Builtin(Builtin::Float64)),
|
||||||
|
(Float(4.0), Layout::Builtin(Builtin::Float64)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_addition() {
|
||||||
|
compiles_to(
|
||||||
|
"0xDEADBEEF + 4",
|
||||||
|
CallByName(
|
||||||
|
Symbol::INT_ADD,
|
||||||
|
&[
|
||||||
|
(Int(3735928559), Layout::Builtin(Builtin::Int64)),
|
||||||
|
(Int(4), Layout::Builtin(Builtin::Int64)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_addition() {
|
||||||
|
// Default to Int for `Num *`
|
||||||
|
compiles_to(
|
||||||
|
"3 + 5",
|
||||||
|
CallByName(
|
||||||
|
Symbol::INT_ADD,
|
||||||
|
&[
|
||||||
|
(Int(3), Layout::Builtin(Builtin::Int64)),
|
||||||
|
(Int(5), Layout::Builtin(Builtin::Int64)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn specialize_closure() {
|
||||||
|
compiles_to(
|
||||||
|
r#"
|
||||||
|
f = \x -> x + 5
|
||||||
|
|
||||||
|
{ x: f 0x4, y: f 3.14 }
|
||||||
|
"#,
|
||||||
|
{
|
||||||
|
use self::Builtin::*;
|
||||||
|
use Layout::Builtin;
|
||||||
|
let home = test_home();
|
||||||
|
|
||||||
|
let gen_symbol_3 = Interns::from_index(home, 3);
|
||||||
|
let gen_symbol_4 = Interns::from_index(home, 4);
|
||||||
|
|
||||||
|
Struct {
|
||||||
|
fields: &[
|
||||||
|
(
|
||||||
|
"x".into(),
|
||||||
|
CallByName(gen_symbol_3, &[(Int(4), Builtin(Int64))]),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"y".into(),
|
||||||
|
CallByName(gen_symbol_4, &[(Float(3.14), Builtin(Float64))]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
layout: Layout::Struct(&[
|
||||||
|
("x".into(), Builtin(Int64)),
|
||||||
|
("y".into(), Builtin(Float64)),
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn record_pattern() {
|
||||||
|
// compiles_to(
|
||||||
|
// r#"
|
||||||
|
// \{ x } -> x + 0x5
|
||||||
|
// "#,
|
||||||
|
// { Float(3.45) },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn tag_pattern() {
|
||||||
|
// compiles_to(
|
||||||
|
// r#"
|
||||||
|
// \Foo x -> x + 0x5
|
||||||
|
// "#,
|
||||||
|
// { Float(3.45) },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_identity() {
|
||||||
|
compiles_to(
|
||||||
|
r#"
|
||||||
|
id = \x -> x
|
||||||
|
|
||||||
|
id { x: id 0x4 }
|
||||||
|
"#,
|
||||||
|
{
|
||||||
|
use self::Builtin::*;
|
||||||
|
use Layout::Builtin;
|
||||||
|
let home = test_home();
|
||||||
|
|
||||||
|
let gen_symbol_3 = Interns::from_index(home, 3);
|
||||||
|
let gen_symbol_4 = Interns::from_index(home, 4);
|
||||||
|
|
||||||
|
CallByName(
|
||||||
|
gen_symbol_3,
|
||||||
|
&[(
|
||||||
|
Struct {
|
||||||
|
fields: &[(
|
||||||
|
"x".into(),
|
||||||
|
CallByName(gen_symbol_4, &[(Int(4), Builtin(Int64))]),
|
||||||
|
)],
|
||||||
|
layout: Layout::Struct(&[("x".into(), Builtin(Int64))]),
|
||||||
|
},
|
||||||
|
Layout::Struct(&[("x".into(), Builtin(Int64))]),
|
||||||
|
)],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// needs LetRec to be converted to mono
|
||||||
|
// #[test]
|
||||||
|
// fn polymorphic_recursive() {
|
||||||
|
// compiles_to(
|
||||||
|
// r#"
|
||||||
|
// f = \x ->
|
||||||
|
// when x < 10 is
|
||||||
|
// True -> f (x + 1)
|
||||||
|
// False -> x
|
||||||
|
//
|
||||||
|
// { x: f 0x4, y: f 3.14 }
|
||||||
|
// "#,
|
||||||
|
// {
|
||||||
|
// use self::Builtin::*;
|
||||||
|
// use Layout::Builtin;
|
||||||
|
// let home = test_home();
|
||||||
|
//
|
||||||
|
// let gen_symbol_3 = Interns::from_index(home, 3);
|
||||||
|
// let gen_symbol_4 = Interns::from_index(home, 4);
|
||||||
|
//
|
||||||
|
// Float(3.4)
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// needs layout for non-empty tag union
|
||||||
|
// #[test]
|
||||||
|
// fn is_nil() {
|
||||||
|
// let arena = Bump::new();
|
||||||
|
//
|
||||||
|
// compiles_to_with_interns(
|
||||||
|
// r#"
|
||||||
|
// LinkedList a : [ Cons a (LinkedList a), Nil ]
|
||||||
|
//
|
||||||
|
// isNil : LinkedList a -> Bool
|
||||||
|
// isNil = \list ->
|
||||||
|
// when list is
|
||||||
|
// Nil -> True
|
||||||
|
// Cons _ _ -> False
|
||||||
|
//
|
||||||
|
// listInt : LinkedList Int
|
||||||
|
// listInt = Nil
|
||||||
|
//
|
||||||
|
// isNil listInt
|
||||||
|
// "#,
|
||||||
|
// |interns| {
|
||||||
|
// let home = test_home();
|
||||||
|
// let var_is_nil = interns.symbol(home, "isNil".into());
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool_literal() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
compiles_to_with_interns(
|
||||||
|
r#"
|
||||||
|
x : Bool
|
||||||
|
x = True
|
||||||
|
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
|interns| {
|
||||||
|
let home = test_home();
|
||||||
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
|
let stores = [(
|
||||||
|
var_x,
|
||||||
|
Layout::Builtin(Builtin::Bool(Global("False".into()), Global("True".into()))),
|
||||||
|
Bool(true),
|
||||||
|
)];
|
||||||
|
|
||||||
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
Store(arena.alloc(stores), arena.alloc(load))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_element_enum() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
compiles_to_with_interns(
|
||||||
|
r#"
|
||||||
|
x : [ Yes, No ]
|
||||||
|
x = No
|
||||||
|
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
|interns| {
|
||||||
|
let home = test_home();
|
||||||
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
|
let stores = [(
|
||||||
|
var_x,
|
||||||
|
Layout::Builtin(Builtin::Bool(Global("No".into()), Global("Yes".into()))),
|
||||||
|
Bool(false),
|
||||||
|
)];
|
||||||
|
|
||||||
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
Store(arena.alloc(stores), arena.alloc(load))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn three_element_enum() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
compiles_to_with_interns(
|
||||||
|
r#"
|
||||||
|
# this test is brought to you by fruits.com!
|
||||||
|
x : [ Apple, Orange, Banana ]
|
||||||
|
x = Orange
|
||||||
|
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
|interns| {
|
||||||
|
let home = test_home();
|
||||||
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
|
let mut fruits = MutMap::default();
|
||||||
|
|
||||||
|
fruits.insert(Global("Banana".into()), 0);
|
||||||
|
fruits.insert(Global("Orange".into()), 1);
|
||||||
|
fruits.insert(Global("Apple".into()), 2);
|
||||||
|
|
||||||
|
let stores = [(var_x, Layout::Builtin(Builtin::Byte(fruits)), Byte(1))];
|
||||||
|
|
||||||
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
Store(arena.alloc(stores), arena.alloc(load))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_unique_int_list() {
|
||||||
|
compiles_to("List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1", {
|
||||||
CallByName(
|
CallByName(
|
||||||
Symbol::LIST_GET_UNSAFE,
|
Symbol::LIST_GET_UNSAFE,
|
||||||
&vec![
|
&vec![
|
||||||
|
@ -91,7 +379,7 @@ mod test_mono {
|
||||||
),
|
),
|
||||||
(Int(1), Layout::Builtin(Builtin::Int64)),
|
(Int(1), Layout::Builtin(Builtin::Int64)),
|
||||||
],
|
],
|
||||||
),
|
)
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ mod helpers;
|
||||||
mod test_opt {
|
mod test_opt {
|
||||||
use crate::helpers::{infer_expr, uniq_expr};
|
use crate::helpers::{infer_expr, uniq_expr};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::MutMap;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::expr::Expr::{self, *};
|
use roc_mono::expr::Expr::{self, *};
|
||||||
|
use roc_mono::expr::Procs;
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
@ -25,20 +25,24 @@ mod test_opt {
|
||||||
let (loc_expr, _, _problems, subs, var, constraint, home, mut interns) = uniq_expr(src);
|
let (loc_expr, _, _problems, subs, var, constraint, home, mut interns) = uniq_expr(src);
|
||||||
|
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (_content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
|
// assume 64-bit pointers
|
||||||
|
let pointer_size = std::mem::size_of::<u64>() as u32;
|
||||||
|
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||||
let mono_expr = Expr::new(
|
let mono_expr = Expr::new(
|
||||||
&arena,
|
&arena,
|
||||||
&subs,
|
&mut subs,
|
||||||
loc_expr.value,
|
loc_expr.value,
|
||||||
&mut procs,
|
&mut procs,
|
||||||
home,
|
home,
|
||||||
&mut ident_ids,
|
&mut ident_ids,
|
||||||
|
pointer_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(mono_expr, expected);
|
assert_eq!(mono_expr, expected);
|
||||||
|
|
|
@ -93,7 +93,7 @@ pub struct WhenPattern<'a> {
|
||||||
pub enum Expr<'a> {
|
pub enum Expr<'a> {
|
||||||
// Number Literals
|
// Number Literals
|
||||||
Float(&'a str),
|
Float(&'a str),
|
||||||
Int(&'a str),
|
Num(&'a str),
|
||||||
NonBase10Int {
|
NonBase10Int {
|
||||||
string: &'a str,
|
string: &'a str,
|
||||||
base: Base,
|
base: Base,
|
||||||
|
@ -324,7 +324,7 @@ pub enum Pattern<'a> {
|
||||||
Nested(&'a Pattern<'a>),
|
Nested(&'a Pattern<'a>),
|
||||||
|
|
||||||
// Literal
|
// Literal
|
||||||
IntLiteral(&'a str),
|
NumLiteral(&'a str),
|
||||||
NonBase10Literal {
|
NonBase10Literal {
|
||||||
string: &'a str,
|
string: &'a str,
|
||||||
base: Base,
|
base: Base,
|
||||||
|
@ -425,7 +425,7 @@ impl<'a> Pattern<'a> {
|
||||||
(Nested(x), Nested(y)) => x.equivalent(y),
|
(Nested(x), Nested(y)) => x.equivalent(y),
|
||||||
|
|
||||||
// Literal
|
// Literal
|
||||||
(IntLiteral(x), IntLiteral(y)) => x == y,
|
(NumLiteral(x), NumLiteral(y)) => x == y,
|
||||||
(
|
(
|
||||||
NonBase10Literal {
|
NonBase10Literal {
|
||||||
string: string_x,
|
string: string_x,
|
||||||
|
|
|
@ -299,7 +299,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Float(string) => Ok(Pattern::FloatLiteral(string)),
|
Expr::Float(string) => Ok(Pattern::FloatLiteral(string)),
|
||||||
Expr::Int(string) => Ok(Pattern::IntLiteral(string)),
|
Expr::Num(string) => Ok(Pattern::NumLiteral(string)),
|
||||||
Expr::NonBase10Int {
|
Expr::NonBase10Int {
|
||||||
string,
|
string,
|
||||||
base,
|
base,
|
||||||
|
@ -551,7 +551,7 @@ fn annotation_or_alias<'a>(
|
||||||
QualifiedIdentifier { .. } => {
|
QualifiedIdentifier { .. } => {
|
||||||
panic!("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`");
|
panic!("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`");
|
||||||
}
|
}
|
||||||
IntLiteral(_)
|
NumLiteral(_)
|
||||||
| NonBase10Literal { .. }
|
| NonBase10Literal { .. }
|
||||||
| FloatLiteral(_)
|
| FloatLiteral(_)
|
||||||
| StrLiteral(_)
|
| StrLiteral(_)
|
||||||
|
@ -1310,8 +1310,11 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
region: loc_arg.region,
|
region: loc_arg.region,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err(_malformed) => {
|
Err(malformed) => {
|
||||||
panic!("TODO early return malformed pattern");
|
panic!(
|
||||||
|
"TODO early return malformed pattern {:?}",
|
||||||
|
malformed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ where
|
||||||
{
|
{
|
||||||
use self::LiteralType::*;
|
use self::LiteralType::*;
|
||||||
|
|
||||||
let mut typ = Int;
|
let mut typ = Num;
|
||||||
|
|
||||||
// We already parsed 1 character (which may have been a minus sign).
|
// We already parsed 1 character (which may have been a minus sign).
|
||||||
let mut bytes_parsed = 1;
|
let mut bytes_parsed = 1;
|
||||||
|
@ -71,8 +71,8 @@ where
|
||||||
} else {
|
} else {
|
||||||
return err_unexpected();
|
return err_unexpected();
|
||||||
}
|
}
|
||||||
} else if next_ch == 'b' && typ == Int {
|
} else if next_ch == 'b' && typ == Num {
|
||||||
// We have to check for typ == Int because otherwise we get a false
|
// We have to check for typ == Num because otherwise we get a false
|
||||||
// positive here when parsing a hex literal that happens to have
|
// positive here when parsing a hex literal that happens to have
|
||||||
// a 'b' in it, e.g. 0xbbbb
|
// a 'b' in it, e.g. 0xbbbb
|
||||||
if is_potentially_non_base10() {
|
if is_potentially_non_base10() {
|
||||||
|
@ -129,7 +129,7 @@ where
|
||||||
// If the number is malformed (outside the supported range),
|
// If the number is malformed (outside the supported range),
|
||||||
// we'll succeed with an appropriate Expr which records that.
|
// we'll succeed with an appropriate Expr which records that.
|
||||||
let expr = match typ {
|
let expr = match typ {
|
||||||
Int => Expr::Int(&state.input[0..bytes_parsed]),
|
Num => Expr::Num(&state.input[0..bytes_parsed]),
|
||||||
Float => Expr::Float(&state.input[0..bytes_parsed]),
|
Float => Expr::Float(&state.input[0..bytes_parsed]),
|
||||||
// For these we trim off the 0x/0o/0b part
|
// For these we trim off the 0x/0o/0b part
|
||||||
Hex => from_base(Base::Hex),
|
Hex => from_base(Base::Hex),
|
||||||
|
@ -144,7 +144,7 @@ where
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
enum LiteralType {
|
enum LiteralType {
|
||||||
Int,
|
Num,
|
||||||
Float,
|
Float,
|
||||||
Hex,
|
Hex,
|
||||||
Octal,
|
Octal,
|
||||||
|
|
|
@ -147,26 +147,26 @@ mod test_parse {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zero_int() {
|
fn zero_int() {
|
||||||
assert_parses_to("0", Int("0"));
|
assert_parses_to("0", Num("0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn positive_int() {
|
fn positive_int() {
|
||||||
assert_parses_to("1", Int("1"));
|
assert_parses_to("1", Num("1"));
|
||||||
assert_parses_to("42", Int("42"));
|
assert_parses_to("42", Num("42"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negative_int() {
|
fn negative_int() {
|
||||||
assert_parses_to("-1", Int("-1"));
|
assert_parses_to("-1", Num("-1"));
|
||||||
assert_parses_to("-42", Int("-42"));
|
assert_parses_to("-42", Num("-42"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn highest_int() {
|
fn highest_int() {
|
||||||
assert_parses_to(
|
assert_parses_to(
|
||||||
i64::MAX.to_string().as_str(),
|
i64::MAX.to_string().as_str(),
|
||||||
Int(i64::MAX.to_string().as_str()),
|
Num(i64::MAX.to_string().as_str()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,24 +174,24 @@ mod test_parse {
|
||||||
fn lowest_int() {
|
fn lowest_int() {
|
||||||
assert_parses_to(
|
assert_parses_to(
|
||||||
i64::MIN.to_string().as_str(),
|
i64::MIN.to_string().as_str(),
|
||||||
Int(i64::MIN.to_string().as_str()),
|
Num(i64::MIN.to_string().as_str()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn int_with_underscore() {
|
fn int_with_underscore() {
|
||||||
assert_parses_to("1_2_34_567", Int("1_2_34_567"));
|
assert_parses_to("1_2_34_567", Num("1_2_34_567"));
|
||||||
assert_parses_to("-1_2_34_567", Int("-1_2_34_567"));
|
assert_parses_to("-1_2_34_567", Num("-1_2_34_567"));
|
||||||
// The following cases are silly. They aren't supported on purpose,
|
// The following cases are silly. They aren't supported on purpose,
|
||||||
// but there would be a performance cost to explicitly disallowing them,
|
// but there would be a performance cost to explicitly disallowing them,
|
||||||
// which doesn't seem like it would benefit anyone.
|
// which doesn't seem like it would benefit anyone.
|
||||||
assert_parses_to("1_", Int("1_"));
|
assert_parses_to("1_", Num("1_"));
|
||||||
assert_parses_to("1__23", Int("1__23"));
|
assert_parses_to("1__23", Num("1__23"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[quickcheck]
|
#[quickcheck]
|
||||||
fn all_i64_values_parse(num: i64) {
|
fn all_i64_values_parse(num: i64) {
|
||||||
assert_parses_to(num.to_string().as_str(), Int(num.to_string().as_str()));
|
assert_parses_to(num.to_string().as_str(), Num(num.to_string().as_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FLOAT LITERALS
|
// FLOAT LITERALS
|
||||||
|
@ -262,12 +262,12 @@ mod test_parse {
|
||||||
let label1 = LabeledValue(
|
let label1 = LabeledValue(
|
||||||
Located::new(0, 0, 16, 17, "x"),
|
Located::new(0, 0, 16, 17, "x"),
|
||||||
&[],
|
&[],
|
||||||
arena.alloc(Located::new(0, 0, 19, 20, Int("5"))),
|
arena.alloc(Located::new(0, 0, 19, 20, Num("5"))),
|
||||||
);
|
);
|
||||||
let label2 = LabeledValue(
|
let label2 = LabeledValue(
|
||||||
Located::new(0, 0, 22, 23, "y"),
|
Located::new(0, 0, 22, 23, "y"),
|
||||||
&[],
|
&[],
|
||||||
arena.alloc(Located::new(0, 0, 25, 26, Int("0"))),
|
arena.alloc(Located::new(0, 0, 25, 26, Num("0"))),
|
||||||
);
|
);
|
||||||
let fields = bumpalo::vec![in &arena;
|
let fields = bumpalo::vec![in &arena;
|
||||||
Located::new(0, 0, 16, 20, label1),
|
Located::new(0, 0, 16, 20, label1),
|
||||||
|
@ -293,9 +293,9 @@ mod test_parse {
|
||||||
fn one_plus_two() {
|
fn one_plus_two() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, Int("1")),
|
Located::new(0, 0, 0, 1, Num("1")),
|
||||||
Located::new(0, 0, 1, 2, Plus),
|
Located::new(0, 0, 1, 2, Plus),
|
||||||
Located::new(0, 0, 2, 3, Int("2")),
|
Located::new(0, 0, 2, 3, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1+2");
|
let actual = parse_with(&arena, "1+2");
|
||||||
|
@ -307,9 +307,9 @@ mod test_parse {
|
||||||
fn one_minus_two() {
|
fn one_minus_two() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, Int("1")),
|
Located::new(0, 0, 0, 1, Num("1")),
|
||||||
Located::new(0, 0, 1, 2, Minus),
|
Located::new(0, 0, 1, 2, Minus),
|
||||||
Located::new(0, 0, 2, 3, Int("2")),
|
Located::new(0, 0, 2, 3, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1-2");
|
let actual = parse_with(&arena, "1-2");
|
||||||
|
@ -321,9 +321,9 @@ mod test_parse {
|
||||||
fn add_with_spaces() {
|
fn add_with_spaces() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, Int("1")),
|
Located::new(0, 0, 0, 1, Num("1")),
|
||||||
Located::new(0, 0, 3, 4, Plus),
|
Located::new(0, 0, 3, 4, Plus),
|
||||||
Located::new(0, 0, 7, 8, Int("2")),
|
Located::new(0, 0, 7, 8, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1 + 2");
|
let actual = parse_with(&arena, "1 + 2");
|
||||||
|
@ -335,9 +335,9 @@ mod test_parse {
|
||||||
fn sub_with_spaces() {
|
fn sub_with_spaces() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, Int("1")),
|
Located::new(0, 0, 0, 1, Num("1")),
|
||||||
Located::new(0, 0, 3, 4, Minus),
|
Located::new(0, 0, 3, 4, Minus),
|
||||||
Located::new(0, 0, 7, 8, Int("2")),
|
Located::new(0, 0, 7, 8, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1 - 2");
|
let actual = parse_with(&arena, "1 - 2");
|
||||||
|
@ -360,7 +360,7 @@ mod test_parse {
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, var),
|
Located::new(0, 0, 0, 1, var),
|
||||||
Located::new(0, 0, 2, 3, Plus),
|
Located::new(0, 0, 2, 3, Plus),
|
||||||
Located::new(0, 0, 4, 5, Int("2")),
|
Located::new(0, 0, 4, 5, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "x + 2");
|
let actual = parse_with(&arena, "x + 2");
|
||||||
|
@ -382,7 +382,7 @@ mod test_parse {
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, var),
|
Located::new(0, 0, 0, 1, var),
|
||||||
Located::new(0, 0, 2, 3, Minus),
|
Located::new(0, 0, 2, 3, Minus),
|
||||||
Located::new(0, 0, 4, 5, Int("2")),
|
Located::new(0, 0, 4, 5, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "x - 2");
|
let actual = parse_with(&arena, "x - 2");
|
||||||
|
@ -394,13 +394,13 @@ mod test_parse {
|
||||||
fn newline_before_add() {
|
fn newline_before_add() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int = Expr::SpaceAfter(
|
let spaced_int = Expr::SpaceAfter(
|
||||||
arena.alloc(Int("3")),
|
arena.alloc(Num("3")),
|
||||||
bumpalo::vec![in &arena; Newline].into_bump_slice(),
|
bumpalo::vec![in &arena; Newline].into_bump_slice(),
|
||||||
);
|
);
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, spaced_int),
|
Located::new(0, 0, 0, 1, spaced_int),
|
||||||
Located::new(1, 1, 0, 1, Plus),
|
Located::new(1, 1, 0, 1, Plus),
|
||||||
Located::new(1, 1, 2, 3, Int("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 \n+ 4");
|
let actual = parse_with(&arena, "3 \n+ 4");
|
||||||
|
@ -412,13 +412,13 @@ mod test_parse {
|
||||||
fn newline_before_sub() {
|
fn newline_before_sub() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int = Expr::SpaceAfter(
|
let spaced_int = Expr::SpaceAfter(
|
||||||
arena.alloc(Int("3")),
|
arena.alloc(Num("3")),
|
||||||
bumpalo::vec![in &arena; Newline].into_bump_slice(),
|
bumpalo::vec![in &arena; Newline].into_bump_slice(),
|
||||||
);
|
);
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, spaced_int),
|
Located::new(0, 0, 0, 1, spaced_int),
|
||||||
Located::new(1, 1, 0, 1, Minus),
|
Located::new(1, 1, 0, 1, Minus),
|
||||||
Located::new(1, 1, 2, 3, Int("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 \n- 4");
|
let actual = parse_with(&arena, "3 \n- 4");
|
||||||
|
@ -430,10 +430,10 @@ mod test_parse {
|
||||||
fn newline_after_mul() {
|
fn newline_after_mul() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int = arena
|
let spaced_int = arena
|
||||||
.alloc(Int("4"))
|
.alloc(Num("4"))
|
||||||
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, Int("3")),
|
Located::new(0, 0, 0, 1, Num("3")),
|
||||||
Located::new(0, 0, 3, 4, Star),
|
Located::new(0, 0, 3, 4, Star),
|
||||||
Located::new(1, 1, 2, 3, spaced_int),
|
Located::new(1, 1, 2, 3, spaced_int),
|
||||||
));
|
));
|
||||||
|
@ -447,10 +447,10 @@ mod test_parse {
|
||||||
fn newline_after_sub() {
|
fn newline_after_sub() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int = arena
|
let spaced_int = arena
|
||||||
.alloc(Int("4"))
|
.alloc(Num("4"))
|
||||||
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, Int("3")),
|
Located::new(0, 0, 0, 1, Num("3")),
|
||||||
Located::new(0, 0, 3, 4, Minus),
|
Located::new(0, 0, 3, 4, Minus),
|
||||||
Located::new(1, 1, 2, 3, spaced_int),
|
Located::new(1, 1, 2, 3, spaced_int),
|
||||||
));
|
));
|
||||||
|
@ -464,12 +464,12 @@ mod test_parse {
|
||||||
fn comment_with_unicode() {
|
fn comment_with_unicode() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int = arena
|
let spaced_int = arena
|
||||||
.alloc(Int("3"))
|
.alloc(Num("3"))
|
||||||
.after(bumpalo::vec![in &arena; LineComment(" 2 × 2")].into_bump_slice());
|
.after(bumpalo::vec![in &arena; LineComment(" 2 × 2")].into_bump_slice());
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, spaced_int),
|
Located::new(0, 0, 0, 1, spaced_int),
|
||||||
Located::new(1, 1, 0, 1, Plus),
|
Located::new(1, 1, 0, 1, Plus),
|
||||||
Located::new(1, 1, 2, 3, Int("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 # 2 × 2\n+ 4");
|
let actual = parse_with(&arena, "3 # 2 × 2\n+ 4");
|
||||||
|
@ -481,12 +481,12 @@ mod test_parse {
|
||||||
fn comment_before_op() {
|
fn comment_before_op() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int = arena
|
let spaced_int = arena
|
||||||
.alloc(Int("3"))
|
.alloc(Num("3"))
|
||||||
.after(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
|
.after(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, spaced_int),
|
Located::new(0, 0, 0, 1, spaced_int),
|
||||||
Located::new(1, 1, 0, 1, Plus),
|
Located::new(1, 1, 0, 1, Plus),
|
||||||
Located::new(1, 1, 2, 3, Int("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 # test!\n+ 4");
|
let actual = parse_with(&arena, "3 # test!\n+ 4");
|
||||||
|
@ -498,10 +498,10 @@ mod test_parse {
|
||||||
fn comment_after_op() {
|
fn comment_after_op() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int = arena
|
let spaced_int = arena
|
||||||
.alloc(Int("92"))
|
.alloc(Num("92"))
|
||||||
.before(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
|
.before(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 2, Int("12")),
|
Located::new(0, 0, 0, 2, Num("12")),
|
||||||
Located::new(0, 0, 4, 5, Star),
|
Located::new(0, 0, 4, 5, Star),
|
||||||
Located::new(1, 1, 1, 3, spaced_int),
|
Located::new(1, 1, 1, 3, spaced_int),
|
||||||
));
|
));
|
||||||
|
@ -515,10 +515,10 @@ mod test_parse {
|
||||||
fn ops_with_newlines() {
|
fn ops_with_newlines() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let spaced_int1 = arena
|
let spaced_int1 = arena
|
||||||
.alloc(Int("3"))
|
.alloc(Num("3"))
|
||||||
.after(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
.after(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
||||||
let spaced_int2 = arena
|
let spaced_int2 = arena
|
||||||
.alloc(Int("4"))
|
.alloc(Num("4"))
|
||||||
.before(bumpalo::vec![in &arena; Newline, Newline].into_bump_slice());
|
.before(bumpalo::vec![in &arena; Newline, Newline].into_bump_slice());
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 1, spaced_int1),
|
Located::new(0, 0, 0, 1, spaced_int1),
|
||||||
|
@ -559,9 +559,9 @@ mod test_parse {
|
||||||
fn minus_twelve_minus_five() {
|
fn minus_twelve_minus_five() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 3, Int("-12")),
|
Located::new(0, 0, 0, 3, Num("-12")),
|
||||||
Located::new(0, 0, 3, 4, Minus),
|
Located::new(0, 0, 3, 4, Minus),
|
||||||
Located::new(0, 0, 4, 5, Int("5")),
|
Located::new(0, 0, 4, 5, Num("5")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "-12-5");
|
let actual = parse_with(&arena, "-12-5");
|
||||||
|
@ -573,9 +573,9 @@ mod test_parse {
|
||||||
fn ten_times_eleven() {
|
fn ten_times_eleven() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let tuple = arena.alloc((
|
let tuple = arena.alloc((
|
||||||
Located::new(0, 0, 0, 2, Int("10")),
|
Located::new(0, 0, 0, 2, Num("10")),
|
||||||
Located::new(0, 0, 2, 3, Star),
|
Located::new(0, 0, 2, 3, Star),
|
||||||
Located::new(0, 0, 3, 5, Int("11")),
|
Located::new(0, 0, 3, 5, Num("11")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "10*11");
|
let actual = parse_with(&arena, "10*11");
|
||||||
|
@ -587,12 +587,12 @@ mod test_parse {
|
||||||
fn multiple_operators() {
|
fn multiple_operators() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let inner = arena.alloc((
|
let inner = arena.alloc((
|
||||||
Located::new(0, 0, 3, 5, Int("42")),
|
Located::new(0, 0, 3, 5, Num("42")),
|
||||||
Located::new(0, 0, 5, 6, Plus),
|
Located::new(0, 0, 5, 6, Plus),
|
||||||
Located::new(0, 0, 6, 9, Int("534")),
|
Located::new(0, 0, 6, 9, Num("534")),
|
||||||
));
|
));
|
||||||
let outer = arena.alloc((
|
let outer = arena.alloc((
|
||||||
Located::new(0, 0, 0, 2, Int("31")),
|
Located::new(0, 0, 0, 2, Num("31")),
|
||||||
Located::new(0, 0, 2, 3, Star),
|
Located::new(0, 0, 2, 3, Star),
|
||||||
Located::new(0, 0, 3, 9, BinOp(inner)),
|
Located::new(0, 0, 3, 9, BinOp(inner)),
|
||||||
));
|
));
|
||||||
|
@ -707,8 +707,8 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_private_tag() {
|
fn apply_private_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Num("12")));
|
||||||
let arg2 = arena.alloc(Located::new(0, 0, 9, 11, Int("34")));
|
let arg2 = arena.alloc(Located::new(0, 0, 9, 11, Num("34")));
|
||||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||||
let expected = Expr::Apply(
|
let expected = Expr::Apply(
|
||||||
arena.alloc(Located::new(0, 0, 0, 5, Expr::PrivateTag("@Whee"))),
|
arena.alloc(Located::new(0, 0, 0, 5, Expr::PrivateTag("@Whee"))),
|
||||||
|
@ -723,8 +723,8 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_global_tag() {
|
fn apply_global_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 5, 7, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 5, 7, Num("12")));
|
||||||
let arg2 = arena.alloc(Located::new(0, 0, 8, 10, Int("34")));
|
let arg2 = arena.alloc(Located::new(0, 0, 8, 10, Num("34")));
|
||||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||||
let expected = Expr::Apply(
|
let expected = Expr::Apply(
|
||||||
arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))),
|
arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))),
|
||||||
|
@ -739,8 +739,8 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_parenthetical_global_tag_args() {
|
fn apply_parenthetical_global_tag_args() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let int1 = ParensAround(arena.alloc(Int("12")));
|
let int1 = ParensAround(arena.alloc(Num("12")));
|
||||||
let int2 = ParensAround(arena.alloc(Int("34")));
|
let int2 = ParensAround(arena.alloc(Num("34")));
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, int1));
|
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, int1));
|
||||||
let arg2 = arena.alloc(Located::new(0, 0, 11, 13, int2));
|
let arg2 = arena.alloc(Located::new(0, 0, 11, 13, int2));
|
||||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||||
|
@ -780,7 +780,7 @@ mod test_parse {
|
||||||
let patterns = bumpalo::vec![in &arena; pattern];
|
let patterns = bumpalo::vec![in &arena; pattern];
|
||||||
let expected = Closure(
|
let expected = Closure(
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 10, 12, Int("42"))),
|
arena.alloc(Located::new(0, 0, 10, 12, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\Thing -> 42");
|
let actual = parse_with(&arena, "\\Thing -> 42");
|
||||||
|
|
||||||
|
@ -822,7 +822,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn packed_singleton_list() {
|
fn packed_singleton_list() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 1, 2, Int("1")))];
|
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
|
||||||
let expected = List(elems);
|
let expected = List(elems);
|
||||||
let actual = parse_with(&arena, "[1]");
|
let actual = parse_with(&arena, "[1]");
|
||||||
|
|
||||||
|
@ -832,7 +832,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn spaced_singleton_list() {
|
fn spaced_singleton_list() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 2, 3, Int("1")))];
|
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
|
||||||
let expected = List(elems);
|
let expected = List(elems);
|
||||||
let actual = parse_with(&arena, "[ 1 ]");
|
let actual = parse_with(&arena, "[ 1 ]");
|
||||||
|
|
||||||
|
@ -917,7 +917,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_apply() {
|
fn basic_apply() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg = arena.alloc(Located::new(0, 0, 5, 6, Int("1")));
|
let arg = arena.alloc(Located::new(0, 0, 5, 6, Num("1")));
|
||||||
let args = bumpalo::vec![in &arena; &*arg];
|
let args = bumpalo::vec![in &arena; &*arg];
|
||||||
let expected = Expr::Apply(
|
let expected = Expr::Apply(
|
||||||
arena.alloc(Located::new(
|
arena.alloc(Located::new(
|
||||||
|
@ -941,8 +941,8 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_two_args() {
|
fn apply_two_args() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Num("12")));
|
||||||
let arg2 = arena.alloc(Located::new(0, 0, 10, 12, Int("34")));
|
let arg2 = arena.alloc(Located::new(0, 0, 10, 12, Num("34")));
|
||||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||||
let expected = Expr::Apply(
|
let expected = Expr::Apply(
|
||||||
arena.alloc(Located::new(
|
arena.alloc(Located::new(
|
||||||
|
@ -1019,7 +1019,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn parenthetical_apply() {
|
fn parenthetical_apply() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg = arena.alloc(Located::new(0, 0, 7, 8, Int("1")));
|
let arg = arena.alloc(Located::new(0, 0, 7, 8, Num("1")));
|
||||||
let args = bumpalo::vec![in &arena; &*arg];
|
let args = bumpalo::vec![in &arena; &*arg];
|
||||||
let parens_var = Expr::ParensAround(arena.alloc(Var {
|
let parens_var = Expr::ParensAround(arena.alloc(Var {
|
||||||
module_name: "",
|
module_name: "",
|
||||||
|
@ -1080,7 +1080,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_unary_negation() {
|
fn apply_unary_negation() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 7, 9, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 7, 9, Num("12")));
|
||||||
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Negate);
|
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Negate);
|
||||||
let arg2 = arena.alloc(Located::new(
|
let arg2 = arena.alloc(Located::new(
|
||||||
0,
|
0,
|
||||||
|
@ -1116,7 +1116,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_unary_not() {
|
fn apply_unary_not() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 7, 9, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 7, 9, Num("12")));
|
||||||
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Not);
|
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Not);
|
||||||
let arg2 = arena.alloc(Located::new(
|
let arg2 = arena.alloc(Located::new(
|
||||||
0,
|
0,
|
||||||
|
@ -1152,7 +1152,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn unary_negation_with_parens() {
|
fn unary_negation_with_parens() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 8, 10, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 8, 10, Num("12")));
|
||||||
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Negate);
|
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Negate);
|
||||||
let arg2 = arena.alloc(Located::new(
|
let arg2 = arena.alloc(Located::new(
|
||||||
0,
|
0,
|
||||||
|
@ -1188,7 +1188,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn unary_not_with_parens() {
|
fn unary_not_with_parens() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 8, 10, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 8, 10, Num("12")));
|
||||||
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Not);
|
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Not);
|
||||||
let arg2 = arena.alloc(Located::new(
|
let arg2 = arena.alloc(Located::new(
|
||||||
0,
|
0,
|
||||||
|
@ -1224,7 +1224,7 @@ mod test_parse {
|
||||||
#[test]
|
#[test]
|
||||||
fn unary_negation_arg() {
|
fn unary_negation_arg() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Int("12")));
|
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Num("12")));
|
||||||
let loc_op = Located::new(0, 0, 9, 10, UnaryOp::Negate);
|
let loc_op = Located::new(0, 0, 9, 10, UnaryOp::Negate);
|
||||||
let var1 = Var {
|
let var1 = Var {
|
||||||
module_name: "",
|
module_name: "",
|
||||||
|
@ -1257,7 +1257,7 @@ mod test_parse {
|
||||||
let patterns = bumpalo::vec![in &arena; pattern];
|
let patterns = bumpalo::vec![in &arena; pattern];
|
||||||
let expected = Closure(
|
let expected = Closure(
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 6, 8, Int("42"))),
|
arena.alloc(Located::new(0, 0, 6, 8, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\a -> 42");
|
let actual = parse_with(&arena, "\\a -> 42");
|
||||||
|
|
||||||
|
@ -1271,7 +1271,7 @@ mod test_parse {
|
||||||
let patterns = bumpalo::vec![in &arena; pattern];
|
let patterns = bumpalo::vec![in &arena; pattern];
|
||||||
let expected = Closure(
|
let expected = Closure(
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 6, 8, Int("42"))),
|
arena.alloc(Located::new(0, 0, 6, 8, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\_ -> 42");
|
let actual = parse_with(&arena, "\\_ -> 42");
|
||||||
|
|
||||||
|
@ -1297,7 +1297,7 @@ mod test_parse {
|
||||||
let patterns = bumpalo::vec![in &arena; arg1, arg2];
|
let patterns = bumpalo::vec![in &arena; arg1, arg2];
|
||||||
let expected = Closure(
|
let expected = Closure(
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 9, 11, Int("42"))),
|
arena.alloc(Located::new(0, 0, 9, 11, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\a, b -> 42");
|
let actual = parse_with(&arena, "\\a, b -> 42");
|
||||||
|
|
||||||
|
@ -1313,7 +1313,7 @@ mod test_parse {
|
||||||
let patterns = bumpalo::vec![in &arena; arg1, arg2, arg3];
|
let patterns = bumpalo::vec![in &arena; arg1, arg2, arg3];
|
||||||
let expected = Closure(
|
let expected = Closure(
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 12, 14, Int("42"))),
|
arena.alloc(Located::new(0, 0, 12, 14, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\a, b, c -> 42");
|
let actual = parse_with(&arena, "\\a, b, c -> 42");
|
||||||
|
|
||||||
|
@ -1328,7 +1328,7 @@ mod test_parse {
|
||||||
let patterns = bumpalo::vec![in &arena; underscore1, underscore2];
|
let patterns = bumpalo::vec![in &arena; underscore1, underscore2];
|
||||||
let expected = Closure(
|
let expected = Closure(
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 9, 11, Int("42"))),
|
arena.alloc(Located::new(0, 0, 9, 11, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\_, _ -> 42");
|
let actual = parse_with(&arena, "\\_, _ -> 42");
|
||||||
|
|
||||||
|
@ -1343,11 +1343,11 @@ mod test_parse {
|
||||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||||
let def = Def::Body(
|
let def = Def::Body(
|
||||||
arena.alloc(Located::new(1, 1, 0, 1, Identifier("x"))),
|
arena.alloc(Located::new(1, 1, 0, 1, Identifier("x"))),
|
||||||
arena.alloc(Located::new(1, 1, 2, 3, Int("5"))),
|
arena.alloc(Located::new(1, 1, 2, 3, Num("5"))),
|
||||||
);
|
);
|
||||||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def));
|
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def));
|
||||||
let defs = bumpalo::vec![in &arena; loc_def];
|
let defs = bumpalo::vec![in &arena; loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||||
let expected = Expr::SpaceBefore(
|
let expected = Expr::SpaceBefore(
|
||||||
|
@ -1373,11 +1373,11 @@ mod test_parse {
|
||||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||||
let def = Def::Body(
|
let def = Def::Body(
|
||||||
arena.alloc(Located::new(1, 1, 0, 1, Identifier("x"))),
|
arena.alloc(Located::new(1, 1, 0, 1, Identifier("x"))),
|
||||||
arena.alloc(Located::new(1, 1, 4, 5, Int("5"))),
|
arena.alloc(Located::new(1, 1, 4, 5, Num("5"))),
|
||||||
);
|
);
|
||||||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def));
|
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def));
|
||||||
let defs = bumpalo::vec![in &arena; loc_def];
|
let defs = bumpalo::vec![in &arena; loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||||
let expected = Expr::SpaceBefore(
|
let expected = Expr::SpaceBefore(
|
||||||
|
@ -1404,13 +1404,13 @@ mod test_parse {
|
||||||
let newline = bumpalo::vec![in &arena; Newline];
|
let newline = bumpalo::vec![in &arena; Newline];
|
||||||
let def1 = Def::Body(
|
let def1 = Def::Body(
|
||||||
arena.alloc(Located::new(1, 1, 0, 1, Identifier("x"))),
|
arena.alloc(Located::new(1, 1, 0, 1, Identifier("x"))),
|
||||||
arena.alloc(Located::new(1, 1, 4, 5, Int("5"))),
|
arena.alloc(Located::new(1, 1, 4, 5, Num("5"))),
|
||||||
);
|
);
|
||||||
let loc_def1 = &*arena.alloc(Located::new(1, 1, 0, 1, def1));
|
let loc_def1 = &*arena.alloc(Located::new(1, 1, 0, 1, def1));
|
||||||
let def2 = Def::SpaceBefore(
|
let def2 = Def::SpaceBefore(
|
||||||
&*arena.alloc(Def::Body(
|
&*arena.alloc(Def::Body(
|
||||||
arena.alloc(Located::new(2, 2, 0, 1, Identifier("y"))),
|
arena.alloc(Located::new(2, 2, 0, 1, Identifier("y"))),
|
||||||
arena.alloc(Located::new(2, 2, 4, 5, Int("6"))),
|
arena.alloc(Located::new(2, 2, 4, 5, Num("6"))),
|
||||||
)),
|
)),
|
||||||
newline.into_bump_slice(),
|
newline.into_bump_slice(),
|
||||||
);
|
);
|
||||||
|
@ -1419,7 +1419,7 @@ mod test_parse {
|
||||||
// gets added by .push(), since that's more efficient and since
|
// gets added by .push(), since that's more efficient and since
|
||||||
// canonicalization is going to re-sort these all anyway.)
|
// canonicalization is going to re-sort these all anyway.)
|
||||||
let defs = bumpalo::vec![in &arena; loc_def2, loc_def1];
|
let defs = bumpalo::vec![in &arena; loc_def2, loc_def1];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
||||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||||
let expected = Expr::SpaceBefore(
|
let expected = Expr::SpaceBefore(
|
||||||
|
@ -1457,13 +1457,13 @@ mod test_parse {
|
||||||
8,
|
8,
|
||||||
RecordDestructure(fields.into_bump_slice()),
|
RecordDestructure(fields.into_bump_slice()),
|
||||||
)),
|
)),
|
||||||
arena.alloc(Located::new(1, 1, 11, 12, Int("5"))),
|
arena.alloc(Located::new(1, 1, 11, 12, Num("5"))),
|
||||||
);
|
);
|
||||||
let loc_def1 = &*arena.alloc(Located::new(1, 1, 1, 8, def1));
|
let loc_def1 = &*arena.alloc(Located::new(1, 1, 1, 8, def1));
|
||||||
let def2 = Def::SpaceBefore(
|
let def2 = Def::SpaceBefore(
|
||||||
&*arena.alloc(Def::Body(
|
&*arena.alloc(Def::Body(
|
||||||
arena.alloc(Located::new(2, 2, 0, 1, Identifier("y"))),
|
arena.alloc(Located::new(2, 2, 0, 1, Identifier("y"))),
|
||||||
arena.alloc(Located::new(2, 2, 4, 5, Int("6"))),
|
arena.alloc(Located::new(2, 2, 4, 5, Num("6"))),
|
||||||
)),
|
)),
|
||||||
newline.into_bump_slice(),
|
newline.into_bump_slice(),
|
||||||
);
|
);
|
||||||
|
@ -1472,7 +1472,7 @@ mod test_parse {
|
||||||
// gets added by .push(), since that's more efficient and since
|
// gets added by .push(), since that's more efficient and since
|
||||||
// canonicalization is going to re-sort these all anyway.)
|
// canonicalization is going to re-sort these all anyway.)
|
||||||
let defs = bumpalo::vec![in &arena; loc_def2, loc_def1];
|
let defs = bumpalo::vec![in &arena; loc_def2, loc_def1];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
||||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||||
let expected = Expr::SpaceBefore(
|
let expected = Expr::SpaceBefore(
|
||||||
|
@ -1505,14 +1505,14 @@ mod test_parse {
|
||||||
);
|
);
|
||||||
let def = Def::Body(
|
let def = Def::Body(
|
||||||
arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
||||||
arena.alloc(Located::new(1, 1, 6, 7, Int("4"))),
|
arena.alloc(Located::new(1, 1, 6, 7, Num("4"))),
|
||||||
);
|
);
|
||||||
let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
||||||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 7, spaced_def));
|
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 7, spaced_def));
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1552,7 +1552,7 @@ mod test_parse {
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann];
|
let defs = bumpalo::vec![in &arena; loc_ann];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(2, 2, 0, 2, ret);
|
let loc_ret = Located::new(2, 2, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1588,7 +1588,7 @@ mod test_parse {
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 4, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 4, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann];
|
let defs = bumpalo::vec![in &arena; loc_ann];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(2, 2, 0, 2, ret);
|
let loc_ret = Located::new(2, 2, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1630,7 +1630,7 @@ mod test_parse {
|
||||||
Located::new(1,1,7,8, Identifier("x")),
|
Located::new(1,1,7,8, Identifier("x")),
|
||||||
Located::new(1,1,10,11, Underscore)
|
Located::new(1,1,10,11, Underscore)
|
||||||
];
|
];
|
||||||
let body = Located::new(1, 1, 15, 17, Int("42"));
|
let body = Located::new(1, 1, 15, 17, Num("42"));
|
||||||
|
|
||||||
let closure = Expr::Closure(&args, &body);
|
let closure = Expr::Closure(&args, &body);
|
||||||
|
|
||||||
|
@ -1643,7 +1643,7 @@ mod test_parse {
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1698,7 +1698,7 @@ mod test_parse {
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1751,7 +1751,7 @@ mod test_parse {
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1805,7 +1805,7 @@ mod test_parse {
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1858,7 +1858,7 @@ mod test_parse {
|
||||||
|
|
||||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
@ -1884,7 +1884,7 @@ mod test_parse {
|
||||||
let pattern1 =
|
let pattern1 =
|
||||||
Pattern::SpaceBefore(arena.alloc(StrLiteral("blah")), newlines.into_bump_slice());
|
Pattern::SpaceBefore(arena.alloc(StrLiteral("blah")), newlines.into_bump_slice());
|
||||||
let loc_pattern1 = Located::new(1, 1, 1, 7, pattern1);
|
let loc_pattern1 = Located::new(1, 1, 1, 7, pattern1);
|
||||||
let expr1 = Int("1");
|
let expr1 = Num("1");
|
||||||
let loc_expr1 = Located::new(1, 1, 11, 12, expr1);
|
let loc_expr1 = Located::new(1, 1, 11, 12, expr1);
|
||||||
let branch1 = &*arena.alloc(WhenBranch {
|
let branch1 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
||||||
|
@ -1895,7 +1895,7 @@ mod test_parse {
|
||||||
let pattern2 =
|
let pattern2 =
|
||||||
Pattern::SpaceBefore(arena.alloc(StrLiteral("mise")), newlines.into_bump_slice());
|
Pattern::SpaceBefore(arena.alloc(StrLiteral("mise")), newlines.into_bump_slice());
|
||||||
let loc_pattern2 = Located::new(2, 2, 1, 7, pattern2);
|
let loc_pattern2 = Located::new(2, 2, 1, 7, pattern2);
|
||||||
let expr2 = Int("2");
|
let expr2 = Num("2");
|
||||||
let loc_expr2 = Located::new(2, 2, 11, 12, expr2);
|
let loc_expr2 = Located::new(2, 2, 11, 12, expr2);
|
||||||
let branch2 = &*arena.alloc(WhenBranch {
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
||||||
|
@ -1928,9 +1928,9 @@ mod test_parse {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let newlines = bumpalo::vec![in &arena; Newline];
|
let newlines = bumpalo::vec![in &arena; Newline];
|
||||||
let pattern1 =
|
let pattern1 =
|
||||||
Pattern::SpaceBefore(arena.alloc(IntLiteral("1")), newlines.into_bump_slice());
|
Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines.into_bump_slice());
|
||||||
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
|
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
|
||||||
let expr1 = Int("2");
|
let expr1 = Num("2");
|
||||||
let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
|
let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
|
||||||
let branch1 = &*arena.alloc(WhenBranch {
|
let branch1 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
||||||
|
@ -1939,9 +1939,9 @@ mod test_parse {
|
||||||
});
|
});
|
||||||
let newlines = bumpalo::vec![in &arena; Newline];
|
let newlines = bumpalo::vec![in &arena; Newline];
|
||||||
let pattern2 =
|
let pattern2 =
|
||||||
Pattern::SpaceBefore(arena.alloc(IntLiteral("3")), newlines.into_bump_slice());
|
Pattern::SpaceBefore(arena.alloc(NumLiteral("3")), newlines.into_bump_slice());
|
||||||
let loc_pattern2 = Located::new(2, 2, 1, 2, pattern2);
|
let loc_pattern2 = Located::new(2, 2, 1, 2, pattern2);
|
||||||
let expr2 = Int("4");
|
let expr2 = Num("4");
|
||||||
let loc_expr2 = Located::new(2, 2, 6, 7, expr2);
|
let loc_expr2 = Located::new(2, 2, 6, 7, expr2);
|
||||||
let branch2 = &*arena.alloc(WhenBranch {
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern2],
|
patterns: bumpalo::vec![in &arena;loc_pattern2],
|
||||||
|
@ -1979,7 +1979,7 @@ mod test_parse {
|
||||||
newlines.into_bump_slice(),
|
newlines.into_bump_slice(),
|
||||||
);
|
);
|
||||||
let loc_pattern1 = Located::new(1, 1, 1, 6, pattern1);
|
let loc_pattern1 = Located::new(1, 1, 1, 6, pattern1);
|
||||||
let expr1 = Int("2");
|
let expr1 = Num("2");
|
||||||
let loc_expr1 = Located::new(1, 1, 10, 11, expr1);
|
let loc_expr1 = Located::new(1, 1, 10, 11, expr1);
|
||||||
let branch1 = &*arena.alloc(WhenBranch {
|
let branch1 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern1 ],
|
patterns: bumpalo::vec![in &arena;loc_pattern1 ],
|
||||||
|
@ -1993,7 +1993,7 @@ mod test_parse {
|
||||||
newlines.into_bump_slice(),
|
newlines.into_bump_slice(),
|
||||||
);
|
);
|
||||||
let loc_pattern2 = Located::new(2, 2, 1, 9, pattern2);
|
let loc_pattern2 = Located::new(2, 2, 1, 9, pattern2);
|
||||||
let expr2 = Int("4");
|
let expr2 = Num("4");
|
||||||
let loc_expr2 = Located::new(2, 2, 13, 14, expr2);
|
let loc_expr2 = Located::new(2, 2, 13, 14, expr2);
|
||||||
let branch2 = &*arena.alloc(WhenBranch {
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
||||||
|
@ -2030,7 +2030,7 @@ mod test_parse {
|
||||||
let pattern1_alt = StrLiteral("blop");
|
let pattern1_alt = StrLiteral("blop");
|
||||||
let loc_pattern1 = Located::new(1, 1, 1, 7, pattern1);
|
let loc_pattern1 = Located::new(1, 1, 1, 7, pattern1);
|
||||||
let loc_pattern1_alt = Located::new(1, 1, 10, 16, pattern1_alt);
|
let loc_pattern1_alt = Located::new(1, 1, 10, 16, pattern1_alt);
|
||||||
let expr1 = Int("1");
|
let expr1 = Num("1");
|
||||||
let loc_expr1 = Located::new(1, 1, 20, 21, expr1);
|
let loc_expr1 = Located::new(1, 1, 20, 21, expr1);
|
||||||
let branch1 = &*arena.alloc(WhenBranch {
|
let branch1 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern1, loc_pattern1_alt],
|
patterns: bumpalo::vec![in &arena;loc_pattern1, loc_pattern1_alt],
|
||||||
|
@ -2045,7 +2045,7 @@ mod test_parse {
|
||||||
Pattern::SpaceBefore(arena.alloc(StrLiteral("bar")), newlines.into_bump_slice());
|
Pattern::SpaceBefore(arena.alloc(StrLiteral("bar")), newlines.into_bump_slice());
|
||||||
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
|
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
|
||||||
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt);
|
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt);
|
||||||
let expr2 = Int("2");
|
let expr2 = Num("2");
|
||||||
let loc_expr2 = Located::new(3, 3, 10, 11, expr2);
|
let loc_expr2 = Located::new(3, 3, 10, 11, expr2);
|
||||||
let branch2 = &*arena.alloc(WhenBranch {
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
patterns: bumpalo::vec![in &arena;loc_pattern2, loc_pattern2_alt],
|
patterns: bumpalo::vec![in &arena;loc_pattern2, loc_pattern2_alt],
|
||||||
|
@ -2148,7 +2148,7 @@ mod test_parse {
|
||||||
let def1 = SpaceAfter(
|
let def1 = SpaceAfter(
|
||||||
arena.alloc(Body(
|
arena.alloc(Body(
|
||||||
arena.alloc(Located::new(0, 0, 0, 3, pattern1)),
|
arena.alloc(Located::new(0, 0, 0, 3, pattern1)),
|
||||||
arena.alloc(Located::new(0, 0, 6, 7, Int("1"))),
|
arena.alloc(Located::new(0, 0, 6, 7, Num("1"))),
|
||||||
)),
|
)),
|
||||||
newlines1.into_bump_slice(),
|
newlines1.into_bump_slice(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -89,6 +89,11 @@ impl<T> Located<T> {
|
||||||
pub fn at(region: Region, value: T) -> Located<T> {
|
pub fn at(region: Region, value: T) -> Located<T> {
|
||||||
Located { value, region }
|
Located { value, region }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn at_zero(value: T) -> Located<T> {
|
||||||
|
let region = Region::zero();
|
||||||
|
Located { value, region }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Located<T> {
|
impl<T> Located<T> {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, MutMap, SendMap, SendSet};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import};
|
use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
|
@ -350,108 +350,3 @@ pub fn fixtures_dir<'a>() -> PathBuf {
|
||||||
pub fn builtins_dir<'a>() -> PathBuf {
|
pub fn builtins_dir<'a>() -> PathBuf {
|
||||||
PathBuf::new().join("builtins")
|
PathBuf::new().join("builtins")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check constraints
|
|
||||||
//
|
|
||||||
// Keep track of the used (in types or expectations) variables, and the declared variables (in
|
|
||||||
// flex_vars or rigid_vars fields of LetConstraint. These roc_collections should match: no duplicates
|
|
||||||
// and no variables that are used but not declared are allowed.
|
|
||||||
//
|
|
||||||
// There is one exception: the initial variable (that stores the type of the whole expression) is
|
|
||||||
// never declared, but is used.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn assert_correct_variable_usage(constraint: &Constraint) {
|
|
||||||
// variables declared in constraint (flex_vars or rigid_vars)
|
|
||||||
// and variables actually used in constraints
|
|
||||||
let (declared, used) = variable_usage(constraint);
|
|
||||||
|
|
||||||
let used: ImSet<Variable> = used.clone().into();
|
|
||||||
let mut decl: ImSet<Variable> = declared.rigid_vars.clone().into();
|
|
||||||
|
|
||||||
for var in declared.flex_vars.clone() {
|
|
||||||
decl.insert(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff = used.clone().relative_complement(decl);
|
|
||||||
|
|
||||||
// NOTE: this checks whether we're using variables that are not declared. For recursive type
|
|
||||||
// definitions, their rigid types are declared twice, which is correct!
|
|
||||||
if !diff.is_empty() {
|
|
||||||
println!("VARIABLE USAGE PROBLEM");
|
|
||||||
|
|
||||||
println!("used: {:?}", &used);
|
|
||||||
println!("rigids: {:?}", &declared.rigid_vars);
|
|
||||||
println!("flexs: {:?}", &declared.flex_vars);
|
|
||||||
|
|
||||||
println!("difference: {:?}", &diff);
|
|
||||||
|
|
||||||
panic!("variable usage problem (see stdout for details)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SeenVariables {
|
|
||||||
pub rigid_vars: Vec<Variable>,
|
|
||||||
pub flex_vars: Vec<Variable>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec<Variable>) {
|
|
||||||
let mut declared = SeenVariables::default();
|
|
||||||
let mut used = ImSet::default();
|
|
||||||
variable_usage_help(con, &mut declared, &mut used);
|
|
||||||
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(1) });
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(2) });
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(3) });
|
|
||||||
|
|
||||||
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
|
||||||
used_vec.sort();
|
|
||||||
|
|
||||||
declared.rigid_vars.sort();
|
|
||||||
declared.flex_vars.sort();
|
|
||||||
|
|
||||||
(declared, used_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mut ImSet<Variable>) {
|
|
||||||
use Constraint::*;
|
|
||||||
|
|
||||||
match con {
|
|
||||||
True | SaveTheEnvironment => (),
|
|
||||||
Eq(tipe, expectation, _) => {
|
|
||||||
for v in tipe.variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in expectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Lookup(_, expectation, _) => {
|
|
||||||
for v in expectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pattern(_, _, tipe, pexpectation) => {
|
|
||||||
for v in tipe.variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in pexpectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Let(letcon) => {
|
|
||||||
declared.rigid_vars.extend(letcon.rigid_vars.clone());
|
|
||||||
declared.flex_vars.extend(letcon.flex_vars.clone());
|
|
||||||
|
|
||||||
variable_usage_help(&letcon.defs_constraint, declared, used);
|
|
||||||
variable_usage_help(&letcon.ret_constraint, declared, used);
|
|
||||||
}
|
|
||||||
And(constraints) => {
|
|
||||||
for sub in constraints {
|
|
||||||
variable_usage_help(sub, declared, used);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ mod test_report {
|
||||||
use roc_types::types;
|
use roc_types::types;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
// use roc_region::all;
|
// use roc_region::all;
|
||||||
use crate::helpers::{assert_correct_variable_usage, can_expr, infer_expr, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
||||||
use roc_reporting::report::ReportText::{EmText, Plain, Region, Type, Url, Value};
|
use roc_reporting::report::ReportText::{EmText, Plain, Region, Type, Url, Value};
|
||||||
use roc_types::subs::Content::{FlexVar, RigidVar, Structure};
|
use roc_types::subs::Content::{FlexVar, RigidVar, Structure};
|
||||||
use roc_types::subs::FlatType::EmptyRecord;
|
use roc_types::subs::FlatType::EmptyRecord;
|
||||||
|
@ -53,8 +53,6 @@ mod test_report {
|
||||||
} = can_expr(expr_src);
|
} = can_expr(expr_src);
|
||||||
let mut subs = Subs::new(var_store.into());
|
let mut subs = Subs::new(var_store.into());
|
||||||
|
|
||||||
assert_correct_variable_usage(&constraint);
|
|
||||||
|
|
||||||
for (var, name) in output.introduced_variables.name_by_var {
|
for (var, name) in output.introduced_variables.name_by_var {
|
||||||
subs.rigid_var(var, name);
|
subs.rigid_var(var, name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -546,6 +546,7 @@ fn type_to_variable(
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
|
||||||
Alias(symbol, args, alias_type) => {
|
Alias(symbol, args, alias_type) => {
|
||||||
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
||||||
// different variables (once for each occurence). The recursion restriction is required
|
// different variables (once for each occurence). The recursion restriction is required
|
||||||
|
@ -560,6 +561,7 @@ fn type_to_variable(
|
||||||
//
|
//
|
||||||
// This `u` variable can be different between lists, so giving just one variable to
|
// This `u` variable can be different between lists, so giving just one variable to
|
||||||
// this type is incorrect.
|
// this type is incorrect.
|
||||||
|
// TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
|
||||||
let is_recursive = alias_type.is_recursive();
|
let is_recursive = alias_type.is_recursive();
|
||||||
let no_args = args.is_empty();
|
let no_args = args.is_empty();
|
||||||
if no_args && !is_recursive {
|
if no_args && !is_recursive {
|
||||||
|
|
|
@ -400,9 +400,10 @@ pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec<Variable>) {
|
||||||
let mut used = ImSet::default();
|
let mut used = ImSet::default();
|
||||||
variable_usage_help(con, &mut declared, &mut used);
|
variable_usage_help(con, &mut declared, &mut used);
|
||||||
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(1) });
|
// ..= because there is an extra undeclared variable that contains the type of the full expression
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(2) });
|
for i in 0..=Variable::RESERVED {
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(3) });
|
used.remove(unsafe { &Variable::unsafe_test_debug_variable(i as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
||||||
used_vec.sort();
|
used_vec.sort();
|
||||||
|
|
|
@ -69,7 +69,7 @@ mod test_solve {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn int_literal() {
|
fn int_literal() {
|
||||||
infer_eq("5", "Int");
|
infer_eq("5", "Num *");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -188,7 +188,7 @@ mod test_solve {
|
||||||
[42]
|
[42]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List Int",
|
"List (Num *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ mod test_solve {
|
||||||
[[[ 5 ]]]
|
[[[ 5 ]]]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (List (List Int))",
|
"List (List (List (Num *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ mod test_solve {
|
||||||
[ 1, 2, 3 ]
|
[ 1, 2, 3 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List Int",
|
"List (Num *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ mod test_solve {
|
||||||
[ [ 1 ], [ 2, 3 ] ]
|
[ [ 1 ], [ 2, 3 ] ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (List Int)",
|
"List (List (Num *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +340,7 @@ mod test_solve {
|
||||||
\_, _ -> 42
|
\_, _ -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"*, * -> Int",
|
"*, * -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ mod test_solve {
|
||||||
func
|
func
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"*, * -> Int",
|
"*, * -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +474,7 @@ mod test_solve {
|
||||||
c
|
c
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,7 +509,7 @@ mod test_solve {
|
||||||
alwaysFive "stuff"
|
alwaysFive "stuff"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +556,7 @@ mod test_solve {
|
||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +570,7 @@ mod test_solve {
|
||||||
enlist 5
|
enlist 5
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List Int",
|
"List (Num *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +597,7 @@ mod test_solve {
|
||||||
1 |> (\a -> a)
|
1 |> (\a -> a)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,7 +611,7 @@ mod test_solve {
|
||||||
1 |> always "foo"
|
1 |> always "foo"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +676,7 @@ mod test_solve {
|
||||||
apply identity 5
|
apply identity 5
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@ mod test_solve {
|
||||||
// flip neverendingInt
|
// flip neverendingInt
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "(Int, (a -> a)) -> Int",
|
// "(Num *, (a -> a)) -> Num *",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -779,7 +779,7 @@ mod test_solve {
|
||||||
// 1 // 2
|
// 1 // 2
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "Int",
|
// "Num *",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -791,7 +791,7 @@ mod test_solve {
|
||||||
// 1 + 2
|
// 1 + 2
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "Int",
|
// "Num *",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -835,12 +835,12 @@ mod test_solve {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
alwaysFive = \_ -> 5
|
alwaysFive = \_ -> 5
|
||||||
|
|
||||||
[ alwaysFive "foo", alwaysFive [] ]
|
[ alwaysFive "foo", alwaysFive [] ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List Int",
|
"List (Num *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -855,7 +855,7 @@ mod test_solve {
|
||||||
24
|
24
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,7 +869,7 @@ mod test_solve {
|
||||||
3 -> 4
|
3 -> 4
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,17 +882,17 @@ mod test_solve {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_field_record() {
|
fn one_field_record() {
|
||||||
infer_eq("{ x: 5 }", "{ x : Int }");
|
infer_eq("{ x: 5 }", "{ x : Num * }");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn two_field_record() {
|
fn two_field_record() {
|
||||||
infer_eq("{ x: 5, y : 3.14 }", "{ x : Int, y : Float }");
|
infer_eq("{ x: 5, y : 3.14 }", "{ x : Num *, y : Float }");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record_literal_accessor() {
|
fn record_literal_accessor() {
|
||||||
infer_eq("{ x: 5, y : 3.14 }.x", "Int");
|
infer_eq("{ x: 5, y : 3.14 }.x", "Num *");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -951,7 +951,7 @@ mod test_solve {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
foo : Int -> custom
|
foo : Num * -> custom
|
||||||
|
|
||||||
foo 2
|
foo 2
|
||||||
"#
|
"#
|
||||||
|
@ -1017,9 +1017,9 @@ mod test_solve {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
user = { year: "foo", name: "Sam" }
|
user = { year: "foo", name: "Sam" }
|
||||||
|
|
||||||
{ user & year: "foo" }
|
{ user & year: "foo" }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ name : Str, year : Str }",
|
"{ name : Str, year : Str }",
|
||||||
|
@ -1030,7 +1030,8 @@ mod test_solve {
|
||||||
fn bare_tag() {
|
fn bare_tag() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"Foo
|
r#"
|
||||||
|
Foo
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo ]*",
|
"[ Foo ]*",
|
||||||
|
@ -1041,10 +1042,11 @@ mod test_solve {
|
||||||
fn single_tag_pattern() {
|
fn single_tag_pattern() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"\Foo -> 42
|
r#"
|
||||||
|
\Foo -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo ]* -> Int",
|
"[ Foo ]* -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1052,10 +1054,11 @@ mod test_solve {
|
||||||
fn single_private_tag_pattern() {
|
fn single_private_tag_pattern() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"\@Foo -> 42
|
r#"
|
||||||
|
\@Foo -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ @Foo ]* -> Int",
|
"[ @Foo ]* -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1063,13 +1066,14 @@ mod test_solve {
|
||||||
fn two_tag_pattern() {
|
fn two_tag_pattern() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"\x ->
|
r#"
|
||||||
when x is
|
\x ->
|
||||||
True -> 1
|
when x is
|
||||||
False -> 0
|
True -> 1
|
||||||
|
False -> 0
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ False, True ]* -> Int",
|
"[ False, True ]* -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,10 +1081,11 @@ mod test_solve {
|
||||||
fn tag_application() {
|
fn tag_application() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"Foo "happy" 2020
|
r#"
|
||||||
|
Foo "happy" 2020
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo Str Int ]*",
|
"[ Foo Str (Num *) ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1088,10 +1093,11 @@ mod test_solve {
|
||||||
fn private_tag_application() {
|
fn private_tag_application() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"@Foo "happy" 2020
|
r#"
|
||||||
|
@Foo "happy" 2020
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ @Foo Str Int ]*",
|
"[ @Foo Str (Num *) ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,7 +1126,7 @@ mod test_solve {
|
||||||
{ x: 4 } -> x
|
{ x: 4 } -> x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1129,7 +1135,7 @@ mod test_solve {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
\Foo x -> Foo x
|
\Foo x -> Foo x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo a ]* -> [ Foo a ]*",
|
"[ Foo a ]* -> [ Foo a ]*",
|
||||||
|
@ -1141,7 +1147,7 @@ mod test_solve {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
\Foo x _ -> Foo x "y"
|
\Foo x _ -> Foo x "y"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo a * ]* -> [ Foo a Str ]*",
|
"[ Foo a * ]* -> [ Foo a Str ]*",
|
||||||
|
@ -1464,9 +1470,9 @@ mod test_solve {
|
||||||
y = numIdentity 3.14
|
y = numIdentity 3.14
|
||||||
|
|
||||||
{ numIdentity, x : numIdentity 42, y }
|
{ numIdentity, x : numIdentity 42, y }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ numIdentity : Num a -> Num a, x : Int, y : Float }",
|
"{ numIdentity : Num a -> Num a, x : Num a, y : Float }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1482,7 +1488,7 @@ mod test_solve {
|
||||||
_ -> 5
|
_ -> 5
|
||||||
|
|
||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Int",
|
||||||
);
|
);
|
||||||
|
@ -1494,15 +1500,15 @@ mod test_solve {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
f = \n ->
|
f = \n ->
|
||||||
when n is
|
when n is
|
||||||
0 -> 0
|
0 -> 0
|
||||||
_ -> f n
|
_ -> f n
|
||||||
|
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int -> Int",
|
"Num * -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1511,12 +1517,12 @@ mod test_solve {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
map : (a -> b), [ Identity a ] -> [ Identity b ]
|
map : (a -> b), [ Identity a ] -> [ Identity b ]
|
||||||
map = \f, identity ->
|
map = \f, identity ->
|
||||||
when identity is
|
when identity is
|
||||||
Identity v -> Identity (f v)
|
Identity v -> Identity (f v)
|
||||||
map
|
map
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"(a -> b), [ Identity a ] -> [ Identity b ]",
|
"(a -> b), [ Identity a ] -> [ Identity b ]",
|
||||||
);
|
);
|
||||||
|
@ -1527,16 +1533,15 @@ mod test_solve {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
# toBit : [ False, True ] -> Num.Num Int.Integer
|
|
||||||
toBit = \bool ->
|
toBit = \bool ->
|
||||||
when bool is
|
when bool is
|
||||||
True -> 1
|
True -> 1
|
||||||
False -> 0
|
False -> 0
|
||||||
|
|
||||||
toBit
|
toBit
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ False, True ]* -> Int",
|
"[ False, True ]* -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1571,9 +1576,9 @@ mod test_solve {
|
||||||
_ -> True
|
_ -> True
|
||||||
|
|
||||||
fromBit
|
fromBit
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int -> [ False, True ]*",
|
"Num * -> [ False, True ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1589,7 +1594,7 @@ mod test_solve {
|
||||||
Err e -> Err e
|
Err e -> Err e
|
||||||
|
|
||||||
map
|
map
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"(a -> b), [ Err e, Ok a ] -> [ Err e, Ok b ]",
|
"(a -> b), [ Err e, Ok a ] -> [ Err e, Ok b ]",
|
||||||
);
|
);
|
||||||
|
@ -1620,12 +1625,12 @@ mod test_solve {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
foo = \{ x } -> x
|
foo = \{ x } -> x
|
||||||
|
|
||||||
foo { x: 5 }
|
foo { x: 5 }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2288,7 +2293,7 @@ mod test_solve {
|
||||||
List.get [ 10, 9, 8, 7 ] 1
|
List.get [ 10, 9, 8, 7 ] 1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Result Int [ IndexOutOfBounds ]*",
|
"Result (Num *) [ IndexOutOfBounds ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2370,7 +2375,7 @@ mod test_solve {
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ p : *, q : * }* -> Int",
|
"{ p : *, q : * }* -> Num *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ mod test_uniq_solve {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn int_literal() {
|
fn int_literal() {
|
||||||
infer_eq("5", "Attr * Int");
|
infer_eq("5", "Attr * (Num (Attr * *))");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -176,7 +176,7 @@ mod test_uniq_solve {
|
||||||
[42]
|
[42]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (List (Attr * Int))",
|
"Attr * (List (Attr * (Num (Attr * *))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ mod test_uniq_solve {
|
||||||
[[[ 5 ]]]
|
[[[ 5 ]]]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (List (Attr * (List (Attr * (List (Attr * Int))))))",
|
"Attr * (List (Attr * (List (Attr * (List (Attr * (Num (Attr * *))))))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ mod test_uniq_solve {
|
||||||
[ 1, 2, 3 ]
|
[ 1, 2, 3 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (List (Attr * Int))",
|
"Attr * (List (Attr * (Num (Attr * *))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ mod test_uniq_solve {
|
||||||
[ [ 1 ], [ 2, 3 ] ]
|
[ [ 1 ], [ 2, 3 ] ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (List (Attr * (List (Attr * Int))))",
|
"Attr * (List (Attr * (List (Attr * (Num (Attr * *))))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +328,7 @@ mod test_uniq_solve {
|
||||||
\_, _ -> 42
|
\_, _ -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (*, * -> Attr * Int)",
|
"Attr * (*, * -> Attr * (Num (Attr * *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,7 +398,7 @@ mod test_uniq_solve {
|
||||||
func
|
func
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (*, * -> Attr * Int)",
|
"Attr * (*, * -> Attr * (Num (Attr * *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ mod test_uniq_solve {
|
||||||
c
|
c
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,7 +498,7 @@ mod test_uniq_solve {
|
||||||
alwaysFive "stuff"
|
alwaysFive "stuff"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +546,7 @@ mod test_uniq_solve {
|
||||||
),
|
),
|
||||||
// TODO investigate why is this not shared?
|
// TODO investigate why is this not shared?
|
||||||
// maybe because y is not used it is dropped?
|
// maybe because y is not used it is dropped?
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,7 +560,7 @@ mod test_uniq_solve {
|
||||||
enlist 5
|
enlist 5
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (List (Attr * Int))",
|
"Attr * (List (Attr * (Num (Attr * *))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,7 +587,7 @@ mod test_uniq_solve {
|
||||||
1 |> (\a -> a)
|
1 |> (\a -> a)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,7 +599,7 @@ mod test_uniq_solve {
|
||||||
(\a -> a) 1
|
(\a -> a) 1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,7 +613,7 @@ mod test_uniq_solve {
|
||||||
1 |> always "foo"
|
1 |> always "foo"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,7 +679,7 @@ mod test_uniq_solve {
|
||||||
apply identity 5
|
apply identity 5
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -702,13 +702,13 @@ mod test_uniq_solve {
|
||||||
// indoc!(
|
// indoc!(
|
||||||
// r#"
|
// r#"
|
||||||
// flip = \f -> (\a b -> f b a)
|
// flip = \f -> (\a b -> f b a)
|
||||||
// neverendingInt = \f int -> f int
|
// neverendingNum = \f int -> f int
|
||||||
// x = neverendingInt (\a -> a) 5
|
// x = neverendingNum (\a -> a) 5
|
||||||
|
|
||||||
// flip neverendingInt
|
// flip neverendingNum
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "(Int, (a -> a)) -> Int",
|
// "((Num (Attr * *)), (a -> a)) -> (Num (Attr * *))",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -782,7 +782,7 @@ mod test_uniq_solve {
|
||||||
// 1 // 2
|
// 1 // 2
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "Int",
|
// "(Num (Attr * *))",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -794,7 +794,7 @@ mod test_uniq_solve {
|
||||||
// 1 + 2
|
// 1 + 2
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "Int",
|
// "(Num (Attr * *))",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -843,7 +843,7 @@ mod test_uniq_solve {
|
||||||
[ alwaysFive "foo", alwaysFive [] ]
|
[ alwaysFive "foo", alwaysFive [] ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (List (Attr * Int))",
|
"Attr * (List (Attr * (Num (Attr * *))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,7 +858,7 @@ mod test_uniq_solve {
|
||||||
24
|
24
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -872,18 +872,18 @@ mod test_uniq_solve {
|
||||||
3 -> 4
|
3 -> 4
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record() {
|
fn record() {
|
||||||
infer_eq("{ foo: 42 }", "Attr * { foo : (Attr * Int) }");
|
infer_eq("{ foo: 42 }", "Attr * { foo : (Attr * (Num (Attr * *))) }");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record_access() {
|
fn record_access() {
|
||||||
infer_eq("{ foo: 42 }.foo", "Attr * Int");
|
infer_eq("{ foo: 42 }.foo", "Attr * (Num (Attr * *))");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -937,7 +937,7 @@ mod test_uniq_solve {
|
||||||
\Foo -> 42
|
\Foo -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr * [ Foo ]* -> Attr * Int)",
|
"Attr * (Attr * [ Foo ]* -> Attr * (Num (Attr * *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,7 +949,7 @@ mod test_uniq_solve {
|
||||||
\@Foo -> 42
|
\@Foo -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr * [ @Foo ]* -> Attr * Int)",
|
"Attr * (Attr * [ @Foo ]* -> Attr * (Num (Attr * *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -964,7 +964,7 @@ mod test_uniq_solve {
|
||||||
False -> 0
|
False -> 0
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr * [ False, True ]* -> Attr * Int)",
|
"Attr * (Attr * [ False, True ]* -> Attr * (Num (Attr * *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,7 +976,7 @@ mod test_uniq_solve {
|
||||||
Foo "happy" 2020
|
Foo "happy" 2020
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * [ Foo (Attr * Str) (Attr * Int) ]*",
|
"Attr * [ Foo (Attr * Str) (Attr * (Num (Attr * *))) ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -988,7 +988,7 @@ mod test_uniq_solve {
|
||||||
@Foo "happy" 2020
|
@Foo "happy" 2020
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * [ @Foo (Attr * Str) (Attr * Int) ]*",
|
"Attr * [ @Foo (Attr * Str) (Attr * (Num (Attr * *))) ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1037,7 +1037,7 @@ mod test_uniq_solve {
|
||||||
{ x: 4 } -> x
|
{ x: 4 } -> x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1079,7 +1079,7 @@ mod test_uniq_solve {
|
||||||
Foo x -> x
|
Foo x -> x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,7 +1092,7 @@ mod test_uniq_solve {
|
||||||
@Foo x -> x
|
@Foo x -> x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1194,7 +1194,7 @@ mod test_uniq_solve {
|
||||||
{ numIdentity, p, q }
|
{ numIdentity, p, q }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * { numIdentity : (Attr Shared (Attr a (Num (Attr b p)) -> Attr a (Num (Attr b p)))), p : (Attr * Int), q : (Attr * Float) }"
|
"Attr * { numIdentity : (Attr Shared (Attr a (Num (Attr b p)) -> Attr a (Num (Attr b p)))), p : (Attr * (Num (Attr * p))), q : (Attr * Float) }"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1380,7 +1380,7 @@ mod test_uniq_solve {
|
||||||
factorial
|
factorial
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr Shared (Attr * Int -> Attr * Int)",
|
"Attr Shared (Attr * (Num (Attr * *)) -> Attr * (Num (Attr * *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1479,7 +1479,7 @@ mod test_uniq_solve {
|
||||||
s.left
|
s.left
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr Shared Int",
|
"Attr Shared (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1498,7 +1498,7 @@ mod test_uniq_solve {
|
||||||
{ y: s.left }
|
{ y: s.left }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * { y : (Attr Shared Int) }",
|
"Attr * { y : (Attr Shared (Num (Attr * *))) }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1549,7 +1549,7 @@ mod test_uniq_solve {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
// it's fine that the inner fields are not shared: only shared extraction is possible
|
// it's fine that the inner fields are not shared: only shared extraction is possible
|
||||||
"Attr * { left : (Attr Shared { left : (Attr * Int), right : (Attr * Int) }), right : (Attr Shared { left : (Attr * Int), right : (Attr * Int) }) }",
|
"Attr * { left : (Attr Shared { left : (Attr * (Num (Attr * *))), right : (Attr * (Num (Attr * *))) }), right : (Attr Shared { left : (Attr * (Num (Attr * *))), right : (Attr * (Num (Attr * *))) }) }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1869,7 +1869,7 @@ mod test_uniq_solve {
|
||||||
4 + 4
|
4 + 4
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1882,7 +1882,7 @@ mod test_uniq_solve {
|
||||||
|> List.get 2
|
|> List.get 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Result (Attr * Int) (Attr * [ IndexOutOfBounds ]*))",
|
"Attr * (Result (Attr * (Num (Attr * *))) (Attr * [ IndexOutOfBounds ]*))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1988,7 +1988,7 @@ mod test_uniq_solve {
|
||||||
list
|
list
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr a (List (Attr Shared Int)) -> Attr a (List (Attr Shared Int)))",
|
"Attr * (Attr a (List (Attr Shared (Num (Attr b c)))) -> Attr a (List (Attr Shared (Num (Attr b c)))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2004,7 +2004,7 @@ mod test_uniq_solve {
|
||||||
List.set list 0 42
|
List.set list 0 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr (a | b) (List (Attr b Int)) -> Attr (a | b) (List (Attr b Int)))",
|
"Attr * (Attr (a | b) (List (Attr b (Num (Attr c d)))) -> Attr (a | b) (List (Attr b (Num (Attr c d)))))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2047,7 +2047,7 @@ mod test_uniq_solve {
|
||||||
sum
|
sum
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr * (List (Attr * Int)) -> Attr * Int)",
|
"Attr * (Attr * (List (Attr * (Num (Attr a b)))) -> Attr * (Num (Attr a b)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2095,7 +2095,7 @@ mod test_uniq_solve {
|
||||||
List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1
|
List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * Int",
|
"Attr * (Num (Attr * *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2113,7 +2113,8 @@ mod test_uniq_solve {
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr (* | a | b) { p : (Attr b *), q : (Attr a *) }* -> Attr * Int)",
|
"Attr * (Attr (* | a | b) { p : (Attr a *), q : (Attr b *) }* -> Attr * (Num (Attr * *)))",
|
||||||
|
//"Attr * (Attr (* | a | b) { p : (Attr b *), q : (Attr a *) }* -> Attr * (Num (Attr * *)))"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2234,26 +2235,26 @@ mod test_uniq_solve {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
Model position : { evaluated : Set position
|
Model position : { evaluated : Set position
|
||||||
, openSet : Set position
|
, openSet : Set position
|
||||||
, costs : Map.Map position Float
|
, costs : Map.Map position Float
|
||||||
, cameFrom : Map.Map position position
|
, cameFrom : Map.Map position position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
initialModel : position -> Model position
|
initialModel : position -> Model position
|
||||||
initialModel = \start ->
|
initialModel = \start ->
|
||||||
{ evaluated : Set.empty
|
{ evaluated : Set.empty
|
||||||
, openSet : Set.singleton start
|
, openSet : Set.singleton start
|
||||||
, costs : Map.singleton start 0.0
|
, costs : Map.singleton start 0.0
|
||||||
, cameFrom : Map.empty
|
, cameFrom : Map.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
cheapestOpen : (position -> Float), Model position -> Result position [ KeyNotFound ]*
|
cheapestOpen : (position -> Float), Model position -> Result position [ KeyNotFound ]*
|
||||||
cheapestOpen = \costFunction, model ->
|
cheapestOpen = \costFunction, model ->
|
||||||
|
|
||||||
folder = \position, resSmallestSoFar ->
|
folder = \position, resSmallestSoFar ->
|
||||||
when Map.get model.costs position is
|
when Map.get model.costs position is
|
||||||
Err e ->
|
Err e ->
|
||||||
Err e
|
Err e
|
||||||
|
@ -2270,74 +2271,76 @@ mod test_uniq_solve {
|
||||||
else
|
else
|
||||||
Ok smallestSoFar
|
Ok smallestSoFar
|
||||||
|
|
||||||
Set.foldl model.openSet folder (Err KeyNotFound)
|
|
||||||
|> Result.map (\x -> x.position)
|
Set.foldl model.openSet folder (Err KeyNotFound)
|
||||||
|
|> Result.map (\x -> x.position)
|
||||||
|
|
||||||
|
|
||||||
|
reconstructPath : Map position position, position -> List position
|
||||||
|
reconstructPath = \cameFrom, goal ->
|
||||||
|
when Map.get cameFrom goal is
|
||||||
|
Err KeyNotFound ->
|
||||||
|
[]
|
||||||
|
|
||||||
reconstructPath : Map position position, position -> List position
|
Ok next ->
|
||||||
reconstructPath = \cameFrom, goal ->
|
List.push (reconstructPath cameFrom next) goal
|
||||||
when Map.get cameFrom goal is
|
|
||||||
Err KeyNotFound ->
|
|
||||||
[]
|
|
||||||
|
|
||||||
Ok next ->
|
|
||||||
List.push (reconstructPath cameFrom next) goal
|
|
||||||
|
|
||||||
updateCost : position, position, Model position -> Model position
|
updateCost : position, position, Model position -> Model position
|
||||||
updateCost = \current, neighbour, model ->
|
updateCost = \current, neighbour, model ->
|
||||||
newCameFrom = Map.insert model.cameFrom neighbour current
|
newCameFrom = Map.insert model.cameFrom neighbour current
|
||||||
|
|
||||||
newCosts = Map.insert model.costs neighbour distanceTo
|
newCosts = Map.insert model.costs neighbour distanceTo
|
||||||
|
|
||||||
distanceTo = reconstructPath newCameFrom neighbour
|
distanceTo =
|
||||||
|> List.len
|
reconstructPath newCameFrom neighbour
|
||||||
|> Num.toFloat
|
|> List.length
|
||||||
|
|> Num.toFloat
|
||||||
|
|
||||||
newModel = { model & costs : newCosts , cameFrom : newCameFrom }
|
newModel = { model & costs : newCosts , cameFrom : newCameFrom }
|
||||||
|
|
||||||
when Map.get model.costs neighbour is
|
when Map.get model.costs neighbour is
|
||||||
Err KeyNotFound ->
|
Err KeyNotFound ->
|
||||||
newModel
|
|
||||||
|
|
||||||
Ok previousDistance ->
|
|
||||||
if distanceTo < previousDistance then
|
|
||||||
newModel
|
newModel
|
||||||
|
|
||||||
else
|
Ok previousDistance ->
|
||||||
model
|
if distanceTo < previousDistance then
|
||||||
|
newModel
|
||||||
|
|
||||||
|
else
|
||||||
|
model
|
||||||
|
|
||||||
|
|
||||||
findPath : { costFunction: (position, position -> Float), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [ KeyNotFound ]*
|
findPath : { costFunction: (position, position -> Float), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [ KeyNotFound ]*
|
||||||
findPath = \{ costFunction, moveFunction, start, end } ->
|
findPath = \{ costFunction, moveFunction, start, end } ->
|
||||||
astar costFunction moveFunction end (initialModel start)
|
astar costFunction moveFunction end (initialModel start)
|
||||||
|
|
||||||
|
|
||||||
astar : (position, position -> Float), (position -> Set position), position, Model position -> [ Err [ KeyNotFound ]*, Ok (List position) ]*
|
astar : (position, position -> Float), (position -> Set position), position, Model position -> [ Err [ KeyNotFound ]*, Ok (List position) ]*
|
||||||
astar = \costFn, moveFn, goal, model ->
|
astar = \costFn, moveFn, goal, model ->
|
||||||
when cheapestOpen (\position -> costFn goal position) model is
|
when cheapestOpen (\position -> costFn goal position) model is
|
||||||
Err _ ->
|
Err _ ->
|
||||||
Err KeyNotFound
|
Err KeyNotFound
|
||||||
|
|
||||||
Ok current ->
|
Ok current ->
|
||||||
if current == goal then
|
if current == goal then
|
||||||
Ok (reconstructPath model.cameFrom goal)
|
Ok (reconstructPath model.cameFrom goal)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
modelPopped = { model & openSet : Set.remove model.openSet current, evaluated : Set.insert model.evaluated current }
|
modelPopped = { model & openSet : Set.remove model.openSet current, evaluated : Set.insert model.evaluated current }
|
||||||
|
|
||||||
neighbours = moveFn current
|
neighbours = moveFn current
|
||||||
|
|
||||||
newNeighbours = Set.diff neighbours modelPopped.evaluated
|
newNeighbours = Set.diff neighbours modelPopped.evaluated
|
||||||
|
|
||||||
modelWithNeighbours = { modelPopped & openSet : Set.union modelPopped.openSet newNeighbours }
|
modelWithNeighbours = { modelPopped & openSet : Set.union modelPopped.openSet newNeighbours }
|
||||||
|
|
||||||
modelWithCosts = Set.foldl newNeighbours (\nb, md -> updateCost current nb md) modelWithNeighbours
|
modelWithCosts = Set.foldl newNeighbours (\nb, md -> updateCost current nb md) modelWithNeighbours
|
||||||
|
|
||||||
astar costFn moveFn goal modelWithCosts
|
astar costFn moveFn goal modelWithCosts
|
||||||
|
|
||||||
findPath
|
findPath
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr * { costFunction : (Attr Shared (Attr Shared position, Attr Shared position -> Attr Shared Float)), end : (Attr Shared position), moveFunction : (Attr Shared (Attr Shared position -> Attr * (Set (Attr Shared position)))), start : (Attr Shared position) } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))"
|
"Attr * (Attr * { costFunction : (Attr Shared (Attr Shared position, Attr Shared position -> Attr Shared Float)), end : (Attr Shared position), moveFunction : (Attr Shared (Attr Shared position -> Attr * (Set (Attr Shared position)))), start : (Attr Shared position) } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))"
|
||||||
|
|
|
@ -436,7 +436,7 @@ fn write_flat_type(
|
||||||
|
|
||||||
buf.push_str(" ]");
|
buf.push_str(" ]");
|
||||||
|
|
||||||
if let Some(content) = ext_content {
|
if let Err(content) = ext_content {
|
||||||
// This is an open tag union, so print the variable
|
// This is an open tag union, so print the variable
|
||||||
// right after the ']'
|
// right after the ']'
|
||||||
//
|
//
|
||||||
|
@ -483,7 +483,7 @@ fn write_flat_type(
|
||||||
|
|
||||||
buf.push_str(" ]");
|
buf.push_str(" ]");
|
||||||
|
|
||||||
if let Some(content) = ext_content {
|
if let Err(content) = ext_content {
|
||||||
// This is an open tag union, so print the variable
|
// This is an open tag union, so print the variable
|
||||||
// right after the ']'
|
// right after the ']'
|
||||||
//
|
//
|
||||||
|
@ -504,14 +504,14 @@ fn write_flat_type(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chase_ext_tag_union(
|
pub fn chase_ext_tag_union(
|
||||||
subs: &mut Subs,
|
subs: &Subs,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
fields: &mut Vec<(TagName, Vec<Variable>)>,
|
fields: &mut Vec<(TagName, Vec<Variable>)>,
|
||||||
) -> Option<Content> {
|
) -> Result<(), Content> {
|
||||||
use FlatType::*;
|
use FlatType::*;
|
||||||
match subs.get(var).content {
|
match subs.get_without_compacting(var).content {
|
||||||
Content::Structure(EmptyTagUnion) => None,
|
Content::Structure(EmptyTagUnion) => Ok(()),
|
||||||
Content::Structure(TagUnion(tags, ext_var))
|
Content::Structure(TagUnion(tags, ext_var))
|
||||||
| Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => {
|
| Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => {
|
||||||
for (label, vars) in tags {
|
for (label, vars) in tags {
|
||||||
|
@ -521,7 +521,30 @@ fn chase_ext_tag_union(
|
||||||
chase_ext_tag_union(subs, ext_var, fields)
|
chase_ext_tag_union(subs, ext_var, fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
content => Some(content),
|
content => Err(content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chase_ext_record(
|
||||||
|
subs: &Subs,
|
||||||
|
var: Variable,
|
||||||
|
fields: &mut MutMap<Lowercase, Variable>,
|
||||||
|
) -> Result<(), Content> {
|
||||||
|
use crate::subs::Content::*;
|
||||||
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
|
match subs.get_without_compacting(var).content {
|
||||||
|
Structure(Record(sub_fields, sub_ext)) => {
|
||||||
|
fields.extend(sub_fields.into_iter());
|
||||||
|
|
||||||
|
chase_ext_record(subs, sub_ext, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
Structure(EmptyRecord) => Ok(()),
|
||||||
|
|
||||||
|
Alias(_, _, var) => chase_ext_record(subs, var, fields),
|
||||||
|
|
||||||
|
content => Err(content),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use roc_module::symbol::Symbol;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter::{once, Iterator};
|
use std::iter::{once, Iterator};
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use ven_ena::unify::{InPlace, UnificationTable, UnifyKey};
|
use ven_ena::unify::{InPlace, Snapshot, UnificationTable, UnifyKey};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub struct Mark(i32);
|
pub struct Mark(i32);
|
||||||
|
@ -42,7 +42,7 @@ struct NameState {
|
||||||
normals: u32,
|
normals: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Subs {
|
pub struct Subs {
|
||||||
utable: UnificationTable<InPlace<Variable>>,
|
utable: UnificationTable<InPlace<Variable>>,
|
||||||
}
|
}
|
||||||
|
@ -151,9 +151,11 @@ impl Variable {
|
||||||
|
|
||||||
pub const EMPTY_RECORD: Variable = Variable(1);
|
pub const EMPTY_RECORD: Variable = Variable(1);
|
||||||
pub const EMPTY_TAG_UNION: Variable = Variable(2);
|
pub const EMPTY_TAG_UNION: Variable = Variable(2);
|
||||||
|
const BOOL_ENUM: Variable = Variable(3);
|
||||||
|
pub const BOOL: Variable = Variable(4);
|
||||||
|
pub const RESERVED: usize = 5;
|
||||||
|
|
||||||
// variables 1 and 2 are reserved for EmptyRecord and EmptyTagUnion
|
const FIRST_USER_SPACE_VAR: Variable = Variable(Self::RESERVED as u32);
|
||||||
const FIRST_USER_SPACE_VAR: Variable = Variable(3);
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -228,8 +230,26 @@ impl Subs {
|
||||||
subs.utable.new_key(flex_var_descriptor());
|
subs.utable.new_key(flex_var_descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
subs.set_content(Variable(1), Content::Structure(FlatType::EmptyRecord));
|
subs.set_content(
|
||||||
subs.set_content(Variable(2), Content::Structure(FlatType::EmptyTagUnion));
|
Variable::EMPTY_RECORD,
|
||||||
|
Content::Structure(FlatType::EmptyRecord),
|
||||||
|
);
|
||||||
|
subs.set_content(
|
||||||
|
Variable::EMPTY_TAG_UNION,
|
||||||
|
Content::Structure(FlatType::EmptyTagUnion),
|
||||||
|
);
|
||||||
|
|
||||||
|
subs.set_content(Variable::BOOL_ENUM, {
|
||||||
|
let mut tags = MutMap::default();
|
||||||
|
tags.insert(TagName::Global("False".into()), vec![]);
|
||||||
|
tags.insert(TagName::Global("True".into()), vec![]);
|
||||||
|
|
||||||
|
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
||||||
|
});
|
||||||
|
|
||||||
|
subs.set_content(Variable::BOOL, {
|
||||||
|
Content::Alias(Symbol::BOOL_BOOL, vec![], Variable::BOOL_ENUM)
|
||||||
|
});
|
||||||
|
|
||||||
subs
|
subs
|
||||||
}
|
}
|
||||||
|
@ -382,6 +402,14 @@ impl Subs {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.utable.is_empty()
|
self.utable.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(&mut self) -> Snapshot<InPlace<Variable>> {
|
||||||
|
self.utable.snapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rollback_to(&mut self, snapshot: Snapshot<InPlace<Variable>>) {
|
||||||
|
self.utable.rollback_to(snapshot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -522,6 +550,250 @@ pub enum FlatType {
|
||||||
Boolean(boolean_algebra::Bool),
|
Boolean(boolean_algebra::Bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Copy)]
|
||||||
|
pub struct ContentHash(u64);
|
||||||
|
|
||||||
|
impl ContentHash {
|
||||||
|
pub fn from_var(var: Variable, subs: &mut Subs) -> Self {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
|
||||||
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
Self::from_var_help(var, subs, &mut hasher);
|
||||||
|
|
||||||
|
ContentHash(hasher.finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_var_help<T>(var: Variable, subs: &mut Subs, hasher: &mut T)
|
||||||
|
where
|
||||||
|
T: std::hash::Hasher,
|
||||||
|
{
|
||||||
|
Self::from_content_help(var, &subs.get_without_compacting(var).content, subs, hasher)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_content_help<T>(var: Variable, content: &Content, subs: &mut Subs, hasher: &mut T)
|
||||||
|
where
|
||||||
|
T: std::hash::Hasher,
|
||||||
|
{
|
||||||
|
match content {
|
||||||
|
Content::Alias(_, _, actual) => {
|
||||||
|
// ensure an alias has the same hash as just the body of the alias
|
||||||
|
Self::from_var_help(*actual, subs, hasher)
|
||||||
|
}
|
||||||
|
Content::Structure(flat_type) => {
|
||||||
|
hasher.write_u8(0x10);
|
||||||
|
Self::from_flat_type_help(var, flat_type, subs, hasher)
|
||||||
|
}
|
||||||
|
Content::FlexVar(_) | Content::RigidVar(_) => {
|
||||||
|
hasher.write_u8(0x11);
|
||||||
|
}
|
||||||
|
Content::Error => {
|
||||||
|
hasher.write_u8(0x12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_flat_type_help<T>(
|
||||||
|
flat_type_var: Variable,
|
||||||
|
flat_type: &FlatType,
|
||||||
|
subs: &mut Subs,
|
||||||
|
hasher: &mut T,
|
||||||
|
) where
|
||||||
|
T: std::hash::Hasher,
|
||||||
|
{
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
match flat_type {
|
||||||
|
FlatType::Func(arguments, ret) => {
|
||||||
|
hasher.write_u8(0);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::from_var_help(*ret, subs, hasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Apply(symbol, arguments) => {
|
||||||
|
hasher.write_u8(1);
|
||||||
|
|
||||||
|
symbol.hash(hasher);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::EmptyRecord => {
|
||||||
|
hasher.write_u8(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Record(record_fields, ext) => {
|
||||||
|
hasher.write_u8(3);
|
||||||
|
|
||||||
|
// NOTE: This function will modify the subs, putting all fields from the ext_var
|
||||||
|
// into the record itself, then setting the ext_var to EMPTY_RECORD
|
||||||
|
|
||||||
|
let mut fields = Vec::with_capacity(record_fields.len());
|
||||||
|
|
||||||
|
let mut extracted_fields_from_ext = false;
|
||||||
|
if *ext != Variable::EMPTY_RECORD {
|
||||||
|
let mut fields_map = MutMap::default();
|
||||||
|
match crate::pretty_print::chase_ext_record(subs, *ext, &mut fields_map) {
|
||||||
|
Err(Content::FlexVar(_)) | Ok(()) => {
|
||||||
|
if !fields_map.is_empty() {
|
||||||
|
extracted_fields_from_ext = true;
|
||||||
|
fields.extend(fields_map.into_iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(content) => panic!("Record with unexpected ext_var: {:?}", content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.extend(record_fields.clone().into_iter());
|
||||||
|
fields.sort();
|
||||||
|
|
||||||
|
for (name, argument) in &fields {
|
||||||
|
name.hash(hasher);
|
||||||
|
Self::from_var_help(*argument, subs, hasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ext != Variable::EMPTY_RECORD {
|
||||||
|
// unify ext with empty record
|
||||||
|
let desc = subs.get(Variable::EMPTY_RECORD);
|
||||||
|
subs.union(Variable::EMPTY_RECORD, *ext, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if extracted_fields_from_ext {
|
||||||
|
let fields_map = fields.into_iter().collect();
|
||||||
|
|
||||||
|
subs.set_content(
|
||||||
|
flat_type_var,
|
||||||
|
Content::Structure(FlatType::Record(fields_map, Variable::EMPTY_RECORD)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::EmptyTagUnion => {
|
||||||
|
hasher.write_u8(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::TagUnion(tags, ext) => {
|
||||||
|
hasher.write_u8(5);
|
||||||
|
|
||||||
|
// NOTE: This function will modify the subs, putting all tags from the ext_var
|
||||||
|
// into the tag union itself, then setting the ext_var to EMPTY_TAG_UNION
|
||||||
|
|
||||||
|
let mut tag_vec = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
let mut extracted_fields_from_ext = false;
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
match crate::pretty_print::chase_ext_tag_union(subs, *ext, &mut tag_vec) {
|
||||||
|
Err(Content::FlexVar(_)) | Ok(()) => {
|
||||||
|
extracted_fields_from_ext = !tag_vec.is_empty();
|
||||||
|
}
|
||||||
|
Err(content) => panic!("TagUnion with unexpected ext_var: {:?}", content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_vec.extend(tags.clone().into_iter());
|
||||||
|
tag_vec.sort();
|
||||||
|
for (name, arguments) in &tag_vec {
|
||||||
|
name.hash(hasher);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
// unify ext with empty record
|
||||||
|
let desc = subs.get(Variable::EMPTY_TAG_UNION);
|
||||||
|
subs.union(Variable::EMPTY_TAG_UNION, *ext, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if extracted_fields_from_ext {
|
||||||
|
let fields_map = tag_vec.into_iter().collect();
|
||||||
|
|
||||||
|
subs.set_content(
|
||||||
|
flat_type_var,
|
||||||
|
Content::Structure(FlatType::TagUnion(
|
||||||
|
fields_map,
|
||||||
|
Variable::EMPTY_TAG_UNION,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::RecursiveTagUnion(rec, tags, ext) => {
|
||||||
|
// NOTE: rec is not hashed in. If all the tags and their arguments are the same,
|
||||||
|
// then the recursive tag unions are the same
|
||||||
|
hasher.write_u8(6);
|
||||||
|
|
||||||
|
// NOTE: This function will modify the subs, putting all tags from the ext_var
|
||||||
|
// into the tag union itself, then setting the ext_var to EMPTY_TAG_UNION
|
||||||
|
|
||||||
|
let mut tag_vec = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
let mut extracted_fields_from_ext = false;
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
match crate::pretty_print::chase_ext_tag_union(subs, *ext, &mut tag_vec) {
|
||||||
|
Err(Content::FlexVar(_)) | Ok(()) => {
|
||||||
|
extracted_fields_from_ext = !tag_vec.is_empty();
|
||||||
|
}
|
||||||
|
Err(content) => {
|
||||||
|
panic!("RecursiveTagUnion with unexpected ext_var: {:?}", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_vec.extend(tags.clone().into_iter());
|
||||||
|
tag_vec.sort();
|
||||||
|
for (name, arguments) in &tag_vec {
|
||||||
|
name.hash(hasher);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
// unify ext with empty record
|
||||||
|
let desc = subs.get(Variable::EMPTY_TAG_UNION);
|
||||||
|
subs.union(Variable::EMPTY_TAG_UNION, *ext, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if extracted_fields_from_ext {
|
||||||
|
let fields_map = tag_vec.into_iter().collect();
|
||||||
|
|
||||||
|
subs.set_content(
|
||||||
|
flat_type_var,
|
||||||
|
Content::Structure(FlatType::RecursiveTagUnion(
|
||||||
|
*rec,
|
||||||
|
fields_map,
|
||||||
|
Variable::EMPTY_TAG_UNION,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Boolean(boolean) => {
|
||||||
|
hasher.write_u8(7);
|
||||||
|
match boolean.simplify(subs) {
|
||||||
|
Ok(_variables) => hasher.write_u8(1),
|
||||||
|
Err(crate::boolean_algebra::Atom::One) => hasher.write_u8(1),
|
||||||
|
Err(crate::boolean_algebra::Atom::Zero) => hasher.write_u8(0),
|
||||||
|
Err(crate::boolean_algebra::Atom::Variable(_)) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Erroneous(_problem) => {
|
||||||
|
hasher.write_u8(8);
|
||||||
|
//TODO hash the problem?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||||
pub enum Builtin {
|
pub enum Builtin {
|
||||||
Str,
|
Str,
|
||||||
|
|
|
@ -621,6 +621,7 @@ pub enum Reason {
|
||||||
BinOpRet(BinOp),
|
BinOpRet(BinOp),
|
||||||
FloatLiteral,
|
FloatLiteral,
|
||||||
IntLiteral,
|
IntLiteral,
|
||||||
|
NumLiteral,
|
||||||
InterpolatedStringVar,
|
InterpolatedStringVar,
|
||||||
WhenBranch { index: usize },
|
WhenBranch { index: usize },
|
||||||
IfCondition,
|
IfCondition,
|
||||||
|
@ -638,8 +639,9 @@ pub enum PatternCategory {
|
||||||
Set,
|
Set,
|
||||||
Map,
|
Map,
|
||||||
Ctor(TagName),
|
Ctor(TagName),
|
||||||
Int,
|
|
||||||
Str,
|
Str,
|
||||||
|
Num,
|
||||||
|
Int,
|
||||||
Float,
|
Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,14 +728,14 @@ pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lower
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_fields(
|
pub fn gather_fields(
|
||||||
subs: &mut Subs,
|
subs: &Subs,
|
||||||
fields: MutMap<Lowercase, Variable>,
|
fields: MutMap<Lowercase, Variable>,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
) -> RecordStructure {
|
) -> RecordStructure {
|
||||||
use crate::subs::Content::*;
|
use crate::subs::Content::*;
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
match subs.get(var).content {
|
match subs.get_without_compacting(var).content {
|
||||||
Structure(Record(sub_fields, sub_ext)) => {
|
Structure(Record(sub_fields, sub_ext)) => {
|
||||||
gather_fields(subs, union(fields, &sub_fields), sub_ext)
|
gather_fields(subs, union(fields, &sub_fields), sub_ext)
|
||||||
}
|
}
|
||||||
|
|
|
@ -515,6 +515,7 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
RuntimeError(_)
|
RuntimeError(_)
|
||||||
|
| Num(_, _)
|
||||||
| Int(_, _)
|
| Int(_, _)
|
||||||
| Float(_, _)
|
| Float(_, _)
|
||||||
| Str(_)
|
| Str(_)
|
||||||
|
|
|
@ -694,24 +694,6 @@ Any operation which would result in one of these (such as `sqrt` or `/`) will
|
||||||
result in a runtime exception. Similarly to overflow, you can opt into handling these
|
result in a runtime exception. Similarly to overflow, you can opt into handling these
|
||||||
a different way, such as `Float.trySqrt` which returns a `Result`.
|
a different way, such as `Float.trySqrt` which returns a `Result`.
|
||||||
|
|
||||||
Also like Elm, number literals with decimal points are `Float`. However, number
|
|
||||||
literals *without* a decimal point are always `Int`. So `x / 2` will never
|
|
||||||
compile in Roc; it would have to be `x / 2.0`, like in Python. Also [like Python](https://www.python.org/dev/peps/pep-0515/)
|
|
||||||
Roc permits underscores in number literals for readability purposes, and supports hexadecimal (`0x01`), octal (`0o01`), and binary (`0b01`) `Int` literals.
|
|
||||||
|
|
||||||
If you put these into a hypothetical Roc REPL, here's what you'd see:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
> 1_024 + 1_024
|
|
||||||
2048 : Int
|
|
||||||
|
|
||||||
> 1.0 + 2.14
|
|
||||||
3.14 : Float
|
|
||||||
|
|
||||||
> 1 + 2.14
|
|
||||||
<type mismatch>
|
|
||||||
```
|
|
||||||
|
|
||||||
The way `+` works here is also a bit different than in Elm. Imagine if Elm's
|
The way `+` works here is also a bit different than in Elm. Imagine if Elm's
|
||||||
`(+)` operator had this type:
|
`(+)` operator had this type:
|
||||||
|
|
||||||
|
@ -736,6 +718,32 @@ These don't exist in Roc.
|
||||||
* `comparable` is used for comparison operators (like `<` and such), plus `List.sort`, `Dict`, and `Set`. Roc's `List.sort` accepts a `Sorter` argument which specifies how to sort the elements. Roc's comparison operators (like `<`) only accept numbers; `"foo" < "bar"` is valid Elm, but will not compile in Roc. Roc's dictionaries and sets are hashmaps behind the scenes (rather than ordered trees), and their keys have no visible type restrictions.
|
* `comparable` is used for comparison operators (like `<` and such), plus `List.sort`, `Dict`, and `Set`. Roc's `List.sort` accepts a `Sorter` argument which specifies how to sort the elements. Roc's comparison operators (like `<`) only accept numbers; `"foo" < "bar"` is valid Elm, but will not compile in Roc. Roc's dictionaries and sets are hashmaps behind the scenes (rather than ordered trees), and their keys have no visible type restrictions.
|
||||||
* `number` is replaced by `Num`, as described earlier.
|
* `number` is replaced by `Num`, as described earlier.
|
||||||
|
|
||||||
|
Like in Elm, number literals with decimal points are `Float`. However, number
|
||||||
|
literals *without* a decimal point are `Num *` instead of `number`.
|
||||||
|
Also [like Python](https://www.python.org/dev/peps/pep-0515/)
|
||||||
|
Roc permits underscores in number literals for readability purposes. Roc also supports
|
||||||
|
hexadecimal (`0x01`), octal (`0o01`), and binary (`0b01`) integer literals; these
|
||||||
|
literals all have type `Int` instead of `Num *`.
|
||||||
|
|
||||||
|
If you put these into a hypothetical Roc REPL, here's what you'd see:
|
||||||
|
|
||||||
|
```elm
|
||||||
|
> 1_024 + 1_024
|
||||||
|
2048 : Num *
|
||||||
|
|
||||||
|
> 1 + 2.14
|
||||||
|
3.14 : Float
|
||||||
|
|
||||||
|
> 1.0 + 1
|
||||||
|
2.0 : Float
|
||||||
|
|
||||||
|
> 1.1 + 0x11
|
||||||
|
<type mismatch between 1.1 : Float and 0x11 : Int>
|
||||||
|
|
||||||
|
> 11 + 0x11
|
||||||
|
28 : Int
|
||||||
|
```
|
||||||
|
|
||||||
## Operators
|
## Operators
|
||||||
|
|
||||||
In Elm, operators are functions. In Roc, all operators are syntax sugar.
|
In Elm, operators are functions. In Roc, all operators are syntax sugar.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue