mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge branch 'trunk' into builtins-list-intersperse
This commit is contained in:
commit
ce8a88416d
319 changed files with 4413 additions and 3654 deletions
2
AUTHORS
2
AUTHORS
|
@ -52,3 +52,5 @@ Ayaz Hafiz <ayaz.hafiz.1@gmail.com>
|
||||||
Johannes Maas <github@j-maas.de>
|
Johannes Maas <github@j-maas.de>
|
||||||
Takeshi Sato <doublequotation@gmail.com>
|
Takeshi Sato <doublequotation@gmail.com>
|
||||||
Joost Baas <joost@joostbaas.eu>
|
Joost Baas <joost@joostbaas.eu>
|
||||||
|
Callum Dunster <cdunster@users.noreply.github.com>
|
||||||
|
Martin Stewart <MartinSStewart@gmail.com>
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3476,7 +3476,9 @@ dependencies = [
|
||||||
name = "roc_parse"
|
name = "roc_parse"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
"diff",
|
||||||
"encode_unicode",
|
"encode_unicode",
|
||||||
"indoc",
|
"indoc",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
|
|
@ -12,7 +12,6 @@ members = [
|
||||||
"compiler/constrain",
|
"compiler/constrain",
|
||||||
"compiler/unify",
|
"compiler/unify",
|
||||||
"compiler/solve",
|
"compiler/solve",
|
||||||
"compiler/reporting",
|
|
||||||
"compiler/fmt",
|
"compiler/fmt",
|
||||||
"compiler/mono",
|
"compiler/mono",
|
||||||
"compiler/test_mono",
|
"compiler/test_mono",
|
||||||
|
@ -31,6 +30,7 @@ members = [
|
||||||
"ast",
|
"ast",
|
||||||
"cli",
|
"cli",
|
||||||
"code_markup",
|
"code_markup",
|
||||||
|
"reporting",
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"utils",
|
"utils",
|
||||||
"docs",
|
"docs",
|
||||||
|
|
|
@ -47,7 +47,7 @@ install-zig-llvm-valgrind-clippy-rustfmt:
|
||||||
|
|
||||||
copy-dirs:
|
copy-dirs:
|
||||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||||
COPY --dir cli cli_utils compiler docs editor ast code_markup utils roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./
|
COPY --dir cli cli_utils compiler docs editor ast code_markup utils reporting roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./
|
||||||
|
|
||||||
test-zig:
|
test-zig:
|
||||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||||
|
|
|
@ -20,6 +20,7 @@ use crate::{
|
||||||
expr2::{ClosureExtra, Expr2, ExprId, WhenBranch},
|
expr2::{ClosureExtra, Expr2, ExprId, WhenBranch},
|
||||||
record_field::RecordField,
|
record_field::RecordField,
|
||||||
},
|
},
|
||||||
|
fun_def::FunctionDef,
|
||||||
pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct},
|
pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct},
|
||||||
types::{Type2, TypeId},
|
types::{Type2, TypeId},
|
||||||
val_def::ValueDef,
|
val_def::ValueDef,
|
||||||
|
@ -818,6 +819,126 @@ pub fn constrain_expr<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// In an expression like
|
||||||
|
// id = \x -> x
|
||||||
|
//
|
||||||
|
// id 1
|
||||||
|
// The `def_id` refers to the definition `id = \x -> x`,
|
||||||
|
// and the body refers to `id 1`.
|
||||||
|
Expr2::LetFunction {
|
||||||
|
def_id,
|
||||||
|
body_id,
|
||||||
|
body_var: _,
|
||||||
|
} => {
|
||||||
|
let body = env.pool.get(*body_id);
|
||||||
|
let body_con = constrain_expr(arena, env, body, expected.shallow_clone(), region);
|
||||||
|
|
||||||
|
let function_def = env.pool.get(*def_id);
|
||||||
|
|
||||||
|
let (name, arguments, body_id, rigid_vars, args_constrs) = match function_def {
|
||||||
|
FunctionDef::WithAnnotation {
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
body_id,
|
||||||
|
rigids,
|
||||||
|
return_type: _,
|
||||||
|
} => {
|
||||||
|
// The annotation gives us arguments with proper Type2s, but the constraints we
|
||||||
|
// generate below args bound to type variables. Create fresh ones and bind them
|
||||||
|
// to the types we already know.
|
||||||
|
let mut args_constrs = BumpVec::with_capacity_in(arguments.len(), arena);
|
||||||
|
let args_vars = PoolVec::with_capacity(arguments.len() as u32, env.pool);
|
||||||
|
for (arg_ty_node_id, arg_var_node_id) in
|
||||||
|
arguments.iter_node_ids().zip(args_vars.iter_node_ids())
|
||||||
|
{
|
||||||
|
let (ty, pattern) = env.pool.get(arg_ty_node_id);
|
||||||
|
let arg_var = env.var_store.fresh();
|
||||||
|
let ty = env.pool.get(*ty);
|
||||||
|
args_constrs.push(Eq(
|
||||||
|
Type2::Variable(arg_var),
|
||||||
|
Expected::NoExpectation(ty.shallow_clone()),
|
||||||
|
Category::Storage(std::file!(), std::line!()),
|
||||||
|
// TODO: should be the actual region of the argument
|
||||||
|
region,
|
||||||
|
));
|
||||||
|
env.pool[arg_var_node_id] = (arg_var, *pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rigids = env.pool.get(*rigids);
|
||||||
|
let rigid_vars: BumpVec<Variable> =
|
||||||
|
BumpVec::from_iter_in(rigids.names.iter(env.pool).map(|&(_, v)| v), arena);
|
||||||
|
|
||||||
|
(name, args_vars, body_id, rigid_vars, args_constrs)
|
||||||
|
}
|
||||||
|
FunctionDef::NoAnnotation {
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
body_id,
|
||||||
|
return_var: _,
|
||||||
|
} => {
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
arguments.shallow_clone(),
|
||||||
|
body_id,
|
||||||
|
BumpVec::new_in(arena), // The function is unannotated, so there are no rigid type vars
|
||||||
|
BumpVec::new_in(arena), // No extra constraints to generate for arguments
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A function definition is equivalent to a named value definition, where the
|
||||||
|
// value is a closure. So, we create a closure definition in correspondence
|
||||||
|
// with the function definition, generate type constraints for it, and demand
|
||||||
|
// that type of the function is just the type of the resolved closure.
|
||||||
|
let fn_var = env.var_store.fresh();
|
||||||
|
let fn_ty = Type2::Variable(fn_var);
|
||||||
|
|
||||||
|
let extra = ClosureExtra {
|
||||||
|
return_type: env.var_store.fresh(),
|
||||||
|
captured_symbols: PoolVec::empty(env.pool),
|
||||||
|
closure_type: env.var_store.fresh(),
|
||||||
|
closure_ext_var: env.var_store.fresh(),
|
||||||
|
};
|
||||||
|
let clos = Expr2::Closure {
|
||||||
|
args: arguments.shallow_clone(),
|
||||||
|
uniq_symbol: *name,
|
||||||
|
body_id: *body_id,
|
||||||
|
function_type: env.var_store.fresh(),
|
||||||
|
extra: env.pool.add(extra),
|
||||||
|
recursive: roc_can::expr::Recursive::Recursive,
|
||||||
|
};
|
||||||
|
let clos_con = constrain_expr(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
&clos,
|
||||||
|
Expected::NoExpectation(fn_ty.shallow_clone()),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
// This is the `foo` part in `foo = \...`. We want to bind the name of the
|
||||||
|
// function with its type, whose constraints we generated above.
|
||||||
|
let mut def_pattern_state = PatternState2 {
|
||||||
|
headers: BumpMap::new_in(arena),
|
||||||
|
vars: BumpVec::new_in(arena),
|
||||||
|
constraints: args_constrs,
|
||||||
|
};
|
||||||
|
def_pattern_state.headers.insert(*name, fn_ty);
|
||||||
|
def_pattern_state.vars.push(fn_var);
|
||||||
|
|
||||||
|
Let(arena.alloc(LetConstraint {
|
||||||
|
rigid_vars,
|
||||||
|
flex_vars: def_pattern_state.vars,
|
||||||
|
def_types: def_pattern_state.headers, // Binding function name -> its type
|
||||||
|
defs_constraint: Let(arena.alloc(LetConstraint {
|
||||||
|
rigid_vars: BumpVec::new_in(arena), // always empty
|
||||||
|
flex_vars: BumpVec::new_in(arena), // empty, because our functions have no arguments
|
||||||
|
def_types: BumpMap::new_in(arena), // empty, because our functions have no arguments
|
||||||
|
defs_constraint: And(def_pattern_state.constraints),
|
||||||
|
ret_constraint: clos_con,
|
||||||
|
})),
|
||||||
|
ret_constraint: body_con,
|
||||||
|
}))
|
||||||
|
}
|
||||||
Expr2::Update {
|
Expr2::Update {
|
||||||
symbol,
|
symbol,
|
||||||
updates,
|
updates,
|
||||||
|
@ -1031,7 +1152,6 @@ pub fn constrain_expr<'a>(
|
||||||
exists(arena, vars, And(and_constraints))
|
exists(arena, vars, And(and_constraints))
|
||||||
}
|
}
|
||||||
Expr2::LetRec { .. } => todo!(),
|
Expr2::LetRec { .. } => todo!(),
|
||||||
Expr2::LetFunction { .. } => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1765,7 +1885,7 @@ pub mod test_constrain {
|
||||||
use roc_parse::parser::SyntaxError;
|
use roc_parse::parser::SyntaxError;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_types::{
|
use roc_types::{
|
||||||
pretty_print::content_to_string,
|
pretty_print::{content_to_string, name_all_type_vars},
|
||||||
solved_types::Solved,
|
solved_types::Solved,
|
||||||
subs::{Subs, VarStore, Variable},
|
subs::{Subs, VarStore, Variable},
|
||||||
};
|
};
|
||||||
|
@ -1845,7 +1965,7 @@ pub mod test_constrain {
|
||||||
let expr2_result = str_to_expr2(&code_arena, actual, &mut env, &mut scope, region);
|
let expr2_result = str_to_expr2(&code_arena, actual, &mut env, &mut scope, region);
|
||||||
|
|
||||||
match expr2_result {
|
match expr2_result {
|
||||||
Ok((expr, _)) => {
|
Ok((expr, output)) => {
|
||||||
let constraint = constrain_expr(
|
let constraint = constrain_expr(
|
||||||
&code_arena,
|
&code_arena,
|
||||||
&mut env,
|
&mut env,
|
||||||
|
@ -1865,17 +1985,22 @@ pub mod test_constrain {
|
||||||
let mut var_store = VarStore::default();
|
let mut var_store = VarStore::default();
|
||||||
std::mem::swap(ref_var_store, &mut var_store);
|
std::mem::swap(ref_var_store, &mut var_store);
|
||||||
|
|
||||||
|
let rigids = output.introduced_variables.name_by_var;
|
||||||
|
|
||||||
let (mut solved, _, _) = run_solve(
|
let (mut solved, _, _) = run_solve(
|
||||||
&code_arena,
|
&code_arena,
|
||||||
pool,
|
pool,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
rigids,
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
);
|
);
|
||||||
|
|
||||||
let subs = solved.inner_mut();
|
let subs = solved.inner_mut();
|
||||||
|
|
||||||
|
// name type vars
|
||||||
|
name_all_type_vars(var, subs);
|
||||||
|
|
||||||
let content = subs.get_content_without_compacting(var);
|
let content = subs.get_content_without_compacting(var);
|
||||||
|
|
||||||
// Connect the ModuleId to it's IdentIds
|
// Connect the ModuleId to it's IdentIds
|
||||||
|
@ -2132,6 +2257,102 @@ pub mod test_constrain {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dual_arity_lambda() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
\a, b -> Pair a b
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a, b -> [ Pair a b ]*",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anonymous_identity() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
(\a -> a) 3.14
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Float *",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn identity_of_identity() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
(\val -> val) (\val -> val)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a -> a",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn identity_function() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
\val -> val
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a -> a",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apply_function() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
\f, x -> f x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"(a -> b), a -> b",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flip_function() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
\f -> (\a, b -> f b a)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"(a, b -> c) -> (b, a -> c)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn always_function() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
\val -> \_ -> val
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a -> (* -> a)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pass_a_function() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
\f -> f {}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"({} -> a) -> a",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constrain_closure() {
|
fn constrain_closure() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
@ -2145,4 +2366,130 @@ pub mod test_constrain {
|
||||||
"{}* -> Num *",
|
"{}* -> Num *",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recursive_identity() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
identity = \val -> val
|
||||||
|
|
||||||
|
identity
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a -> a",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn use_apply() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
identity = \a -> a
|
||||||
|
apply = \f, x -> f x
|
||||||
|
|
||||||
|
apply identity 5
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Num *",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_let_function() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
curryPair = \a ->
|
||||||
|
getB = \b -> Pair a b
|
||||||
|
getB
|
||||||
|
|
||||||
|
curryPair
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a -> (b -> [ Pair a b ]*)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_with_bound_var() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
fn = \rec ->
|
||||||
|
x = rec.x
|
||||||
|
|
||||||
|
rec
|
||||||
|
|
||||||
|
fn
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"{ x : a }b -> { x : a }b",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn using_type_signature() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bar : custom -> custom
|
||||||
|
bar = \x -> x
|
||||||
|
|
||||||
|
bar
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"custom -> custom",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore = "Currently panics at 'Invalid Cycle', ast/src/lang/core/def/def.rs:1212:21"]
|
||||||
|
#[test]
|
||||||
|
fn using_type_signature2() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
id1 : tya -> tya
|
||||||
|
id1 = \x -> x
|
||||||
|
|
||||||
|
id2 : tyb -> tyb
|
||||||
|
id2 = id1
|
||||||
|
|
||||||
|
id2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"tyb -> tyb",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore = "Implement annotation-only decls"]
|
||||||
|
#[test]
|
||||||
|
fn type_signature_without_body() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
foo: Str -> {}
|
||||||
|
|
||||||
|
foo "hi"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"{}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore = "Implement annotation-only decls"]
|
||||||
|
#[test]
|
||||||
|
fn type_signature_without_body_rigid() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
foo : Num * -> custom
|
||||||
|
|
||||||
|
foo 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"custom",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,11 @@ use crate::{
|
||||||
rigids::Rigids,
|
rigids::Rigids,
|
||||||
scope::Scope,
|
scope::Scope,
|
||||||
},
|
},
|
||||||
mem_pool::{pool::Pool, pool_vec::PoolVec, shallow_clone::ShallowClone},
|
mem_pool::{
|
||||||
|
pool::{NodeId, Pool},
|
||||||
|
pool_vec::PoolVec,
|
||||||
|
shallow_clone::ShallowClone,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -316,7 +320,7 @@ fn from_pending_alias<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
for loc_lowercase in vars {
|
for loc_lowercase in vars {
|
||||||
if !named_rigids.contains_key(loc_lowercase.value.as_str()) {
|
if !named_rigids.contains_key(&loc_lowercase.value) {
|
||||||
env.problem(Problem::PhantomTypeArgument {
|
env.problem(Problem::PhantomTypeArgument {
|
||||||
alias: symbol,
|
alias: symbol,
|
||||||
variable_region: loc_lowercase.region,
|
variable_region: loc_lowercase.region,
|
||||||
|
@ -454,6 +458,10 @@ fn canonicalize_pending_def<'a>(
|
||||||
output.references.referenced_aliases.insert(symbol);
|
output.references.referenced_aliases.insert(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure rigid type vars and their names are known in the output.
|
||||||
|
for (name, &var) in named_rigids.iter() {
|
||||||
|
output.introduced_variables.insert_named(name.clone(), var);
|
||||||
|
}
|
||||||
let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool);
|
let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool);
|
||||||
|
|
||||||
// bookkeeping for tail-call detection. If we're assigning to an
|
// bookkeeping for tail-call detection. If we're assigning to an
|
||||||
|
@ -521,7 +529,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
// parent commit for the bug this fixed!
|
// parent commit for the bug this fixed!
|
||||||
let refs = References::new();
|
let refs = References::new();
|
||||||
|
|
||||||
let arguments: PoolVec<(PatternId, Type2)> =
|
let arguments: PoolVec<(NodeId<Type2>, PatternId)> =
|
||||||
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
||||||
|
|
||||||
let return_type: TypeId;
|
let return_type: TypeId;
|
||||||
|
@ -558,7 +566,8 @@ fn canonicalize_pending_def<'a>(
|
||||||
for (node_id, ((_, pattern_id), typ)) in
|
for (node_id, ((_, pattern_id), typ)) in
|
||||||
arguments.iter_node_ids().zip(it.into_iter())
|
arguments.iter_node_ids().zip(it.into_iter())
|
||||||
{
|
{
|
||||||
env.pool[node_id] = (pattern_id, typ);
|
let typ = env.pool.add(typ);
|
||||||
|
env.pool[node_id] = (typ, pattern_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return_type = return_type_id;
|
return_type = return_type_id;
|
||||||
|
@ -689,14 +698,14 @@ fn canonicalize_pending_def<'a>(
|
||||||
// parent commit for the bug this fixed!
|
// parent commit for the bug this fixed!
|
||||||
let refs = References::new();
|
let refs = References::new();
|
||||||
|
|
||||||
let arguments: PoolVec<(PatternId, Variable)> =
|
let arguments: PoolVec<(Variable, PatternId)> =
|
||||||
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
||||||
|
|
||||||
let it: Vec<_> = closure_args.iter(env.pool).map(|(x, y)| (*x, *y)).collect();
|
let it: Vec<_> = closure_args.iter(env.pool).map(|(x, y)| (*x, *y)).collect();
|
||||||
|
|
||||||
for (node_id, (_, pattern_id)) in arguments.iter_node_ids().zip(it.into_iter())
|
for (node_id, (_, pattern_id)) in arguments.iter_node_ids().zip(it.into_iter())
|
||||||
{
|
{
|
||||||
env.pool[node_id] = (pattern_id, env.var_store.fresh());
|
env.pool[node_id] = (env.var_store.fresh(), pattern_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let function_def = FunctionDef::NoAnnotation {
|
let function_def = FunctionDef::NoAnnotation {
|
||||||
|
|
|
@ -14,15 +14,15 @@ use super::{
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FunctionDef {
|
pub enum FunctionDef {
|
||||||
WithAnnotation {
|
WithAnnotation {
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
arguments: PoolVec<(PatternId, Type2)>, // 8B
|
arguments: PoolVec<(NodeId<Type2>, PatternId)>, // 8B
|
||||||
rigids: NodeId<Rigids>, // 4B
|
rigids: NodeId<Rigids>, // 4B
|
||||||
return_type: TypeId, // 4B
|
return_type: TypeId, // 4B
|
||||||
body_id: ExprId, // 4B
|
body_id: ExprId, // 4B
|
||||||
},
|
},
|
||||||
NoAnnotation {
|
NoAnnotation {
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
arguments: PoolVec<(PatternId, Variable)>, // 8B
|
arguments: PoolVec<(Variable, PatternId)>, // 8B
|
||||||
return_var: Variable, // 4B
|
return_var: Variable, // 4B
|
||||||
body_id: ExprId, // 4B
|
body_id: ExprId, // 4B
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@ pub mod ast;
|
||||||
mod declaration;
|
mod declaration;
|
||||||
pub mod def;
|
pub mod def;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
mod fun_def;
|
pub mod fun_def;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod pattern;
|
pub mod pattern;
|
||||||
pub mod str;
|
pub mod str;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
// use roc_can::expr::Output;
|
// use roc_can::expr::Output;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Ident, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::types::{Problem, RecordField};
|
use roc_types::types::{Problem, RecordField};
|
||||||
|
@ -20,29 +20,34 @@ pub type TypeId = NodeId<Type2>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Type2 {
|
pub enum Type2 {
|
||||||
Variable(Variable),
|
Variable(Variable), // 4B
|
||||||
|
|
||||||
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 12B + 4B
|
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
|
||||||
AsAlias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 12B + 4B
|
AsAlias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
|
||||||
|
|
||||||
// 32B
|
// 24B
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
arguments: PoolVec<(PoolStr, TypeId)>, // 12B
|
arguments: PoolVec<(PoolStr, TypeId)>, // 8B
|
||||||
actual_var: Variable, // 4B
|
actual_var: Variable, // 4B
|
||||||
actual: TypeId, // 4B
|
actual: TypeId, // 4B
|
||||||
},
|
},
|
||||||
EmptyTagUnion,
|
EmptyTagUnion,
|
||||||
TagUnion(PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 16B = 12B + 4B
|
TagUnion(PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 12B = 8B + 4B
|
||||||
RecursiveTagUnion(Variable, PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 20B = 4B + 12B + 4B
|
RecursiveTagUnion(Variable, PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 16B = 4B + 8B + 4B
|
||||||
|
|
||||||
EmptyRec,
|
EmptyRec,
|
||||||
Record(PoolVec<(PoolStr, RecordField<TypeId>)>, TypeId), // 16B = 12B + 4B
|
Record(PoolVec<(PoolStr, RecordField<TypeId>)>, TypeId), // 12B = 8B + 4B
|
||||||
|
|
||||||
Function(PoolVec<Type2>, TypeId, TypeId), // 20B = 12B + 4B + 4B
|
Function(PoolVec<Type2>, TypeId, TypeId), // 16B = 8B + 4B + 4B
|
||||||
Apply(Symbol, PoolVec<Type2>), // 20B = 8B + 12B
|
Apply(Symbol, PoolVec<Type2>), // 16B = 8B + 8B
|
||||||
|
|
||||||
Erroneous(Problem2),
|
Erroneous(Problem2), // 24B
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type2_size() {
|
||||||
|
assert_eq!(std::mem::size_of::<Type2>(), 32); // 24B + pad
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -171,9 +176,9 @@ pub enum Signature {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Annotation2<'a> {
|
pub enum Annotation2 {
|
||||||
Annotation {
|
Annotation {
|
||||||
named_rigids: MutMap<&'a str, Variable>,
|
named_rigids: MutMap<Lowercase, Variable>,
|
||||||
unnamed_rigids: MutSet<Variable>,
|
unnamed_rigids: MutSet<Variable>,
|
||||||
symbols: MutSet<Symbol>,
|
symbols: MutSet<Symbol>,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
|
@ -186,7 +191,7 @@ pub fn to_annotation2<'a>(
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
annotation: &'a roc_parse::ast::TypeAnnotation<'a>,
|
annotation: &'a roc_parse::ast::TypeAnnotation<'a>,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Annotation2<'a> {
|
) -> Annotation2 {
|
||||||
let mut references = References::default();
|
let mut references = References::default();
|
||||||
|
|
||||||
let annotation = to_type2(env, scope, &mut references, annotation, region);
|
let annotation = to_type2(env, scope, &mut references, annotation, region);
|
||||||
|
@ -240,11 +245,7 @@ pub fn to_annotation2<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shallow_dealias<'a>(
|
fn shallow_dealias<'a>(env: &mut Env, references: References, annotation: Type2) -> Annotation2 {
|
||||||
env: &mut Env,
|
|
||||||
references: References<'a>,
|
|
||||||
annotation: Type2,
|
|
||||||
) -> Annotation2<'a> {
|
|
||||||
let References {
|
let References {
|
||||||
named,
|
named,
|
||||||
unnamed,
|
unnamed,
|
||||||
|
@ -288,8 +289,8 @@ fn shallow_dealias<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct References<'a> {
|
pub struct References {
|
||||||
named: MutMap<&'a str, Variable>,
|
named: MutMap<Lowercase, Variable>,
|
||||||
unnamed: MutSet<Variable>,
|
unnamed: MutSet<Variable>,
|
||||||
hidden: MutSet<Variable>,
|
hidden: MutSet<Variable>,
|
||||||
symbols: MutSet<Symbol>,
|
symbols: MutSet<Symbol>,
|
||||||
|
@ -298,7 +299,7 @@ pub struct References<'a> {
|
||||||
pub fn to_type_id<'a>(
|
pub fn to_type_id<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
rigids: &mut References<'a>,
|
rigids: &mut References,
|
||||||
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> TypeId {
|
) -> TypeId {
|
||||||
|
@ -310,7 +311,7 @@ pub fn to_type_id<'a>(
|
||||||
pub fn as_type_id<'a>(
|
pub fn as_type_id<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
rigids: &mut References<'a>,
|
rigids: &mut References,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
@ -324,7 +325,7 @@ pub fn as_type_id<'a>(
|
||||||
pub fn to_type2<'a>(
|
pub fn to_type2<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
references: &mut References<'a>,
|
references: &mut References,
|
||||||
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Type2 {
|
) -> Type2 {
|
||||||
|
@ -375,8 +376,9 @@ pub fn to_type2<'a>(
|
||||||
Type2::Function(arguments, closure_type_id, return_type_id)
|
Type2::Function(arguments, closure_type_id, return_type_id)
|
||||||
}
|
}
|
||||||
BoundVariable(v) => {
|
BoundVariable(v) => {
|
||||||
// a rigid type variable
|
// A rigid type variable. The parser should have already ensured that the name is indeed a lowercase.
|
||||||
match references.named.get(v) {
|
let v = Lowercase::from(*v);
|
||||||
|
match references.named.get(&v) {
|
||||||
Some(var) => Type2::Variable(*var),
|
Some(var) => Type2::Variable(*var),
|
||||||
None => {
|
None => {
|
||||||
let var = env.var_store.fresh();
|
let var = env.var_store.fresh();
|
||||||
|
@ -387,6 +389,9 @@ pub fn to_type2<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Inferred => {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
Wildcard | Malformed(_) => {
|
Wildcard | Malformed(_) => {
|
||||||
let var = env.var_store.fresh();
|
let var = env.var_store.fresh();
|
||||||
|
|
||||||
|
@ -401,7 +406,7 @@ pub fn to_type2<'a>(
|
||||||
let field_types = PoolVec::with_capacity(field_types_map.len() as u32, env.pool);
|
let field_types = PoolVec::with_capacity(field_types_map.len() as u32, env.pool);
|
||||||
|
|
||||||
for (node_id, (label, field)) in field_types.iter_node_ids().zip(field_types_map) {
|
for (node_id, (label, field)) in field_types.iter_node_ids().zip(field_types_map) {
|
||||||
let poolstr = PoolStr::new(label, env.pool);
|
let poolstr = PoolStr::new(label.as_str(), env.pool);
|
||||||
|
|
||||||
let rec_field = match field {
|
let rec_field = match field {
|
||||||
RecordField::Optional(_) => {
|
RecordField::Optional(_) => {
|
||||||
|
@ -480,10 +485,10 @@ pub fn to_type2<'a>(
|
||||||
{
|
{
|
||||||
match loc_var.value {
|
match loc_var.value {
|
||||||
BoundVariable(ident) => {
|
BoundVariable(ident) => {
|
||||||
let var_name = ident;
|
let var_name = Lowercase::from(ident);
|
||||||
|
|
||||||
if let Some(var) = references.named.get(&var_name) {
|
if let Some(var) = references.named.get(&var_name) {
|
||||||
let poolstr = PoolStr::new(var_name, env.pool);
|
let poolstr = PoolStr::new(var_name.as_str(), env.pool);
|
||||||
|
|
||||||
let type_id = env.pool.add(Type2::Variable(*var));
|
let type_id = env.pool.add(Type2::Variable(*var));
|
||||||
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
||||||
|
@ -494,7 +499,7 @@ pub fn to_type2<'a>(
|
||||||
let var = env.var_store.fresh();
|
let var = env.var_store.fresh();
|
||||||
|
|
||||||
references.named.insert(var_name.clone(), var);
|
references.named.insert(var_name.clone(), var);
|
||||||
let poolstr = PoolStr::new(var_name, env.pool);
|
let poolstr = PoolStr::new(var_name.as_str(), env.pool);
|
||||||
|
|
||||||
let type_id = env.pool.add(Type2::Variable(var));
|
let type_id = env.pool.add(Type2::Variable(var));
|
||||||
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
||||||
|
@ -576,10 +581,10 @@ pub fn to_type2<'a>(
|
||||||
fn can_assigned_fields<'a>(
|
fn can_assigned_fields<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
rigids: &mut References<'a>,
|
rigids: &mut References,
|
||||||
fields: &&[Located<roc_parse::ast::AssignedField<'a, roc_parse::ast::TypeAnnotation<'a>>>],
|
fields: &&[Located<roc_parse::ast::AssignedField<'a, roc_parse::ast::TypeAnnotation<'a>>>],
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> MutMap<&'a str, RecordField<Type2>> {
|
) -> MutMap<Lowercase, RecordField<Type2>> {
|
||||||
use roc_parse::ast::AssignedField::*;
|
use roc_parse::ast::AssignedField::*;
|
||||||
use roc_types::types::RecordField::*;
|
use roc_types::types::RecordField::*;
|
||||||
|
|
||||||
|
@ -602,8 +607,8 @@ fn can_assigned_fields<'a>(
|
||||||
let field_type =
|
let field_type =
|
||||||
to_type2(env, scope, rigids, &annotation.value, annotation.region);
|
to_type2(env, scope, rigids, &annotation.value, annotation.region);
|
||||||
|
|
||||||
let label = field_name.value;
|
let label = Lowercase::from(field_name.value);
|
||||||
field_types.insert(label, Required(field_type));
|
field_types.insert(label.clone(), Required(field_type));
|
||||||
|
|
||||||
break 'inner label;
|
break 'inner label;
|
||||||
}
|
}
|
||||||
|
@ -611,20 +616,20 @@ fn can_assigned_fields<'a>(
|
||||||
let field_type =
|
let field_type =
|
||||||
to_type2(env, scope, rigids, &annotation.value, annotation.region);
|
to_type2(env, scope, rigids, &annotation.value, annotation.region);
|
||||||
|
|
||||||
let label = field_name.value;
|
let label = Lowercase::from(field_name.value);
|
||||||
field_types.insert(label.clone(), Optional(field_type));
|
field_types.insert(label.clone(), Optional(field_type));
|
||||||
|
|
||||||
break 'inner label;
|
break 'inner label;
|
||||||
}
|
}
|
||||||
LabelOnly(loc_field_name) => {
|
LabelOnly(loc_field_name) => {
|
||||||
// Interpret { a, b } as { a : a, b : b }
|
// Interpret { a, b } as { a : a, b : b }
|
||||||
let field_name = loc_field_name.value;
|
let field_name = Lowercase::from(loc_field_name.value);
|
||||||
let field_type = {
|
let field_type = {
|
||||||
if let Some(var) = rigids.named.get(&field_name) {
|
if let Some(var) = rigids.named.get(&field_name) {
|
||||||
Type2::Variable(*var)
|
Type2::Variable(*var)
|
||||||
} else {
|
} else {
|
||||||
let field_var = env.var_store.fresh();
|
let field_var = env.var_store.fresh();
|
||||||
rigids.named.insert(field_name, field_var);
|
rigids.named.insert(field_name.clone(), field_var);
|
||||||
Type2::Variable(field_var)
|
Type2::Variable(field_var)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -664,7 +669,7 @@ fn can_assigned_fields<'a>(
|
||||||
fn can_tags<'a>(
|
fn can_tags<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
rigids: &mut References<'a>,
|
rigids: &mut References,
|
||||||
tags: &'a [Located<roc_parse::ast::Tag<'a>>],
|
tags: &'a [Located<roc_parse::ast::Tag<'a>>],
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Vec<(TagName, PoolVec<Type2>)> {
|
) -> Vec<(TagName, PoolVec<Type2>)> {
|
||||||
|
@ -748,7 +753,7 @@ enum TypeApply {
|
||||||
fn to_type_apply<'a>(
|
fn to_type_apply<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
rigids: &mut References<'a>,
|
rigids: &mut References,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
ident: &str,
|
ident: &str,
|
||||||
type_arguments: &[Located<roc_parse::ast::TypeAnnotation<'a>>],
|
type_arguments: &[Located<roc_parse::ast::TypeAnnotation<'a>>],
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::mem_pool::{
|
||||||
pool::Pool, pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone,
|
pool::Pool, pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone,
|
||||||
};
|
};
|
||||||
use roc_collections::all::WyHash;
|
use roc_collections::all::WyHash;
|
||||||
|
use roc_module::ident::Lowercase;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -18,7 +19,7 @@ pub struct Rigids {
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
impl Rigids {
|
impl Rigids {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
named: HashMap<&str, Variable, BuildHasherDefault<WyHash>>,
|
named: HashMap<Lowercase, Variable, BuildHasherDefault<WyHash>>,
|
||||||
unnamed: HashSet<Variable, BuildHasherDefault<WyHash>>,
|
unnamed: HashSet<Variable, BuildHasherDefault<WyHash>>,
|
||||||
pool: &mut Pool,
|
pool: &mut Pool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -26,7 +27,7 @@ impl Rigids {
|
||||||
|
|
||||||
let mut temp_names = Vec::new();
|
let mut temp_names = Vec::new();
|
||||||
|
|
||||||
temp_names.extend(named.iter().map(|(name, var)| (Some(*name), *var)));
|
temp_names.extend(named.iter().map(|(name, var)| (Some(name.as_str()), *var)));
|
||||||
|
|
||||||
temp_names.extend(unnamed.iter().map(|var| (None, *var)));
|
temp_names.extend(unnamed.iter().map(|var| (None, *var)));
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ roc_load = { path = "../compiler/load" }
|
||||||
roc_gen_llvm = { path = "../compiler/gen_llvm", optional = true }
|
roc_gen_llvm = { path = "../compiler/gen_llvm", optional = true }
|
||||||
roc_build = { path = "../compiler/build", default-features = false }
|
roc_build = { path = "../compiler/build", default-features = false }
|
||||||
roc_fmt = { path = "../compiler/fmt" }
|
roc_fmt = { path = "../compiler/fmt" }
|
||||||
roc_reporting = { path = "../compiler/reporting" }
|
roc_reporting = { path = "../reporting" }
|
||||||
roc_editor = { path = "../editor", optional = true }
|
roc_editor = { path = "../editor", optional = true }
|
||||||
roc_linker = { path = "../linker" }
|
roc_linker = { path = "../linker" }
|
||||||
clap = { version = "= 3.0.0-beta.5", default-features = false, features = ["std", "color", "suggestions"] }
|
clap = { version = "= 3.0.0-beta.5", default-features = false, features = ["std", "color", "suggestions"] }
|
||||||
|
|
|
@ -22,7 +22,7 @@ roc_load = { path = "../load" }
|
||||||
roc_gen_llvm = { path = "../gen_llvm", optional = true }
|
roc_gen_llvm = { path = "../gen_llvm", optional = true }
|
||||||
roc_gen_wasm = { path = "../gen_wasm", optional = true }
|
roc_gen_wasm = { path = "../gen_wasm", optional = true }
|
||||||
roc_gen_dev = { path = "../gen_dev", default-features = false }
|
roc_gen_dev = { path = "../gen_dev", default-features = false }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../../reporting" }
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
libloading = "0.7.1"
|
libloading = "0.7.1"
|
||||||
|
|
|
@ -145,6 +145,8 @@ pub fn build_zig_host_native(
|
||||||
_target: &str,
|
_target: &str,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
shared_lib_path: Option<&Path>,
|
shared_lib_path: Option<&Path>,
|
||||||
|
// For compatibility with the non-macOS def above. Keep these in sync.
|
||||||
|
_target_valgrind: bool,
|
||||||
) -> Output {
|
) -> Output {
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
|
|
@ -870,6 +870,9 @@ pub fn listSublist(
|
||||||
len: usize,
|
len: usize,
|
||||||
dec: Dec,
|
dec: Dec,
|
||||||
) callconv(.C) RocList {
|
) callconv(.C) RocList {
|
||||||
|
if (len == 0) {
|
||||||
|
return RocList.empty();
|
||||||
|
}
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
const size = list.len();
|
const size = list.len();
|
||||||
|
|
||||||
|
|
|
@ -1015,6 +1015,25 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// split : List elem, Nat -> { before: List elem, others: List elem }
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::LIST_SPLIT,
|
||||||
|
vec![list_type(flex(TVAR1)), nat_type(),],
|
||||||
|
Box::new(SolvedType::Record {
|
||||||
|
fields: vec![
|
||||||
|
(
|
||||||
|
"before".into(),
|
||||||
|
RecordField::Required(list_type(flex(TVAR1)))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"others".into(),
|
||||||
|
RecordField::Required(list_type(flex(TVAR1)))
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ext: Box::new(SolvedType::EmptyRecord),
|
||||||
|
},),
|
||||||
|
);
|
||||||
|
|
||||||
// drop : List elem, Nat -> List elem
|
// drop : List elem, Nat -> List elem
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::LIST_DROP,
|
Symbol::LIST_DROP,
|
||||||
|
|
|
@ -459,6 +459,9 @@ fn can_annotation_help(
|
||||||
|
|
||||||
Type::Variable(var)
|
Type::Variable(var)
|
||||||
}
|
}
|
||||||
|
Inferred => {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
Malformed(string) => {
|
Malformed(string) => {
|
||||||
malformed(env, region, string);
|
malformed(env, region, string);
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::def::Def;
|
use crate::def::Def;
|
||||||
use crate::expr::{ClosureData, Expr::*};
|
use crate::expr::{ClosureData, Expr::*};
|
||||||
use crate::expr::{Expr, Recursive, WhenBranch};
|
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
use roc_collections::all::SendMap;
|
use roc_collections::all::SendMap;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::operator::CalledVia;
|
use roc_module::operator::CalledVia;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -96,6 +96,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_TAKE_FIRST => list_take_first,
|
LIST_TAKE_FIRST => list_take_first,
|
||||||
LIST_TAKE_LAST => list_take_last,
|
LIST_TAKE_LAST => list_take_last,
|
||||||
LIST_SUBLIST => list_sublist,
|
LIST_SUBLIST => list_sublist,
|
||||||
|
LIST_SPLIT => list_split,
|
||||||
|
LIST_INTERSPERSE => list_intersperse,
|
||||||
LIST_DROP => list_drop,
|
LIST_DROP => list_drop,
|
||||||
LIST_DROP_AT => list_drop_at,
|
LIST_DROP_AT => list_drop_at,
|
||||||
LIST_DROP_FIRST => list_drop_first,
|
LIST_DROP_FIRST => list_drop_first,
|
||||||
|
@ -111,7 +113,6 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_WALK_UNTIL => list_walk_until,
|
LIST_WALK_UNTIL => list_walk_until,
|
||||||
LIST_SORT_WITH => list_sort_with,
|
LIST_SORT_WITH => list_sort_with,
|
||||||
LIST_ANY => list_any,
|
LIST_ANY => list_any,
|
||||||
LIST_INTERSPERSE => list_intersperse,
|
|
||||||
LIST_FIND => list_find,
|
LIST_FIND => list_find,
|
||||||
DICT_LEN => dict_len,
|
DICT_LEN => dict_len,
|
||||||
DICT_EMPTY => dict_empty,
|
DICT_EMPTY => dict_empty,
|
||||||
|
@ -2226,6 +2227,117 @@ fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// List.split : List elem, Nat -> { before: List elem, others: List elem }
|
||||||
|
fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let list_var = var_store.fresh();
|
||||||
|
let index_var = var_store.fresh();
|
||||||
|
|
||||||
|
let list_sym = Symbol::ARG_1;
|
||||||
|
let index_sym = Symbol::ARG_2;
|
||||||
|
|
||||||
|
let clos_sym = Symbol::LIST_SPLIT_CLOS;
|
||||||
|
let clos_start_sym = Symbol::ARG_3;
|
||||||
|
let clos_len_sym = Symbol::ARG_4;
|
||||||
|
|
||||||
|
let clos_fun_var = var_store.fresh();
|
||||||
|
let clos_start_var = var_store.fresh();
|
||||||
|
let clos_len_var = var_store.fresh();
|
||||||
|
let clos_ret_var = var_store.fresh();
|
||||||
|
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
let zero = int(index_var, Variable::NATURAL, 0);
|
||||||
|
|
||||||
|
let clos = Closure(ClosureData {
|
||||||
|
function_type: clos_fun_var,
|
||||||
|
closure_type: var_store.fresh(),
|
||||||
|
closure_ext_var: var_store.fresh(),
|
||||||
|
return_type: clos_ret_var,
|
||||||
|
name: clos_sym,
|
||||||
|
recursive: Recursive::NotRecursive,
|
||||||
|
captured_symbols: vec![(list_sym, clos_ret_var)],
|
||||||
|
arguments: vec![
|
||||||
|
(
|
||||||
|
clos_start_var,
|
||||||
|
no_region(Pattern::Identifier(clos_start_sym)),
|
||||||
|
),
|
||||||
|
(clos_len_var, no_region(Pattern::Identifier(clos_len_sym))),
|
||||||
|
],
|
||||||
|
loc_body: {
|
||||||
|
Box::new(no_region(RunLowLevel {
|
||||||
|
op: LowLevel::ListSublist,
|
||||||
|
args: vec![
|
||||||
|
(clos_ret_var, Var(list_sym)),
|
||||||
|
(clos_start_var, Var(clos_start_sym)),
|
||||||
|
(clos_len_var, Var(clos_len_sym)),
|
||||||
|
],
|
||||||
|
ret_var: clos_ret_var,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let fun = Box::new((
|
||||||
|
clos_fun_var,
|
||||||
|
no_region(clos),
|
||||||
|
var_store.fresh(),
|
||||||
|
clos_ret_var,
|
||||||
|
));
|
||||||
|
|
||||||
|
let get_before = Call(
|
||||||
|
fun.clone(),
|
||||||
|
vec![
|
||||||
|
(index_var, no_region(zero)),
|
||||||
|
(index_var, no_region(Var(index_sym))),
|
||||||
|
],
|
||||||
|
CalledVia::Space,
|
||||||
|
);
|
||||||
|
|
||||||
|
let get_list_len = RunLowLevel {
|
||||||
|
op: LowLevel::ListLen,
|
||||||
|
args: vec![(list_var, Var(list_sym))],
|
||||||
|
ret_var: index_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
let get_others_len = RunLowLevel {
|
||||||
|
op: LowLevel::NumSubWrap,
|
||||||
|
args: vec![(index_var, get_list_len), (index_var, Var(index_sym))],
|
||||||
|
ret_var: index_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
let get_others = Call(
|
||||||
|
fun,
|
||||||
|
vec![
|
||||||
|
(index_var, no_region(Var(index_sym))),
|
||||||
|
(index_var, no_region(get_others_len)),
|
||||||
|
],
|
||||||
|
CalledVia::Space,
|
||||||
|
);
|
||||||
|
|
||||||
|
let before = Field {
|
||||||
|
var: clos_ret_var,
|
||||||
|
region: Region::zero(),
|
||||||
|
loc_expr: Box::new(no_region(get_before)),
|
||||||
|
};
|
||||||
|
let others = Field {
|
||||||
|
var: clos_ret_var,
|
||||||
|
region: Region::zero(),
|
||||||
|
loc_expr: Box::new(no_region(get_others)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = record(
|
||||||
|
vec![("before".into(), before), ("others".into(), others)],
|
||||||
|
var_store,
|
||||||
|
);
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(list_var, list_sym), (index_var, index_sym)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.drop : List elem, Nat -> List elem
|
/// List.drop : List elem, Nat -> List elem
|
||||||
fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let list_var = var_store.fresh();
|
let list_var = var_store.fresh();
|
||||||
|
@ -4388,17 +4500,17 @@ fn tag(name: &'static str, args: Vec<Expr>, var_store: &mut VarStore) -> Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[inline(always)]
|
#[inline(always)]
|
||||||
// fn record(fields: Vec<(Lowercase, Field)>, var_store: &mut VarStore) -> Expr {
|
fn record(fields: Vec<(Lowercase, Field)>, var_store: &mut VarStore) -> Expr {
|
||||||
// let mut send_map = SendMap::default();
|
let mut send_map = SendMap::default();
|
||||||
// for (k, v) in fields {
|
for (k, v) in fields {
|
||||||
// send_map.insert(k, v);
|
send_map.insert(k, v);
|
||||||
// }
|
}
|
||||||
// Expr::Record {
|
Expr::Record {
|
||||||
// record_var: var_store.fresh(),
|
record_var: var_store.fresh(),
|
||||||
// fields: send_map,
|
fields: send_map,
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn defn(
|
fn defn(
|
||||||
|
|
|
@ -180,7 +180,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Wildcard | BoundVariable(_) | Malformed(_) => false,
|
Wildcard | Inferred | BoundVariable(_) | Malformed(_) => false,
|
||||||
Function(args, result) => {
|
Function(args, result) => {
|
||||||
(&result.value).is_multiline()
|
(&result.value).is_multiline()
|
||||||
|| args.iter().any(|loc_arg| (&loc_arg.value).is_multiline())
|
|| args.iter().any(|loc_arg| (&loc_arg.value).is_multiline())
|
||||||
|
@ -279,6 +279,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||||
}
|
}
|
||||||
BoundVariable(v) => buf.push_str(v),
|
BoundVariable(v) => buf.push_str(v),
|
||||||
Wildcard => buf.push('*'),
|
Wildcard => buf.push('*'),
|
||||||
|
Inferred => buf.push('_'),
|
||||||
|
|
||||||
TagUnion { tags, ext } => {
|
TagUnion { tags, ext } => {
|
||||||
format_sequence!(buf, indent, '[', ']', tags, newlines, Tag);
|
format_sequence!(buf, indent, '[', ']', tags, newlines, Tag);
|
||||||
|
|
|
@ -70,7 +70,7 @@ This is the general procedure I follow with some helpful links:
|
||||||
A good place to look for missing features is in the test files for generation in [test_gen](https://github.com/rtfeldman/roc/tree/trunk/compiler/test_gen). Any test that is not enabled for the `gen-dev` feature still needs to be added to the dev backend. Eventually all features should be enabled for the dev backend.
|
A good place to look for missing features is in the test files for generation in [test_gen](https://github.com/rtfeldman/roc/tree/trunk/compiler/test_gen). Any test that is not enabled for the `gen-dev` feature still needs to be added to the dev backend. Eventually all features should be enabled for the dev backend.
|
||||||
1. Pick/write the simplest test case you can find for the new feature.
|
1. Pick/write the simplest test case you can find for the new feature.
|
||||||
Just add `feature = "gen-dev"` to the `cfg` line for the test case.
|
Just add `feature = "gen-dev"` to the `cfg` line for the test case.
|
||||||
1. Uncomment the code to print out procedures [from here](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/tests/helpers/eval.rs) and run the test.
|
1. Uncomment the code to print out procedures [from here](https://github.com/rtfeldman/roc/blob/b03ed18553569314a420d5bf1fb0ead4b6b5ecda/compiler/test_gen/src/helpers/dev.rs#L76) and run the test.
|
||||||
It should fail and print out the mono ir for this test case.
|
It should fail and print out the mono ir for this test case.
|
||||||
Seeing the actual mono ir tends to be very helpful for complex additions.
|
Seeing the actual mono ir tends to be very helpful for complex additions.
|
||||||
1. Generally it will fail in one of the match statements in the [Backend](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/lib.rs) trait.
|
1. Generally it will fail in one of the match statements in the [Backend](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/lib.rs) trait.
|
||||||
|
|
|
@ -446,6 +446,10 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
unimplemented!("stack offsets over 32k are not yet implement for AArch64");
|
unimplemented!("stack offsets over 32k are not yet implement for AArch64");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn neg_reg64_reg64(_buf: &mut Vec<'_, u8>, _dst: AArch64GeneralReg, _src: AArch64GeneralReg) {
|
||||||
|
unimplemented!("neg is not yet implement for AArch64");
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn sub_reg64_reg64_imm32(
|
fn sub_reg64_reg64_imm32(
|
||||||
|
|
|
@ -149,6 +149,7 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
fn mov_stack32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
fn mov_stack32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
||||||
fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
|
fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
|
||||||
|
|
||||||
|
fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||||
fn imul_reg64_reg64_reg64(
|
fn imul_reg64_reg64_reg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
dst: GeneralReg,
|
dst: GeneralReg,
|
||||||
|
@ -786,6 +787,23 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_num_neg(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
src: &Symbol,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
let dst_reg = self.claim_general_reg(dst)?;
|
||||||
|
let src_reg = self.load_to_general_reg(src)?;
|
||||||
|
ASM::neg_reg64_reg64(&mut self.buf, dst_reg, src_reg);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
x => Err(format!("NumNeg: layout, {:?}, not implemented yet", x)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_num_sub(
|
fn build_num_sub(
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
|
|
|
@ -1053,6 +1053,11 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
||||||
mov_base64_offset32_reg64(buf, X86_64GeneralReg::RSP, offset, src)
|
mov_base64_offset32_reg64(buf, X86_64GeneralReg::RSP, offset, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
|
||||||
|
mov_reg64_reg64(buf, dst, src);
|
||||||
|
neg_reg64(buf, dst);
|
||||||
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn sub_reg64_reg64_imm32(
|
fn sub_reg64_reg64_imm32(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
|
|
|
@ -368,6 +368,18 @@ where
|
||||||
);
|
);
|
||||||
self.build_num_mul(sym, &args[0], &args[1], ret_layout)
|
self.build_num_mul(sym, &args[0], &args[1], ret_layout)
|
||||||
}
|
}
|
||||||
|
LowLevel::NumNeg => {
|
||||||
|
debug_assert_eq!(
|
||||||
|
1,
|
||||||
|
args.len(),
|
||||||
|
"NumNeg: expected to have exactly one argument"
|
||||||
|
);
|
||||||
|
debug_assert_eq!(
|
||||||
|
arg_layouts[0], *ret_layout,
|
||||||
|
"NumNeg: expected to have the same argument and return layout"
|
||||||
|
);
|
||||||
|
self.build_num_neg(sym, &args[0], ret_layout)
|
||||||
|
}
|
||||||
LowLevel::NumPowInt => self.build_fn_call(
|
LowLevel::NumPowInt => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::NUM_POW_INT[IntWidth::I64].to_string(),
|
bitcode::NUM_POW_INT[IntWidth::I64].to_string(),
|
||||||
|
@ -459,6 +471,14 @@ where
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
) -> Result<(), String>;
|
) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// build_num_neg stores the negated value of src into dst.
|
||||||
|
fn build_num_neg(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
src: &Symbol,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
) -> Result<(), String>;
|
||||||
|
|
||||||
/// build_num_sub stores the `src1 - src2` difference into dst.
|
/// build_num_sub stores the `src1 - src2` difference into dst.
|
||||||
fn build_num_sub(
|
fn build_num_sub(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
BIN
compiler/gen_wasm/lib/libc.a
Normal file
BIN
compiler/gen_wasm/lib/libc.a
Normal file
Binary file not shown.
|
@ -444,6 +444,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stmt::Refcounting(_modify, following) => {
|
||||||
|
// TODO: actually deal with refcounting. For hello world, we just skipped it.
|
||||||
|
self.build_stmt(following, ret_layout)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
x => Err(format!("statement not yet implemented: {:?}", x)),
|
x => Err(format!("statement not yet implemented: {:?}", x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -472,7 +479,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
CallType::ByName { name: func_sym, .. } => {
|
CallType::ByName { name: func_sym, .. } => {
|
||||||
// If this function is just a lowlevel wrapper, then inline it
|
// If this function is just a lowlevel wrapper, then inline it
|
||||||
if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
|
if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
|
||||||
return self.build_low_level(lowlevel, arguments, wasm_layout);
|
return self.build_low_level(lowlevel, arguments, *sym, wasm_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut wasm_args_tmp: Vec<Symbol>;
|
let mut wasm_args_tmp: Vec<Symbol>;
|
||||||
|
@ -517,7 +524,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
CallType::LowLevel { op: lowlevel, .. } => {
|
CallType::LowLevel { op: lowlevel, .. } => {
|
||||||
self.build_low_level(*lowlevel, arguments, wasm_layout)
|
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
|
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
|
||||||
|
@ -556,9 +563,18 @@ impl<'a> WasmBackend<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
lowlevel: LowLevel,
|
lowlevel: LowLevel,
|
||||||
arguments: &'a [Symbol],
|
arguments: &'a [Symbol],
|
||||||
|
return_sym: Symbol,
|
||||||
return_layout: WasmLayout,
|
return_layout: WasmLayout,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
self.storage.load_symbols(&mut self.code_builder, arguments);
|
// Load symbols using the "fast calling convention" that Zig uses instead of the C ABI we normally use.
|
||||||
|
// It's only different from the C ABI for small structs, and we are using Zig for all of those cases.
|
||||||
|
// This is a workaround for a bug in Zig. If later versions fix it, we can change to the C ABI.
|
||||||
|
self.storage.load_symbols_fastcc(
|
||||||
|
&mut self.code_builder,
|
||||||
|
arguments,
|
||||||
|
return_sym,
|
||||||
|
&return_layout,
|
||||||
|
);
|
||||||
|
|
||||||
let build_result = decode_low_level(
|
let build_result = decode_low_level(
|
||||||
&mut self.code_builder,
|
&mut self.code_builder,
|
||||||
|
@ -572,7 +588,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
match build_result {
|
match build_result {
|
||||||
Done => Ok(()),
|
Done => Ok(()),
|
||||||
BuiltinCall(name) => {
|
BuiltinCall(name) => {
|
||||||
self.call_imported_builtin(name, arguments, &return_layout);
|
self.call_zig_builtin(name, arguments, &return_layout);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
NotImplemented => Err(format!(
|
NotImplemented => Err(format!(
|
||||||
|
@ -626,8 +642,8 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.lookup_string_constant(string, sym, layout);
|
self.lookup_string_constant(string, sym, layout);
|
||||||
|
|
||||||
self.code_builder.get_local(local_id);
|
self.code_builder.get_local(local_id);
|
||||||
self.code_builder.insert_memory_relocation(linker_sym_index);
|
self.code_builder
|
||||||
self.code_builder.i32_const(elements_addr as i32);
|
.i32_const_mem_addr(elements_addr, linker_sym_index);
|
||||||
self.code_builder.i32_store(Align::Bytes4, offset);
|
self.code_builder.i32_store(Align::Bytes4, offset);
|
||||||
|
|
||||||
self.code_builder.get_local(local_id);
|
self.code_builder.get_local(local_id);
|
||||||
|
@ -749,12 +765,10 @@ impl<'a> WasmBackend<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_imported_builtin(
|
/// Generate a call instruction to a Zig builtin function.
|
||||||
&mut self,
|
/// And if we haven't seen it before, add an Import and linker data for it.
|
||||||
name: &'a str,
|
/// Zig calls use LLVM's "fast" calling convention rather than our usual C ABI.
|
||||||
arguments: &[Symbol],
|
fn call_zig_builtin(&mut self, name: &'a str, arguments: &[Symbol], ret_layout: &WasmLayout) {
|
||||||
ret_layout: &WasmLayout,
|
|
||||||
) {
|
|
||||||
let (fn_index, linker_symbol_index) = match self.builtin_sym_index_map.get(name) {
|
let (fn_index, linker_symbol_index) = match self.builtin_sym_index_map.get(name) {
|
||||||
Some(sym_idx) => match &self.linker_symbols[*sym_idx] {
|
Some(sym_idx) => match &self.linker_symbols[*sym_idx] {
|
||||||
SymInfo::Function(WasmObjectSymbol::Imported { index, .. }) => {
|
SymInfo::Function(WasmObjectSymbol::Imported { index, .. }) => {
|
||||||
|
@ -764,11 +778,29 @@ impl<'a> WasmBackend<'a> {
|
||||||
},
|
},
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
let mut param_types = Vec::with_capacity_in(arguments.len(), self.env.arena);
|
let mut param_types = Vec::with_capacity_in(1 + arguments.len(), self.env.arena);
|
||||||
param_types.extend(arguments.iter().map(|a| self.storage.get(a).value_type()));
|
|
||||||
|
let ret_type = if ret_layout.is_stack_memory() {
|
||||||
|
param_types.push(ValueType::I32);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ret_layout.value_type())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Zig's "fast calling convention" packs structs into CPU registers (stack machine slots) if possible.
|
||||||
|
// If they're small enough they can go into an I32 or I64. If they're big, they're pointers (I32).
|
||||||
|
for arg in arguments {
|
||||||
|
param_types.push(match self.storage.get(arg) {
|
||||||
|
StoredValue::StackMemory { size, .. } if *size > 4 && *size <= 8 => {
|
||||||
|
ValueType::I64
|
||||||
|
}
|
||||||
|
stored => stored.value_type(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let signature_index = self.module.types.insert(Signature {
|
let signature_index = self.module.types.insert(Signature {
|
||||||
param_types,
|
param_types,
|
||||||
ret_type: Some(ret_layout.value_type()), // TODO: handle builtins with no return value
|
ret_type,
|
||||||
});
|
});
|
||||||
|
|
||||||
let import_index = self.module.import.entries.len() as u32;
|
let import_index = self.module.import.entries.len() as u32;
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub mod wasm_module;
|
||||||
use bumpalo::{self, collections::Vec, Bump};
|
use bumpalo::{self, collections::Vec, Bump};
|
||||||
|
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::ir::{Proc, ProcLayout};
|
use roc_mono::ir::{Proc, ProcLayout};
|
||||||
use roc_mono::layout::LayoutIds;
|
use roc_mono::layout::LayoutIds;
|
||||||
|
@ -36,7 +37,7 @@ pub fn build_module<'a>(
|
||||||
env: &'a Env,
|
env: &'a Env,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) -> Result<std::vec::Vec<u8>, String> {
|
) -> Result<std::vec::Vec<u8>, String> {
|
||||||
let mut wasm_module = build_module_help(env, procedures)?;
|
let (mut wasm_module, _) = build_module_help(env, procedures)?;
|
||||||
let mut buffer = std::vec::Vec::with_capacity(4096);
|
let mut buffer = std::vec::Vec::with_capacity(4096);
|
||||||
wasm_module.serialize_mut(&mut buffer);
|
wasm_module.serialize_mut(&mut buffer);
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
|
@ -45,36 +46,54 @@ pub fn build_module<'a>(
|
||||||
pub fn build_module_help<'a>(
|
pub fn build_module_help<'a>(
|
||||||
env: &'a Env,
|
env: &'a Env,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) -> Result<WasmModule<'a>, String> {
|
) -> Result<(WasmModule<'a>, u32), String> {
|
||||||
let mut layout_ids = LayoutIds::default();
|
let mut layout_ids = LayoutIds::default();
|
||||||
let mut proc_symbols = Vec::with_capacity_in(procedures.len(), env.arena);
|
let mut generated_procs = Vec::with_capacity_in(procedures.len(), env.arena);
|
||||||
|
let mut generated_symbols = Vec::with_capacity_in(procedures.len(), env.arena);
|
||||||
let mut linker_symbols = Vec::with_capacity_in(procedures.len() * 2, env.arena);
|
let mut linker_symbols = Vec::with_capacity_in(procedures.len() * 2, env.arena);
|
||||||
let mut exports = Vec::with_capacity_in(procedures.len(), env.arena);
|
let mut exports = Vec::with_capacity_in(4, env.arena);
|
||||||
|
let mut main_fn_index = None;
|
||||||
|
|
||||||
// Collect the symbols & names for the procedures
|
// Collect the symbols & names for the procedures,
|
||||||
for (i, (sym, layout)) in procedures.keys().enumerate() {
|
// and filter out procs we're going to inline
|
||||||
proc_symbols.push(*sym);
|
let mut fn_index: u32 = 0;
|
||||||
|
for ((sym, layout), proc) in procedures.into_iter() {
|
||||||
|
if LowLevel::from_inlined_wrapper(sym).is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
generated_procs.push(proc);
|
||||||
|
generated_symbols.push(sym);
|
||||||
|
|
||||||
let fn_name = layout_ids
|
let fn_name = layout_ids
|
||||||
.get_toplevel(*sym, layout)
|
.get_toplevel(sym, &layout)
|
||||||
.to_symbol_string(*sym, &env.interns);
|
.to_symbol_string(sym, &env.interns);
|
||||||
|
|
||||||
if env.exposed_to_host.contains(sym) {
|
if env.exposed_to_host.contains(&sym) {
|
||||||
|
main_fn_index = Some(fn_index);
|
||||||
exports.push(Export {
|
exports.push(Export {
|
||||||
name: fn_name.clone(),
|
name: fn_name.clone(),
|
||||||
ty: ExportType::Func,
|
ty: ExportType::Func,
|
||||||
index: i as u32,
|
index: fn_index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let linker_sym = SymInfo::for_function(i as u32, fn_name);
|
let linker_sym = SymInfo::for_function(fn_index, fn_name);
|
||||||
linker_symbols.push(linker_sym);
|
linker_symbols.push(linker_sym);
|
||||||
|
|
||||||
|
fn_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop: Build the Wasm module
|
// Build the Wasm module
|
||||||
let (mut module, linker_symbols) = {
|
let (mut module, linker_symbols) = {
|
||||||
let mut backend = WasmBackend::new(env, layout_ids, proc_symbols, linker_symbols, exports);
|
let mut backend = WasmBackend::new(
|
||||||
for ((sym, _), proc) in procedures.into_iter() {
|
env,
|
||||||
|
layout_ids,
|
||||||
|
generated_symbols.clone(),
|
||||||
|
linker_symbols,
|
||||||
|
exports,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (proc, sym) in generated_procs.into_iter().zip(generated_symbols) {
|
||||||
backend.build_proc(proc, sym)?;
|
backend.build_proc(proc, sym)?;
|
||||||
}
|
}
|
||||||
(backend.module, backend.linker_symbols)
|
(backend.module, backend.linker_symbols)
|
||||||
|
@ -83,7 +102,7 @@ pub fn build_module_help<'a>(
|
||||||
let symbol_table = LinkingSubSection::SymbolTable(linker_symbols);
|
let symbol_table = LinkingSubSection::SymbolTable(linker_symbols);
|
||||||
module.linking.subsections.push(symbol_table);
|
module.linking.subsections.push(symbol_table);
|
||||||
|
|
||||||
Ok(module)
|
Ok((module, main_fn_index.unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CopyMemoryConfig {
|
pub struct CopyMemoryConfig {
|
||||||
|
|
|
@ -27,16 +27,18 @@ pub fn decode_low_level<'a>(
|
||||||
let panic_ret_type = || panic!("Invalid return layout for {:?}: {:?}", lowlevel, ret_layout);
|
let panic_ret_type = || panic!("Invalid return layout for {:?}: {:?}", lowlevel, ret_layout);
|
||||||
|
|
||||||
match lowlevel {
|
match lowlevel {
|
||||||
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
|
StrConcat => return BuiltinCall(bitcode::STR_CONCAT),
|
||||||
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft
|
|
||||||
| StrTrimRight | StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim
|
StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt | StrEndsWith | StrSplit
|
||||||
| ListLen | ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse
|
| StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft | StrTrimRight
|
||||||
| ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap
|
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen
|
||||||
| ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
| ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
||||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
|
||||||
| ListSublist | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty
|
| ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil
|
||||||
| DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
|
| ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListSublist
|
||||||
| DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
|
| ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty | DictInsert
|
||||||
|
| DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues | DictUnion
|
||||||
|
| DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use roc_collections::all::MutMap;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
use crate::layout::WasmLayout;
|
use crate::layout::WasmLayout;
|
||||||
use crate::wasm_module::{CodeBuilder, LocalId, ValueType, VmSymbolState};
|
use crate::wasm_module::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
|
||||||
use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, PTR_SIZE, PTR_TYPE};
|
use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, PTR_SIZE, PTR_TYPE};
|
||||||
|
|
||||||
pub enum StoredValueKind {
|
pub enum StoredValueKind {
|
||||||
|
@ -194,6 +194,63 @@ impl<'a> Storage<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load a single symbol using the C Calling Convention
|
||||||
|
/// *Private* because external code should always load symbols in bulk (see load_symbols)
|
||||||
|
fn load_symbol_ccc(&mut self, code_builder: &mut CodeBuilder, sym: Symbol) {
|
||||||
|
let storage = self.get(&sym).to_owned();
|
||||||
|
match storage {
|
||||||
|
StoredValue::VirtualMachineStack {
|
||||||
|
vm_state,
|
||||||
|
value_type,
|
||||||
|
size,
|
||||||
|
} => {
|
||||||
|
let next_local_id = self.get_next_local_id();
|
||||||
|
let maybe_next_vm_state = code_builder.load_symbol(sym, vm_state, next_local_id);
|
||||||
|
match maybe_next_vm_state {
|
||||||
|
// The act of loading the value changed the VM state, so update it
|
||||||
|
Some(next_vm_state) => {
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
sym,
|
||||||
|
StoredValue::VirtualMachineStack {
|
||||||
|
vm_state: next_vm_state,
|
||||||
|
value_type,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Loading the value required creating a new local, because
|
||||||
|
// it was not in a convenient position in the VM stack.
|
||||||
|
self.local_types.push(value_type);
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
sym,
|
||||||
|
StoredValue::Local {
|
||||||
|
local_id: next_local_id,
|
||||||
|
value_type,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StoredValue::Local { local_id, .. } => {
|
||||||
|
code_builder.get_local(local_id);
|
||||||
|
code_builder.set_top_symbol(sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
StoredValue::StackMemory { location, .. } => {
|
||||||
|
let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer);
|
||||||
|
code_builder.get_local(local_id);
|
||||||
|
if offset != 0 {
|
||||||
|
code_builder.i32_const(offset as i32);
|
||||||
|
code_builder.i32_add();
|
||||||
|
}
|
||||||
|
code_builder.set_top_symbol(sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Load symbols to the top of the VM stack
|
/// Load symbols to the top of the VM stack
|
||||||
/// Avoid calling this method in a loop with one symbol at a time! It will work,
|
/// Avoid calling this method in a loop with one symbol at a time! It will work,
|
||||||
/// but it generates very inefficient Wasm code.
|
/// but it generates very inefficient Wasm code.
|
||||||
|
@ -204,61 +261,60 @@ impl<'a> Storage<'a> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for sym in symbols.iter() {
|
for sym in symbols.iter() {
|
||||||
let storage = self.get(sym).to_owned();
|
self.load_symbol_ccc(code_builder, *sym);
|
||||||
match storage {
|
}
|
||||||
StoredValue::VirtualMachineStack {
|
}
|
||||||
vm_state,
|
|
||||||
value_type,
|
/// Load symbols in a way compatible with LLVM's "fast calling convention"
|
||||||
size,
|
/// A bug in Zig means it always uses this for Wasm even when we specify C calling convention.
|
||||||
} => {
|
/// It squashes small structs into primitive values where possible, avoiding stack memory
|
||||||
let next_local_id = self.get_next_local_id();
|
/// in favour of CPU registers (or VM stack values, which eventually become CPU registers).
|
||||||
let maybe_next_vm_state =
|
/// We need to convert some of our structs from our internal C-like representation to work with Zig.
|
||||||
code_builder.load_symbol(*sym, vm_state, next_local_id);
|
/// We are sticking to C ABI for better compatibility on the platform side.
|
||||||
match maybe_next_vm_state {
|
pub fn load_symbols_fastcc(
|
||||||
// The act of loading the value changed the VM state, so update it
|
&mut self,
|
||||||
Some(next_vm_state) => {
|
code_builder: &mut CodeBuilder,
|
||||||
self.symbol_storage_map.insert(
|
symbols: &[Symbol],
|
||||||
*sym,
|
return_symbol: Symbol,
|
||||||
StoredValue::VirtualMachineStack {
|
return_layout: &WasmLayout,
|
||||||
vm_state: next_vm_state,
|
) {
|
||||||
value_type,
|
// Note: we are not doing verify_stack_match in this case so we may generate more code.
|
||||||
size,
|
// We would need more bookkeeping in CodeBuilder to track which representation is on the stack!
|
||||||
},
|
|
||||||
);
|
if return_layout.is_stack_memory() {
|
||||||
}
|
// Load the address where the return value should be written
|
||||||
None => {
|
// Apparently for return values we still use a pointer to stack memory
|
||||||
// Loading the value required creating a new local, because
|
self.load_symbol_ccc(code_builder, return_symbol);
|
||||||
// it was not in a convenient position in the VM stack.
|
};
|
||||||
self.local_types.push(value_type);
|
|
||||||
self.symbol_storage_map.insert(
|
for sym in symbols {
|
||||||
*sym,
|
if let StoredValue::StackMemory {
|
||||||
StoredValue::Local {
|
location,
|
||||||
local_id: next_local_id,
|
size,
|
||||||
value_type,
|
alignment_bytes,
|
||||||
size,
|
} = self.get(sym)
|
||||||
},
|
{
|
||||||
);
|
if *size == 0 {
|
||||||
}
|
unimplemented!("Passing zero-sized values is not implemented yet");
|
||||||
}
|
} else if *size > 8 {
|
||||||
}
|
return self.load_symbol_ccc(code_builder, *sym);
|
||||||
StoredValue::Local { local_id, .. }
|
|
||||||
| StoredValue::StackMemory {
|
|
||||||
location: StackMemoryLocation::PointerArg(local_id),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
code_builder.get_local(local_id);
|
|
||||||
code_builder.set_top_symbol(*sym);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredValue::StackMemory {
|
let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer);
|
||||||
location: StackMemoryLocation::FrameOffset(offset),
|
code_builder.get_local(local_id);
|
||||||
..
|
let align = Align::from(*alignment_bytes);
|
||||||
} => {
|
|
||||||
code_builder.get_local(self.stack_frame_pointer.unwrap());
|
if *size == 1 {
|
||||||
code_builder.i32_const(offset as i32);
|
code_builder.i32_load8_u(align, offset);
|
||||||
code_builder.i32_add();
|
} else if *size == 2 {
|
||||||
code_builder.set_top_symbol(*sym);
|
code_builder.i32_load16_u(align, offset);
|
||||||
|
} else if *size <= 4 {
|
||||||
|
code_builder.i32_load(align, offset);
|
||||||
|
} else {
|
||||||
|
code_builder.i64_load(align, offset);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.load_symbol_ccc(code_builder, *sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use super::opcodes::{OpCode, OpCode::*};
|
||||||
use super::serialize::{SerialBuffer, Serialize};
|
use super::serialize::{SerialBuffer, Serialize};
|
||||||
use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
||||||
|
|
||||||
const ENABLE_DEBUG_LOG: bool = true;
|
const ENABLE_DEBUG_LOG: bool = false;
|
||||||
macro_rules! log_instruction {
|
macro_rules! log_instruction {
|
||||||
($($x: expr),+) => {
|
($($x: expr),+) => {
|
||||||
if ENABLE_DEBUG_LOG { println!($($x,)*); }
|
if ENABLE_DEBUG_LOG { println!($($x,)*); }
|
||||||
|
@ -572,11 +572,14 @@ impl<'a> CodeBuilder<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a linker relocation for a memory address
|
/// Insert a const reference to a memory address
|
||||||
pub fn insert_memory_relocation(&mut self, symbol_index: u32) {
|
pub fn i32_const_mem_addr(&mut self, addr: u32, symbol_index: u32) {
|
||||||
|
self.inst_base(I32CONST, 0, true);
|
||||||
|
let offset = self.code.len() as u32;
|
||||||
|
self.code.encode_padded_u32(addr);
|
||||||
self.relocations.push(RelocationEntry::Offset {
|
self.relocations.push(RelocationEntry::Offset {
|
||||||
type_id: OffsetRelocType::MemoryAddrLeb,
|
type_id: OffsetRelocType::MemoryAddrLeb,
|
||||||
offset: self.code.len() as u32,
|
offset,
|
||||||
symbol_index,
|
symbol_index,
|
||||||
addend: 0,
|
addend: 0,
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,6 +29,8 @@ pub enum SectionId {
|
||||||
Element = 9,
|
Element = 9,
|
||||||
Code = 10,
|
Code = 10,
|
||||||
Data = 11,
|
Data = 11,
|
||||||
|
/// DataCount section is unused. Only needed for single-pass validation of
|
||||||
|
/// memory.init and data.drop, which we don't use
|
||||||
DataCount = 12,
|
DataCount = 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,8 +241,7 @@ impl<'a> Serialize for ImportSection<'a> {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionSection<'a> {
|
pub struct FunctionSection<'a> {
|
||||||
/// Private. See WasmModule::add_function_signature
|
pub signature_indices: Vec<'a, u32>,
|
||||||
signature_indices: Vec<'a, u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FunctionSection<'a> {
|
impl<'a> FunctionSection<'a> {
|
||||||
|
@ -525,42 +526,6 @@ impl Serialize for DataSection<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************
|
|
||||||
*
|
|
||||||
* Data Count section
|
|
||||||
*
|
|
||||||
* Pre-declares the number of segments in the Data section.
|
|
||||||
* This helps the runtime to validate the module in a single pass.
|
|
||||||
* The order of sections is DataCount -> Code -> Data
|
|
||||||
*
|
|
||||||
*******************************************************************/
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DataCountSection {
|
|
||||||
count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataCountSection {
|
|
||||||
fn new(data_section: &DataSection<'_>) -> Self {
|
|
||||||
let count = data_section
|
|
||||||
.segments
|
|
||||||
.iter()
|
|
||||||
.filter(|seg| !seg.init.is_empty())
|
|
||||||
.count() as u32;
|
|
||||||
DataCountSection { count }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for DataCountSection {
|
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
|
||||||
if self.count > 0 {
|
|
||||||
let header_indices = write_section_header(buffer, SectionId::DataCount);
|
|
||||||
buffer.encode_u32(self.count);
|
|
||||||
update_section_size(buffer, header_indices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************
|
/*******************************************************************
|
||||||
*
|
*
|
||||||
* Module
|
* Module
|
||||||
|
@ -658,11 +623,6 @@ impl<'a> WasmModule<'a> {
|
||||||
counter.serialize_and_count(buffer, &self.start);
|
counter.serialize_and_count(buffer, &self.start);
|
||||||
counter.serialize_and_count(buffer, &self.element);
|
counter.serialize_and_count(buffer, &self.element);
|
||||||
|
|
||||||
// Data Count section forward-declares the size of the Data section
|
|
||||||
// so that Code section can be validated in one pass
|
|
||||||
let data_count_section = DataCountSection::new(&self.data);
|
|
||||||
counter.serialize_and_count(buffer, &data_count_section);
|
|
||||||
|
|
||||||
// Code section is the only one with relocations so we can stop counting
|
// Code section is the only one with relocations so we can stop counting
|
||||||
let code_section_index = counter.section_index;
|
let code_section_index = counter.section_index;
|
||||||
let code_section_body_index = self
|
let code_section_body_index = self
|
||||||
|
|
|
@ -18,7 +18,7 @@ roc_unify = { path = "../unify" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../../reporting" }
|
||||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||||
ven_pretty = { path = "../../vendor/pretty" }
|
ven_pretty = { path = "../../vendor/pretty" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
|
|
@ -1074,6 +1074,8 @@ define_builtins! {
|
||||||
49 LIST_SUBLIST: "sublist"
|
49 LIST_SUBLIST: "sublist"
|
||||||
50 LIST_INTERSPERSE: "intersperse"
|
50 LIST_INTERSPERSE: "intersperse"
|
||||||
51 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
51 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
||||||
|
52 LIST_SPLIT: "split"
|
||||||
|
53 LIST_SPLIT_CLOS: "#splitClos"
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
|
|
|
@ -17,3 +17,5 @@ pretty_assertions = "1.0.0"
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "1.0.0"
|
quickcheck_macros = "1.0.0"
|
||||||
|
diff = "0.1.12"
|
||||||
|
ansi_term = "0.12.1"
|
||||||
|
|
|
@ -240,6 +240,9 @@ pub enum TypeAnnotation<'a> {
|
||||||
tags: Collection<'a, Loc<Tag<'a>>>,
|
tags: Collection<'a, Loc<Tag<'a>>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// '_', indicating the compiler should infer the type
|
||||||
|
Inferred,
|
||||||
|
|
||||||
/// The `*` type variable, e.g. in (List *)
|
/// The `*` type variable, e.g. in (List *)
|
||||||
Wildcard,
|
Wildcard,
|
||||||
|
|
||||||
|
|
|
@ -626,6 +626,7 @@ pub enum EType<'a> {
|
||||||
TApply(ETypeApply, Row, Col),
|
TApply(ETypeApply, Row, Col),
|
||||||
TBadTypeVariable(Row, Col),
|
TBadTypeVariable(Row, Col),
|
||||||
TWildcard(Row, Col),
|
TWildcard(Row, Col),
|
||||||
|
TInferred(Row, Col),
|
||||||
///
|
///
|
||||||
TStart(Row, Col),
|
TStart(Row, Col),
|
||||||
TEnd(Row, Col),
|
TEnd(Row, Col),
|
||||||
|
|
|
@ -55,6 +55,7 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, ETy
|
||||||
and!(
|
and!(
|
||||||
one_of!(
|
one_of!(
|
||||||
loc_wildcard(),
|
loc_wildcard(),
|
||||||
|
loc_inferred(),
|
||||||
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
|
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
|
||||||
loc!(specialize(EType::TRecord, record_type(min_indent))),
|
loc!(specialize(EType::TRecord, record_type(min_indent))),
|
||||||
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))),
|
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))),
|
||||||
|
@ -111,6 +112,15 @@ fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The `_` indicating an inferred type, e.g. in (List _)
|
||||||
|
fn loc_inferred<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||||
|
map!(loc!(word1(b'_', EType::TInferred)), |loc_val: Located<
|
||||||
|
(),
|
||||||
|
>| {
|
||||||
|
loc_val.map(|_| TypeAnnotation::Inferred)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||||
use crate::ast::Spaceable;
|
use crate::ast::Spaceable;
|
||||||
|
|
||||||
|
@ -119,6 +129,7 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotatio
|
||||||
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentStart)),
|
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentStart)),
|
||||||
one_of!(
|
one_of!(
|
||||||
loc_wildcard(),
|
loc_wildcard(),
|
||||||
|
loc_inferred(),
|
||||||
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
|
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
|
||||||
loc!(specialize(EType::TRecord, record_type(min_indent))),
|
loc!(specialize(EType::TRecord, record_type(min_indent))),
|
||||||
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))),
|
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))),
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 0-1| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
},
|
||||||
|
|L 0-0, C 2-3| Plus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 0-0, C 4-5| Num(
|
||||||
|
"2",
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
x + 2
|
|
@ -0,0 +1,13 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 0-1| Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
|L 0-0, C 3-4| Plus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 0-0, C 7-8| Num(
|
||||||
|
"2",
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
1 + 2
|
|
@ -0,0 +1,14 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-4| GlobalTag(
|
||||||
|
"Whee",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
|L 0-0, C 5-7| Num(
|
||||||
|
"12",
|
||||||
|
),
|
||||||
|
|L 0-0, C 8-10| Num(
|
||||||
|
"34",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
Whee 12 34
|
|
@ -0,0 +1,18 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-4| GlobalTag(
|
||||||
|
"Whee",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
|L 0-0, C 6-8| ParensAround(
|
||||||
|
Num(
|
||||||
|
"12",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|L 0-0, C 11-13| ParensAround(
|
||||||
|
Num(
|
||||||
|
"34",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
Whee (12) (34)
|
|
@ -0,0 +1,14 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-5| PrivateTag(
|
||||||
|
"@Whee",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
|L 0-0, C 6-8| Num(
|
||||||
|
"12",
|
||||||
|
),
|
||||||
|
|L 0-0, C 9-11| Num(
|
||||||
|
"34",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
@Whee 12 34
|
|
@ -0,0 +1,21 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-1| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
|L 0-0, C 2-3| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "b",
|
||||||
|
},
|
||||||
|
|L 0-0, C 4-5| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "c",
|
||||||
|
},
|
||||||
|
|L 0-0, C 6-7| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "d",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
a b c d
|
|
@ -0,0 +1,15 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-4| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "whee",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
|L 0-0, C 6-8| Num(
|
||||||
|
"12",
|
||||||
|
),
|
||||||
|
|L 0-0, C 10-12| Num(
|
||||||
|
"34",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
whee 12 34
|
|
@ -0,0 +1,19 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-5| UnaryOp(
|
||||||
|
|L 0-0, C 1-5| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "whee",
|
||||||
|
},
|
||||||
|
|L 0-0, C 0-1| Negate,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
|L 0-0, C 7-9| Num(
|
||||||
|
"12",
|
||||||
|
),
|
||||||
|
|L 0-0, C 10-13| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "foo",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
-whee 12 foo
|
|
@ -0,0 +1,19 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-5| UnaryOp(
|
||||||
|
|L 0-0, C 1-5| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "whee",
|
||||||
|
},
|
||||||
|
|L 0-0, C 0-1| Not,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
|L 0-0, C 7-9| Num(
|
||||||
|
"12",
|
||||||
|
),
|
||||||
|
|L 0-0, C 10-13| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "foo",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
!whee 12 foo
|
|
@ -0,0 +1,12 @@
|
||||||
|
Apply(
|
||||||
|
|L 0-0, C 0-4| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "whee",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
|L 0-0, C 5-6| Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
)
|
1
compiler/parse/tests/snapshots/pass/basic_apply.expr.roc
Normal file
1
compiler/parse/tests/snapshots/pass/basic_apply.expr.roc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
whee 1
|
|
@ -0,0 +1,43 @@
|
||||||
|
SpaceBefore(
|
||||||
|
Defs(
|
||||||
|
[
|
||||||
|
|L 6-6, C 0-5| Body(
|
||||||
|
|L 6-6, C 0-1| Identifier(
|
||||||
|
"x",
|
||||||
|
),
|
||||||
|
|L 6-6, C 4-5| Num(
|
||||||
|
"5",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 8-8, C 0-2| SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"42",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
DocComment(
|
||||||
|
"first line of docs",
|
||||||
|
),
|
||||||
|
DocComment(
|
||||||
|
" second line",
|
||||||
|
),
|
||||||
|
DocComment(
|
||||||
|
" third line",
|
||||||
|
),
|
||||||
|
DocComment(
|
||||||
|
"fourth line",
|
||||||
|
),
|
||||||
|
DocComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
DocComment(
|
||||||
|
"sixth line after doc new line",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
9
compiler/parse/tests/snapshots/pass/basic_docs.expr.roc
Normal file
9
compiler/parse/tests/snapshots/pass/basic_docs.expr.roc
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
## first line of docs
|
||||||
|
## second line
|
||||||
|
## third line
|
||||||
|
## fourth line
|
||||||
|
##
|
||||||
|
## sixth line after doc new line
|
||||||
|
x = 5
|
||||||
|
|
||||||
|
42
|
|
@ -0,0 +1,7 @@
|
||||||
|
Access(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "rec",
|
||||||
|
},
|
||||||
|
"field",
|
||||||
|
)
|
1
compiler/parse/tests/snapshots/pass/basic_field.expr.roc
Normal file
1
compiler/parse/tests/snapshots/pass/basic_field.expr.roc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rec.field
|
|
@ -0,0 +1,3 @@
|
||||||
|
GlobalTag(
|
||||||
|
"Whee",
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
Whee
|
|
@ -0,0 +1,3 @@
|
||||||
|
PrivateTag(
|
||||||
|
"@Whee",
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
@Whee
|
|
@ -0,0 +1,4 @@
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "whee",
|
||||||
|
}
|
1
compiler/parse/tests/snapshots/pass/basic_var.expr.roc
Normal file
1
compiler/parse/tests/snapshots/pass/basic_var.expr.roc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
whee
|
|
@ -0,0 +1,13 @@
|
||||||
|
Closure(
|
||||||
|
[
|
||||||
|
|L 0-0, C 1-2| Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
|L 0-0, C 4-9| Underscore(
|
||||||
|
"name",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 0-0, C 13-15| Num(
|
||||||
|
"42",
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
\_, _name -> 42
|
|
@ -0,0 +1,20 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 0-2| Num(
|
||||||
|
"12",
|
||||||
|
),
|
||||||
|
|L 0-0, C 4-5| Star,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 1-1, C 1-3| SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"92",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
" test!",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
12 * # test!
|
||||||
|
92
|
|
@ -0,0 +1,20 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 0-1| SpaceAfter(
|
||||||
|
Num(
|
||||||
|
"3",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
" test!",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|L 1-1, C 0-1| Plus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 1-1, C 2-3| Num(
|
||||||
|
"4",
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
3 # test!
|
||||||
|
+ 4
|
|
@ -0,0 +1,10 @@
|
||||||
|
List(
|
||||||
|
Collection {
|
||||||
|
items: [],
|
||||||
|
final_comments: [
|
||||||
|
LineComment(
|
||||||
|
"comment",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
[#comment
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 0-1| SpaceAfter(
|
||||||
|
Num(
|
||||||
|
"3",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
" 2 × 2",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|L 1-1, C 0-1| Plus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 1-1, C 2-3| Num(
|
||||||
|
"4",
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
3 # 2 × 2
|
||||||
|
+ 4
|
|
@ -0,0 +1,23 @@
|
||||||
|
App {
|
||||||
|
header: AppHeader {
|
||||||
|
name: |L 0-0, C 4-14| PlainLine(
|
||||||
|
"test-app",
|
||||||
|
),
|
||||||
|
packages: [],
|
||||||
|
imports: [],
|
||||||
|
provides: [],
|
||||||
|
to: |L 0-0, C 53-57| ExistingPackage(
|
||||||
|
"blah",
|
||||||
|
),
|
||||||
|
before_header: [],
|
||||||
|
after_app_keyword: [],
|
||||||
|
before_packages: [],
|
||||||
|
after_packages: [],
|
||||||
|
before_imports: [],
|
||||||
|
after_imports: [],
|
||||||
|
before_provides: [],
|
||||||
|
after_provides: [],
|
||||||
|
before_to: [],
|
||||||
|
after_to: [],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
app "test-app" packages {} imports [] provides [] to blah
|
|
@ -0,0 +1,15 @@
|
||||||
|
Interface {
|
||||||
|
header: InterfaceHeader {
|
||||||
|
name: |L 0-0, C 10-13| ModuleName(
|
||||||
|
"Foo",
|
||||||
|
),
|
||||||
|
exposes: [],
|
||||||
|
imports: [],
|
||||||
|
before_header: [],
|
||||||
|
after_interface_keyword: [],
|
||||||
|
before_exposes: [],
|
||||||
|
after_exposes: [],
|
||||||
|
before_imports: [],
|
||||||
|
after_imports: [],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
interface Foo exposes [] imports []
|
|
@ -0,0 +1,3 @@
|
||||||
|
List(
|
||||||
|
[],
|
||||||
|
)
|
1
compiler/parse/tests/snapshots/pass/empty_list.expr.roc
Normal file
1
compiler/parse/tests/snapshots/pass/empty_list.expr.roc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[]
|
|
@ -0,0 +1,43 @@
|
||||||
|
Platform {
|
||||||
|
header: PlatformHeader {
|
||||||
|
name: |L 0-0, C 9-23| PackageName {
|
||||||
|
account: "rtfeldman",
|
||||||
|
pkg: "blah",
|
||||||
|
},
|
||||||
|
requires: PlatformRequires {
|
||||||
|
rigids: [],
|
||||||
|
signature: |L 0-0, C 38-47| Entry {
|
||||||
|
ident: |L 0-0, C 38-42| "main",
|
||||||
|
spaces_before_colon: [],
|
||||||
|
ann: |L 0-0, C 45-47| Record {
|
||||||
|
fields: [],
|
||||||
|
ext: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exposes: [],
|
||||||
|
packages: [],
|
||||||
|
imports: [],
|
||||||
|
provides: [],
|
||||||
|
effects: Effects {
|
||||||
|
spaces_before_effects_keyword: [],
|
||||||
|
spaces_after_effects_keyword: [],
|
||||||
|
spaces_after_type_name: [],
|
||||||
|
effect_shortname: "fx",
|
||||||
|
effect_type_name: "Blah",
|
||||||
|
entries: [],
|
||||||
|
},
|
||||||
|
before_header: [],
|
||||||
|
after_platform_keyword: [],
|
||||||
|
before_requires: [],
|
||||||
|
after_requires: [],
|
||||||
|
before_exposes: [],
|
||||||
|
after_exposes: [],
|
||||||
|
before_packages: [],
|
||||||
|
after_packages: [],
|
||||||
|
before_imports: [],
|
||||||
|
after_imports: [],
|
||||||
|
before_provides: [],
|
||||||
|
after_provides: [],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
platform rtfeldman/blah requires {} { main : {} } exposes [] packages {} imports [] provides [] effects fx.Blah {}
|
|
@ -0,0 +1,3 @@
|
||||||
|
Record(
|
||||||
|
[],
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1,5 @@
|
||||||
|
Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
""
|
15
compiler/parse/tests/snapshots/pass/equals.expr.result-ast
Normal file
15
compiler/parse/tests/snapshots/pass/equals.expr.result-ast
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 0-1| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
},
|
||||||
|
|L 0-0, C 1-3| Equals,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 0-0, C 3-4| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "y",
|
||||||
|
},
|
||||||
|
)
|
1
compiler/parse/tests/snapshots/pass/equals.expr.roc
Normal file
1
compiler/parse/tests/snapshots/pass/equals.expr.roc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
x==y
|
|
@ -0,0 +1,15 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 0-1| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
},
|
||||||
|
|L 0-0, C 2-4| Equals,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 0-0, C 5-6| Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "y",
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
x == y
|
24
compiler/parse/tests/snapshots/pass/expect.expr.result-ast
Normal file
24
compiler/parse/tests/snapshots/pass/expect.expr.result-ast
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
Expect(
|
||||||
|
|L 0-0, C 7-13| BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
|L 0-0, C 7-8| Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
|L 0-0, C 9-11| Equals,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|L 0-0, C 12-13| Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|L 2-2, C 0-1| SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"4",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
3
compiler/parse/tests/snapshots/pass/expect.expr.roc
Normal file
3
compiler/parse/tests/snapshots/pass/expect.expr.roc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
expect 1 == 1
|
||||||
|
|
||||||
|
4
|
|
@ -0,0 +1,3 @@
|
||||||
|
Float(
|
||||||
|
"-1_23_456.0_1_23_456",
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
-1_23_456.0_1_23_456
|
|
@ -0,0 +1,51 @@
|
||||||
|
App {
|
||||||
|
header: AppHeader {
|
||||||
|
name: |L 0-0, C 4-15| PlainLine(
|
||||||
|
"quicksort",
|
||||||
|
),
|
||||||
|
packages: [
|
||||||
|
|L 1-1, C 15-33| Entry {
|
||||||
|
shorthand: "base",
|
||||||
|
spaces_after_shorthand: [],
|
||||||
|
package_or_path: |L 1-1, C 21-33| Path(
|
||||||
|
PlainLine(
|
||||||
|
"./platform",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
|L 2-2, C 14-25| Package(
|
||||||
|
"foo",
|
||||||
|
ModuleName(
|
||||||
|
"Bar.Baz",
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
provides: [
|
||||||
|
|L 3-3, C 15-24| Exposed(
|
||||||
|
"quicksort",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
to: |L 3-3, C 30-34| ExistingPackage(
|
||||||
|
"base",
|
||||||
|
),
|
||||||
|
before_header: [],
|
||||||
|
after_app_keyword: [],
|
||||||
|
before_packages: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
after_packages: [],
|
||||||
|
before_imports: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
after_imports: [],
|
||||||
|
before_provides: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
after_provides: [],
|
||||||
|
before_to: [],
|
||||||
|
after_to: [],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
app "quicksort"
|
||||||
|
packages { base: "./platform" }
|
||||||
|
imports [ foo.Bar.Baz ]
|
||||||
|
provides [ quicksort ] to base
|
|
@ -0,0 +1,76 @@
|
||||||
|
App {
|
||||||
|
header: AppHeader {
|
||||||
|
name: |L 0-0, C 4-15| PlainLine(
|
||||||
|
"quicksort",
|
||||||
|
),
|
||||||
|
packages: [
|
||||||
|
|L 1-1, C 15-33| Entry {
|
||||||
|
shorthand: "base",
|
||||||
|
spaces_after_shorthand: [],
|
||||||
|
package_or_path: |L 1-1, C 21-33| Path(
|
||||||
|
PlainLine(
|
||||||
|
"./platform",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
|L 2-6, C 14-5| Package(
|
||||||
|
"foo",
|
||||||
|
ModuleName(
|
||||||
|
"Bar",
|
||||||
|
),
|
||||||
|
Collection {
|
||||||
|
items: [
|
||||||
|
|L 3-3, C 8-11| SpaceBefore(
|
||||||
|
Exposed(
|
||||||
|
"Baz",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|L 4-4, C 8-16| SpaceBefore(
|
||||||
|
Exposed(
|
||||||
|
"FortyTwo",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
final_comments: [
|
||||||
|
Newline,
|
||||||
|
LineComment(
|
||||||
|
" I'm a happy comment",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
provides: [
|
||||||
|
|L 7-7, C 15-24| Exposed(
|
||||||
|
"quicksort",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
to: |L 7-7, C 31-35| ExistingPackage(
|
||||||
|
"base",
|
||||||
|
),
|
||||||
|
before_header: [],
|
||||||
|
after_app_keyword: [],
|
||||||
|
before_packages: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
after_packages: [],
|
||||||
|
before_imports: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
after_imports: [],
|
||||||
|
before_provides: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
after_provides: [],
|
||||||
|
before_to: [],
|
||||||
|
after_to: [],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
app "quicksort"
|
||||||
|
packages { base: "./platform", }
|
||||||
|
imports [ foo.Bar.{
|
||||||
|
Baz,
|
||||||
|
FortyTwo,
|
||||||
|
# I'm a happy comment
|
||||||
|
} ]
|
||||||
|
provides [ quicksort, ] to base
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue