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>
|
||||
Takeshi Sato <doublequotation@gmail.com>
|
||||
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"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
"diff",
|
||||
"encode_unicode",
|
||||
"indoc",
|
||||
"pretty_assertions",
|
||||
|
|
|
@ -12,7 +12,6 @@ members = [
|
|||
"compiler/constrain",
|
||||
"compiler/unify",
|
||||
"compiler/solve",
|
||||
"compiler/reporting",
|
||||
"compiler/fmt",
|
||||
"compiler/mono",
|
||||
"compiler/test_mono",
|
||||
|
@ -31,6 +30,7 @@ members = [
|
|||
"ast",
|
||||
"cli",
|
||||
"code_markup",
|
||||
"reporting",
|
||||
"roc_std",
|
||||
"utils",
|
||||
"docs",
|
||||
|
|
|
@ -47,7 +47,7 @@ install-zig-llvm-valgrind-clippy-rustfmt:
|
|||
|
||||
copy-dirs:
|
||||
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:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::{
|
|||
expr2::{ClosureExtra, Expr2, ExprId, WhenBranch},
|
||||
record_field::RecordField,
|
||||
},
|
||||
fun_def::FunctionDef,
|
||||
pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct},
|
||||
types::{Type2, TypeId},
|
||||
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 {
|
||||
symbol,
|
||||
updates,
|
||||
|
@ -1031,7 +1152,6 @@ pub fn constrain_expr<'a>(
|
|||
exists(arena, vars, And(and_constraints))
|
||||
}
|
||||
Expr2::LetRec { .. } => todo!(),
|
||||
Expr2::LetFunction { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1765,7 +1885,7 @@ pub mod test_constrain {
|
|||
use roc_parse::parser::SyntaxError;
|
||||
use roc_region::all::Region;
|
||||
use roc_types::{
|
||||
pretty_print::content_to_string,
|
||||
pretty_print::{content_to_string, name_all_type_vars},
|
||||
solved_types::Solved,
|
||||
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);
|
||||
|
||||
match expr2_result {
|
||||
Ok((expr, _)) => {
|
||||
Ok((expr, output)) => {
|
||||
let constraint = constrain_expr(
|
||||
&code_arena,
|
||||
&mut env,
|
||||
|
@ -1865,17 +1985,22 @@ pub mod test_constrain {
|
|||
let mut var_store = VarStore::default();
|
||||
std::mem::swap(ref_var_store, &mut var_store);
|
||||
|
||||
let rigids = output.introduced_variables.name_by_var;
|
||||
|
||||
let (mut solved, _, _) = run_solve(
|
||||
&code_arena,
|
||||
pool,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
rigids,
|
||||
constraint,
|
||||
var_store,
|
||||
);
|
||||
|
||||
let subs = solved.inner_mut();
|
||||
|
||||
// name type vars
|
||||
name_all_type_vars(var, subs);
|
||||
|
||||
let content = subs.get_content_without_compacting(var);
|
||||
|
||||
// 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]
|
||||
fn constrain_closure() {
|
||||
infer_eq(
|
||||
|
@ -2145,4 +2366,130 @@ pub mod test_constrain {
|
|||
"{}* -> 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,
|
||||
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)]
|
||||
|
@ -316,7 +320,7 @@ fn from_pending_alias<'a>(
|
|||
}
|
||||
|
||||
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 {
|
||||
alias: symbol,
|
||||
variable_region: loc_lowercase.region,
|
||||
|
@ -454,6 +458,10 @@ fn canonicalize_pending_def<'a>(
|
|||
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);
|
||||
|
||||
// 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!
|
||||
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);
|
||||
|
||||
let return_type: TypeId;
|
||||
|
@ -558,7 +566,8 @@ fn canonicalize_pending_def<'a>(
|
|||
for (node_id, ((_, pattern_id), typ)) in
|
||||
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;
|
||||
|
@ -689,14 +698,14 @@ fn canonicalize_pending_def<'a>(
|
|||
// parent commit for the bug this fixed!
|
||||
let refs = References::new();
|
||||
|
||||
let arguments: PoolVec<(PatternId, Variable)> =
|
||||
let arguments: PoolVec<(Variable, PatternId)> =
|
||||
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
||||
|
||||
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())
|
||||
{
|
||||
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 {
|
||||
|
|
|
@ -15,14 +15,14 @@ use super::{
|
|||
pub enum FunctionDef {
|
||||
WithAnnotation {
|
||||
name: Symbol, // 8B
|
||||
arguments: PoolVec<(PatternId, Type2)>, // 8B
|
||||
arguments: PoolVec<(NodeId<Type2>, PatternId)>, // 8B
|
||||
rigids: NodeId<Rigids>, // 4B
|
||||
return_type: TypeId, // 4B
|
||||
body_id: ExprId, // 4B
|
||||
},
|
||||
NoAnnotation {
|
||||
name: Symbol, // 8B
|
||||
arguments: PoolVec<(PatternId, Variable)>, // 8B
|
||||
arguments: PoolVec<(Variable, PatternId)>, // 8B
|
||||
return_var: Variable, // 4B
|
||||
body_id: ExprId, // 4B
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@ pub mod ast;
|
|||
mod declaration;
|
||||
pub mod def;
|
||||
pub mod expr;
|
||||
mod fun_def;
|
||||
pub mod fun_def;
|
||||
pub mod header;
|
||||
pub mod pattern;
|
||||
pub mod str;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#![allow(unused_imports)]
|
||||
// use roc_can::expr::Output;
|
||||
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_region::all::{Located, Region};
|
||||
use roc_types::types::{Problem, RecordField};
|
||||
|
@ -20,29 +20,34 @@ pub type TypeId = NodeId<Type2>;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum Type2 {
|
||||
Variable(Variable),
|
||||
Variable(Variable), // 4B
|
||||
|
||||
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 12B + 4B
|
||||
AsAlias(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 + 8B + 4B + pad
|
||||
|
||||
// 32B
|
||||
// 24B
|
||||
HostExposedAlias {
|
||||
name: Symbol, // 8B
|
||||
arguments: PoolVec<(PoolStr, TypeId)>, // 12B
|
||||
arguments: PoolVec<(PoolStr, TypeId)>, // 8B
|
||||
actual_var: Variable, // 4B
|
||||
actual: TypeId, // 4B
|
||||
},
|
||||
EmptyTagUnion,
|
||||
TagUnion(PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 16B = 12B + 4B
|
||||
RecursiveTagUnion(Variable, PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 20B = 4B + 12B + 4B
|
||||
TagUnion(PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 12B = 8B + 4B
|
||||
RecursiveTagUnion(Variable, PoolVec<(TagName, PoolVec<Type2>)>, TypeId), // 16B = 4B + 8B + 4B
|
||||
|
||||
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
|
||||
Apply(Symbol, PoolVec<Type2>), // 20B = 8B + 12B
|
||||
Function(PoolVec<Type2>, TypeId, TypeId), // 16B = 8B + 4B + 4B
|
||||
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)]
|
||||
|
@ -171,9 +176,9 @@ pub enum Signature {
|
|||
},
|
||||
}
|
||||
|
||||
pub enum Annotation2<'a> {
|
||||
pub enum Annotation2 {
|
||||
Annotation {
|
||||
named_rigids: MutMap<&'a str, Variable>,
|
||||
named_rigids: MutMap<Lowercase, Variable>,
|
||||
unnamed_rigids: MutSet<Variable>,
|
||||
symbols: MutSet<Symbol>,
|
||||
signature: Signature,
|
||||
|
@ -186,7 +191,7 @@ pub fn to_annotation2<'a>(
|
|||
scope: &mut Scope,
|
||||
annotation: &'a roc_parse::ast::TypeAnnotation<'a>,
|
||||
region: Region,
|
||||
) -> Annotation2<'a> {
|
||||
) -> Annotation2 {
|
||||
let mut references = References::default();
|
||||
|
||||
let annotation = to_type2(env, scope, &mut references, annotation, region);
|
||||
|
@ -240,11 +245,7 @@ pub fn to_annotation2<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn shallow_dealias<'a>(
|
||||
env: &mut Env,
|
||||
references: References<'a>,
|
||||
annotation: Type2,
|
||||
) -> Annotation2<'a> {
|
||||
fn shallow_dealias<'a>(env: &mut Env, references: References, annotation: Type2) -> Annotation2 {
|
||||
let References {
|
||||
named,
|
||||
unnamed,
|
||||
|
@ -288,8 +289,8 @@ fn shallow_dealias<'a>(
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct References<'a> {
|
||||
named: MutMap<&'a str, Variable>,
|
||||
pub struct References {
|
||||
named: MutMap<Lowercase, Variable>,
|
||||
unnamed: MutSet<Variable>,
|
||||
hidden: MutSet<Variable>,
|
||||
symbols: MutSet<Symbol>,
|
||||
|
@ -298,7 +299,7 @@ pub struct References<'a> {
|
|||
pub fn to_type_id<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
rigids: &mut References<'a>,
|
||||
rigids: &mut References,
|
||||
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
||||
region: Region,
|
||||
) -> TypeId {
|
||||
|
@ -310,7 +311,7 @@ pub fn to_type_id<'a>(
|
|||
pub fn as_type_id<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
rigids: &mut References<'a>,
|
||||
rigids: &mut References,
|
||||
type_id: TypeId,
|
||||
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
||||
region: Region,
|
||||
|
@ -324,7 +325,7 @@ pub fn as_type_id<'a>(
|
|||
pub fn to_type2<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
references: &mut References<'a>,
|
||||
references: &mut References,
|
||||
annotation: &roc_parse::ast::TypeAnnotation<'a>,
|
||||
region: Region,
|
||||
) -> Type2 {
|
||||
|
@ -375,8 +376,9 @@ pub fn to_type2<'a>(
|
|||
Type2::Function(arguments, closure_type_id, return_type_id)
|
||||
}
|
||||
BoundVariable(v) => {
|
||||
// a rigid type variable
|
||||
match references.named.get(v) {
|
||||
// A rigid type variable. The parser should have already ensured that the name is indeed a lowercase.
|
||||
let v = Lowercase::from(*v);
|
||||
match references.named.get(&v) {
|
||||
Some(var) => Type2::Variable(*var),
|
||||
None => {
|
||||
let var = env.var_store.fresh();
|
||||
|
@ -387,6 +389,9 @@ pub fn to_type2<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Inferred => {
|
||||
unimplemented!();
|
||||
}
|
||||
Wildcard | Malformed(_) => {
|
||||
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);
|
||||
|
||||
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 {
|
||||
RecordField::Optional(_) => {
|
||||
|
@ -480,10 +485,10 @@ pub fn to_type2<'a>(
|
|||
{
|
||||
match loc_var.value {
|
||||
BoundVariable(ident) => {
|
||||
let var_name = ident;
|
||||
let var_name = Lowercase::from(ident);
|
||||
|
||||
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));
|
||||
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
||||
|
@ -494,7 +499,7 @@ pub fn to_type2<'a>(
|
|||
let var = env.var_store.fresh();
|
||||
|
||||
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));
|
||||
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
||||
|
@ -576,10 +581,10 @@ pub fn to_type2<'a>(
|
|||
fn can_assigned_fields<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
rigids: &mut References<'a>,
|
||||
rigids: &mut References,
|
||||
fields: &&[Located<roc_parse::ast::AssignedField<'a, roc_parse::ast::TypeAnnotation<'a>>>],
|
||||
region: Region,
|
||||
) -> MutMap<&'a str, RecordField<Type2>> {
|
||||
) -> MutMap<Lowercase, RecordField<Type2>> {
|
||||
use roc_parse::ast::AssignedField::*;
|
||||
use roc_types::types::RecordField::*;
|
||||
|
||||
|
@ -602,8 +607,8 @@ fn can_assigned_fields<'a>(
|
|||
let field_type =
|
||||
to_type2(env, scope, rigids, &annotation.value, annotation.region);
|
||||
|
||||
let label = field_name.value;
|
||||
field_types.insert(label, Required(field_type));
|
||||
let label = Lowercase::from(field_name.value);
|
||||
field_types.insert(label.clone(), Required(field_type));
|
||||
|
||||
break 'inner label;
|
||||
}
|
||||
|
@ -611,20 +616,20 @@ fn can_assigned_fields<'a>(
|
|||
let field_type =
|
||||
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));
|
||||
|
||||
break 'inner label;
|
||||
}
|
||||
LabelOnly(loc_field_name) => {
|
||||
// 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 = {
|
||||
if let Some(var) = rigids.named.get(&field_name) {
|
||||
Type2::Variable(*var)
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
};
|
||||
|
@ -664,7 +669,7 @@ fn can_assigned_fields<'a>(
|
|||
fn can_tags<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
rigids: &mut References<'a>,
|
||||
rigids: &mut References,
|
||||
tags: &'a [Located<roc_parse::ast::Tag<'a>>],
|
||||
region: Region,
|
||||
) -> Vec<(TagName, PoolVec<Type2>)> {
|
||||
|
@ -748,7 +753,7 @@ enum TypeApply {
|
|||
fn to_type_apply<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
rigids: &mut References<'a>,
|
||||
rigids: &mut References,
|
||||
module_name: &str,
|
||||
ident: &str,
|
||||
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,
|
||||
};
|
||||
use roc_collections::all::WyHash;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_types::subs::Variable;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -18,7 +19,7 @@ pub struct Rigids {
|
|||
#[allow(clippy::needless_collect)]
|
||||
impl Rigids {
|
||||
pub fn new(
|
||||
named: HashMap<&str, Variable, BuildHasherDefault<WyHash>>,
|
||||
named: HashMap<Lowercase, Variable, BuildHasherDefault<WyHash>>,
|
||||
unnamed: HashSet<Variable, BuildHasherDefault<WyHash>>,
|
||||
pool: &mut Pool,
|
||||
) -> Self {
|
||||
|
@ -26,7 +27,7 @@ impl Rigids {
|
|||
|
||||
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)));
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ roc_load = { path = "../compiler/load" }
|
|||
roc_gen_llvm = { path = "../compiler/gen_llvm", optional = true }
|
||||
roc_build = { path = "../compiler/build", default-features = false }
|
||||
roc_fmt = { path = "../compiler/fmt" }
|
||||
roc_reporting = { path = "../compiler/reporting" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_editor = { path = "../editor", optional = true }
|
||||
roc_linker = { path = "../linker" }
|
||||
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_wasm = { path = "../gen_wasm", optional = true }
|
||||
roc_gen_dev = { path = "../gen_dev", default-features = false }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
roc_std = { path = "../../roc_std" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
libloading = "0.7.1"
|
||||
|
|
|
@ -145,6 +145,8 @@ pub fn build_zig_host_native(
|
|||
_target: &str,
|
||||
opt_level: OptLevel,
|
||||
shared_lib_path: Option<&Path>,
|
||||
// For compatibility with the non-macOS def above. Keep these in sync.
|
||||
_target_valgrind: bool,
|
||||
) -> Output {
|
||||
use serde_json::Value;
|
||||
|
||||
|
|
|
@ -870,6 +870,9 @@ pub fn listSublist(
|
|||
len: usize,
|
||||
dec: Dec,
|
||||
) callconv(.C) RocList {
|
||||
if (len == 0) {
|
||||
return RocList.empty();
|
||||
}
|
||||
if (list.bytes) |source_ptr| {
|
||||
const size = list.len();
|
||||
|
||||
|
|
|
@ -1015,6 +1015,25 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
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
|
||||
add_top_level_function_type!(
|
||||
Symbol::LIST_DROP,
|
||||
|
|
|
@ -459,6 +459,9 @@ fn can_annotation_help(
|
|||
|
||||
Type::Variable(var)
|
||||
}
|
||||
Inferred => {
|
||||
unimplemented!();
|
||||
}
|
||||
Malformed(string) => {
|
||||
malformed(env, region, string);
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{ClosureData, Expr::*};
|
||||
use crate::expr::{Expr, Recursive, WhenBranch};
|
||||
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
||||
use crate::pattern::Pattern;
|
||||
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::operator::CalledVia;
|
||||
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_LAST => list_take_last,
|
||||
LIST_SUBLIST => list_sublist,
|
||||
LIST_SPLIT => list_split,
|
||||
LIST_INTERSPERSE => list_intersperse,
|
||||
LIST_DROP => list_drop,
|
||||
LIST_DROP_AT => list_drop_at,
|
||||
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_SORT_WITH => list_sort_with,
|
||||
LIST_ANY => list_any,
|
||||
LIST_INTERSPERSE => list_intersperse,
|
||||
LIST_FIND => list_find,
|
||||
DICT_LEN => dict_len,
|
||||
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
|
||||
fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
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)]
|
||||
// fn record(fields: Vec<(Lowercase, Field)>, var_store: &mut VarStore) -> Expr {
|
||||
// let mut send_map = SendMap::default();
|
||||
// for (k, v) in fields {
|
||||
// send_map.insert(k, v);
|
||||
// }
|
||||
// Expr::Record {
|
||||
// record_var: var_store.fresh(),
|
||||
// fields: send_map,
|
||||
// }
|
||||
// }
|
||||
#[inline(always)]
|
||||
fn record(fields: Vec<(Lowercase, Field)>, var_store: &mut VarStore) -> Expr {
|
||||
let mut send_map = SendMap::default();
|
||||
for (k, v) in fields {
|
||||
send_map.insert(k, v);
|
||||
}
|
||||
Expr::Record {
|
||||
record_var: var_store.fresh(),
|
||||
fields: send_map,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn defn(
|
||||
|
|
|
@ -180,7 +180,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
|||
true
|
||||
}
|
||||
|
||||
Wildcard | BoundVariable(_) | Malformed(_) => false,
|
||||
Wildcard | Inferred | BoundVariable(_) | Malformed(_) => false,
|
||||
Function(args, result) => {
|
||||
(&result.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),
|
||||
Wildcard => buf.push('*'),
|
||||
Inferred => buf.push('_'),
|
||||
|
||||
TagUnion { tags, ext } => {
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
|
|
@ -446,6 +446,10 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
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)]
|
||||
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_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(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
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(
|
||||
&mut self,
|
||||
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)
|
||||
}
|
||||
|
||||
#[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)]
|
||||
fn sub_reg64_reg64_imm32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
|
|
@ -368,6 +368,18 @@ where
|
|||
);
|
||||
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(
|
||||
sym,
|
||||
bitcode::NUM_POW_INT[IntWidth::I64].to_string(),
|
||||
|
@ -459,6 +471,14 @@ where
|
|||
layout: &Layout<'a>,
|
||||
) -> 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.
|
||||
fn build_num_sub(
|
||||
&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(())
|
||||
}
|
||||
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
@ -472,7 +479,7 @@ impl<'a> WasmBackend<'a> {
|
|||
CallType::ByName { name: func_sym, .. } => {
|
||||
// If this function is just a lowlevel wrapper, then inline it
|
||||
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>;
|
||||
|
@ -517,7 +524,7 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
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)),
|
||||
|
@ -556,9 +563,18 @@ impl<'a> WasmBackend<'a> {
|
|||
&mut self,
|
||||
lowlevel: LowLevel,
|
||||
arguments: &'a [Symbol],
|
||||
return_sym: Symbol,
|
||||
return_layout: WasmLayout,
|
||||
) -> 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(
|
||||
&mut self.code_builder,
|
||||
|
@ -572,7 +588,7 @@ impl<'a> WasmBackend<'a> {
|
|||
match build_result {
|
||||
Done => Ok(()),
|
||||
BuiltinCall(name) => {
|
||||
self.call_imported_builtin(name, arguments, &return_layout);
|
||||
self.call_zig_builtin(name, arguments, &return_layout);
|
||||
Ok(())
|
||||
}
|
||||
NotImplemented => Err(format!(
|
||||
|
@ -626,8 +642,8 @@ impl<'a> WasmBackend<'a> {
|
|||
self.lookup_string_constant(string, sym, layout);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.insert_memory_relocation(linker_sym_index);
|
||||
self.code_builder.i32_const(elements_addr as i32);
|
||||
self.code_builder
|
||||
.i32_const_mem_addr(elements_addr, linker_sym_index);
|
||||
self.code_builder.i32_store(Align::Bytes4, offset);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
|
@ -749,12 +765,10 @@ impl<'a> WasmBackend<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn call_imported_builtin(
|
||||
&mut self,
|
||||
name: &'a str,
|
||||
arguments: &[Symbol],
|
||||
ret_layout: &WasmLayout,
|
||||
) {
|
||||
/// Generate a call instruction to a Zig builtin function.
|
||||
/// And if we haven't seen it before, add an Import and linker data for it.
|
||||
/// Zig calls use LLVM's "fast" calling convention rather than our usual C ABI.
|
||||
fn call_zig_builtin(&mut self, name: &'a str, arguments: &[Symbol], ret_layout: &WasmLayout) {
|
||||
let (fn_index, linker_symbol_index) = match self.builtin_sym_index_map.get(name) {
|
||||
Some(sym_idx) => match &self.linker_symbols[*sym_idx] {
|
||||
SymInfo::Function(WasmObjectSymbol::Imported { index, .. }) => {
|
||||
|
@ -764,11 +778,29 @@ impl<'a> WasmBackend<'a> {
|
|||
},
|
||||
|
||||
None => {
|
||||
let mut param_types = Vec::with_capacity_in(arguments.len(), self.env.arena);
|
||||
param_types.extend(arguments.iter().map(|a| self.storage.get(a).value_type()));
|
||||
let mut param_types = Vec::with_capacity_in(1 + arguments.len(), self.env.arena);
|
||||
|
||||
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 {
|
||||
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;
|
||||
|
|
|
@ -7,6 +7,7 @@ pub mod wasm_module;
|
|||
use bumpalo::{self, collections::Vec, Bump};
|
||||
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::ir::{Proc, ProcLayout};
|
||||
use roc_mono::layout::LayoutIds;
|
||||
|
@ -36,7 +37,7 @@ pub fn build_module<'a>(
|
|||
env: &'a Env,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
) -> 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);
|
||||
wasm_module.serialize_mut(&mut buffer);
|
||||
Ok(buffer)
|
||||
|
@ -45,36 +46,54 @@ pub fn build_module<'a>(
|
|||
pub fn build_module_help<'a>(
|
||||
env: &'a Env,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
) -> Result<WasmModule<'a>, String> {
|
||||
) -> Result<(WasmModule<'a>, u32), String> {
|
||||
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 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
|
||||
for (i, (sym, layout)) in procedures.keys().enumerate() {
|
||||
proc_symbols.push(*sym);
|
||||
// Collect the symbols & names for the procedures,
|
||||
// and filter out procs we're going to inline
|
||||
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
|
||||
.get_toplevel(*sym, layout)
|
||||
.to_symbol_string(*sym, &env.interns);
|
||||
.get_toplevel(sym, &layout)
|
||||
.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 {
|
||||
name: fn_name.clone(),
|
||||
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);
|
||||
|
||||
fn_index += 1;
|
||||
}
|
||||
|
||||
// Main loop: Build the Wasm module
|
||||
// Build the Wasm module
|
||||
let (mut module, linker_symbols) = {
|
||||
let mut backend = WasmBackend::new(env, layout_ids, proc_symbols, linker_symbols, exports);
|
||||
for ((sym, _), proc) in procedures.into_iter() {
|
||||
let mut backend = WasmBackend::new(
|
||||
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.module, backend.linker_symbols)
|
||||
|
@ -83,7 +102,7 @@ pub fn build_module_help<'a>(
|
|||
let symbol_table = LinkingSubSection::SymbolTable(linker_symbols);
|
||||
module.linking.subsections.push(symbol_table);
|
||||
|
||||
Ok(module)
|
||||
Ok((module, main_fn_index.unwrap()))
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
match lowlevel {
|
||||
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
|
||||
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft
|
||||
| StrTrimRight | StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim
|
||||
| ListLen | ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse
|
||||
| ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap
|
||||
| ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||
| ListSublist | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty
|
||||
| DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
|
||||
| DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||
StrConcat => return BuiltinCall(bitcode::STR_CONCAT),
|
||||
|
||||
StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt | StrEndsWith | StrSplit
|
||||
| StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft | StrTrimRight
|
||||
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen
|
||||
| ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
||||
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
|
||||
| ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil
|
||||
| ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListSublist
|
||||
| ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty | DictInsert
|
||||
| DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues | DictUnion
|
||||
| DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||
return NotImplemented;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use roc_collections::all::MutMap;
|
|||
use roc_module::symbol::Symbol;
|
||||
|
||||
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};
|
||||
|
||||
pub enum StoredValueKind {
|
||||
|
@ -194,17 +194,10 @@ impl<'a> Storage<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// but it generates very inefficient Wasm code.
|
||||
pub fn load_symbols(&mut self, code_builder: &mut CodeBuilder, symbols: &[Symbol]) {
|
||||
if code_builder.verify_stack_match(symbols) {
|
||||
// The symbols were already at the top of the stack, do nothing!
|
||||
// This should be quite common due to the structure of the Mono IR
|
||||
return;
|
||||
}
|
||||
for sym in symbols.iter() {
|
||||
let storage = self.get(sym).to_owned();
|
||||
/// 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,
|
||||
|
@ -212,13 +205,12 @@ impl<'a> Storage<'a> {
|
|||
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);
|
||||
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,
|
||||
sym,
|
||||
StoredValue::VirtualMachineStack {
|
||||
vm_state: next_vm_state,
|
||||
value_type,
|
||||
|
@ -231,7 +223,7 @@ impl<'a> Storage<'a> {
|
|||
// it was not in a convenient position in the VM stack.
|
||||
self.local_types.push(value_type);
|
||||
self.symbol_storage_map.insert(
|
||||
*sym,
|
||||
sym,
|
||||
StoredValue::Local {
|
||||
local_id: next_local_id,
|
||||
value_type,
|
||||
|
@ -241,24 +233,88 @@ impl<'a> Storage<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
StoredValue::Local { local_id, .. }
|
||||
| StoredValue::StackMemory {
|
||||
location: StackMemoryLocation::PointerArg(local_id),
|
||||
..
|
||||
} => {
|
||||
|
||||
StoredValue::Local { local_id, .. } => {
|
||||
code_builder.get_local(local_id);
|
||||
code_builder.set_top_symbol(*sym);
|
||||
code_builder.set_top_symbol(sym);
|
||||
}
|
||||
|
||||
StoredValue::StackMemory {
|
||||
location: StackMemoryLocation::FrameOffset(offset),
|
||||
..
|
||||
} => {
|
||||
code_builder.get_local(self.stack_frame_pointer.unwrap());
|
||||
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);
|
||||
}
|
||||
code_builder.set_top_symbol(sym);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// but it generates very inefficient Wasm code.
|
||||
pub fn load_symbols(&mut self, code_builder: &mut CodeBuilder, symbols: &[Symbol]) {
|
||||
if code_builder.verify_stack_match(symbols) {
|
||||
// The symbols were already at the top of the stack, do nothing!
|
||||
// This should be quite common due to the structure of the Mono IR
|
||||
return;
|
||||
}
|
||||
for sym in symbols.iter() {
|
||||
self.load_symbol_ccc(code_builder, *sym);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load symbols in a way compatible with LLVM's "fast calling convention"
|
||||
/// 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
|
||||
/// in favour of CPU registers (or VM stack values, which eventually become CPU registers).
|
||||
/// We need to convert some of our structs from our internal C-like representation to work with Zig.
|
||||
/// We are sticking to C ABI for better compatibility on the platform side.
|
||||
pub fn load_symbols_fastcc(
|
||||
&mut self,
|
||||
code_builder: &mut CodeBuilder,
|
||||
symbols: &[Symbol],
|
||||
return_symbol: Symbol,
|
||||
return_layout: &WasmLayout,
|
||||
) {
|
||||
// Note: we are not doing verify_stack_match in this case so we may generate more code.
|
||||
// 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
|
||||
// Apparently for return values we still use a pointer to stack memory
|
||||
self.load_symbol_ccc(code_builder, return_symbol);
|
||||
};
|
||||
|
||||
for sym in symbols {
|
||||
if let StoredValue::StackMemory {
|
||||
location,
|
||||
size,
|
||||
alignment_bytes,
|
||||
} = 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);
|
||||
}
|
||||
|
||||
let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer);
|
||||
code_builder.get_local(local_id);
|
||||
let align = Align::from(*alignment_bytes);
|
||||
|
||||
if *size == 1 {
|
||||
code_builder.i32_load8_u(align, offset);
|
||||
} else if *size == 2 {
|
||||
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 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 {
|
||||
($($x: expr),+) => {
|
||||
if ENABLE_DEBUG_LOG { println!($($x,)*); }
|
||||
|
@ -572,11 +572,14 @@ impl<'a> CodeBuilder<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
/// Insert a linker relocation for a memory address
|
||||
pub fn insert_memory_relocation(&mut self, symbol_index: u32) {
|
||||
/// Insert a const reference to a memory address
|
||||
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 {
|
||||
type_id: OffsetRelocType::MemoryAddrLeb,
|
||||
offset: self.code.len() as u32,
|
||||
offset,
|
||||
symbol_index,
|
||||
addend: 0,
|
||||
});
|
||||
|
|
|
@ -29,6 +29,8 @@ pub enum SectionId {
|
|||
Element = 9,
|
||||
Code = 10,
|
||||
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,
|
||||
}
|
||||
|
||||
|
@ -239,8 +241,7 @@ impl<'a> Serialize for ImportSection<'a> {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionSection<'a> {
|
||||
/// Private. See WasmModule::add_function_signature
|
||||
signature_indices: Vec<'a, u32>,
|
||||
pub signature_indices: Vec<'a, u32>,
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -658,11 +623,6 @@ impl<'a> WasmModule<'a> {
|
|||
counter.serialize_and_count(buffer, &self.start);
|
||||
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
|
||||
let code_section_index = counter.section_index;
|
||||
let code_section_body_index = self
|
||||
|
|
|
@ -18,7 +18,7 @@ roc_unify = { path = "../unify" }
|
|||
roc_parse = { path = "../parse" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
|
|
|
@ -1074,6 +1074,8 @@ define_builtins! {
|
|||
49 LIST_SUBLIST: "sublist"
|
||||
50 LIST_INTERSPERSE: "intersperse"
|
||||
51 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
||||
52 LIST_SPLIT: "split"
|
||||
53 LIST_SPLIT_CLOS: "#splitClos"
|
||||
}
|
||||
5 RESULT: "Result" => {
|
||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||
|
|
|
@ -17,3 +17,5 @@ pretty_assertions = "1.0.0"
|
|||
indoc = "1.0.3"
|
||||
quickcheck = "1.0.3"
|
||||
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>>>,
|
||||
},
|
||||
|
||||
/// '_', indicating the compiler should infer the type
|
||||
Inferred,
|
||||
|
||||
/// The `*` type variable, e.g. in (List *)
|
||||
Wildcard,
|
||||
|
||||
|
|
|
@ -626,6 +626,7 @@ pub enum EType<'a> {
|
|||
TApply(ETypeApply, Row, Col),
|
||||
TBadTypeVariable(Row, Col),
|
||||
TWildcard(Row, Col),
|
||||
TInferred(Row, Col),
|
||||
///
|
||||
TStart(Row, Col),
|
||||
TEnd(Row, Col),
|
||||
|
|
|
@ -55,6 +55,7 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, ETy
|
|||
and!(
|
||||
one_of!(
|
||||
loc_wildcard(),
|
||||
loc_inferred(),
|
||||
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
|
||||
loc!(specialize(EType::TRecord, record_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>> {
|
||||
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)),
|
||||
one_of!(
|
||||
loc_wildcard(),
|
||||
loc_inferred(),
|
||||
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
|
||||
loc!(specialize(EType::TRecord, record_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