Merge branch 'trunk' of github.com:rtfeldman/roc into type-start-parse-error

This commit is contained in:
Eric Correia 2021-03-30 08:38:34 -04:00
commit c827b52aba
101 changed files with 3560 additions and 2680 deletions

View file

@ -1,11 +1,11 @@
interface Bool2
interface Bool
exposes [ not, and, or, xor, isEq, isNotEq ]
imports []
## Returns #False when given #True, and vice versa.
## Returns `False` when given `True`, and vice versa.
not : [True, False] -> [True, False]
## Returns #True when given #True and #True, and #False when either argument is #False.
## Returns `True` when given `True` and `True`, and `False` when either argument is `False`.
##
## `a && b` is shorthand for `Bool.and a b`
##
@ -39,7 +39,7 @@ not : [True, False] -> [True, False]
and : Bool, Bool -> Bool
## Returns #True when given #True for either argument, and #False only when given #False and #False.
## Returns `True` when given `True` for either argument, and `False` only when given `False` and `False`.
##
## `a || b` is shorthand for `Bool.or a b`.
##
@ -55,14 +55,13 @@ and : Bool, Bool -> Bool
##
## In some languages, `&&` and `||` are special-cased in the compiler to skip
## evaluating the expression after the operator under certain circumstances.
##
## In Roc, this is not the case. See the performance notes for #Bool.and for details.
## # In Roc, this is not the case. See the performance notes for #Bool.and for details.
or : Bool, Bool -> Bool
## Exclusive or
xor : Bool, Bool -> Bool
## Returns #True if the two values are *structurally equal*, and #False otherwise.
## Returns `True` if the two values are *structurally equal*, and `False` otherwise.
##
## `a == b` is shorthand for `Bool.isEq a b`
##

View file

@ -8,15 +8,14 @@ isEmpty : Dict * * -> Bool
## Convert each key and value in the #Dict to something new, by calling a conversion
## function on each of them. Then return a new #Map of the converted keys and values.
##
##
## >>> Dict.map {{ 3.14 => "pi", 1.0 => "one" }} \{ key, value } -> { key:
##
##
## >>> Dict.map {[ "", "a", "bc" ]} Str.isEmpty
##
##
## `map` functions like this are common in Roc, and they all work similarly.
## See for example #Result.map, #List.map, and #Set.map.
map :
Dict beforeKey beforeValue,
(\{ key: beforeKey, value: beforeValue } ->
{ key: afterKey, value: afterValue }
) -> Dict afterKey afterValue
({ key: beforeKey, value: beforeValue } -> { key: afterKey, value: afterValue })
-> Dict afterKey afterValue

View file

@ -1,5 +1,54 @@
interface List2
exposes [ List, single, empty, repeat, range, reverse, sort, map, mapWithIndex, mapOrCancel, mapOks, update, updater, allOks, append, prepend, concat, join, joinMap, oks, zip, zipMap, keepIf, dropIf, first, last, get, max, min, put, drop, append, prepend, dropLast, dropFirst, takeFirst, takeLast, split, sublist, walk, walkBackwards, walkUntil, walkBackwardsUntil, len, isEmpty, contains, all, any ]
exposes
[ List
, single
, empty
, repeat
, range
, reverse
, sort
, map
, mapWithIndex
, mapOrCancel
, mapOks
, update
, updater
, allOks
, append
, prepend
, concat
, join
, joinMap
, oks
, zip
, zipMap
, keepIf
, dropIf
, first
, last
, get
, max
, min
, put
, drop
, append
, prepend
, dropLast
, dropFirst
, takeFirst
, takeLast
, split
, sublist
, walk
, walkBackwards
, walkUntil
, walkBackwardsUntil
, len
, isEmpty
, contains
, all
, any
]
imports []
## Types
@ -298,7 +347,7 @@ oks : List (Result elem *) -> List elem
##
## > For a generalized version that returns whatever you like, instead of a `Pair`,
## > see `zipMap`.
zip : List a, List b, -> List [ Pair a b ]*
zip : List a, List b -> List [ Pair a b ]*
## Like `zip` but you can specify what to do with each element.
##
@ -307,7 +356,7 @@ zip : List a, List b, -> List [ Pair a b ]*
## >>> List.zipMap [ 1, 2, 3 ] [ 0, 5, 4 ] [ 2, 1 ] \num1 num2 num3 -> num1 + num2 - num3
##
## Accepts up to 8 lists.
zipMap : List a, List b, (a, b) -> List c
zipMap : List a, List b, (a, b -> c) -> List c
## Filter

View file

@ -51,7 +51,7 @@ interface Num2
##
## In practice, these are rarely needed. It's most common to write
## number literals without any suffix.
Num range : @Num range
Num range : [ @Num range ]
## A fixed-size integer - that is, a number with no fractional component.
##
@ -102,21 +102,21 @@ Num range : @Num range
## * Start by deciding if this integer should allow negative numbers, and choose signed or unsigned accordingly.
## * Next, think about the range of numbers you expect this number to hold. Choose the smallest size you will never expect to overflow, no matter the inputs your program receives. (Validating inputs for size, and presenting the user with an error if they are too big, can help guard against overflow.)
## * Finally, if a particular numeric calculation is running too slowly, you can try experimenting with other number sizes. This rarely makes a meaningful difference, but some processors can operate on different number sizes at different speeds.
Int size : Num (@Int size)
Int size : Num [ @Int size ]
## A signed 8-bit integer, ranging from -128 to 127
I8 : Int @I8
U8 : Int @U8
U16 : Int @U16
I16 : Int @I16
U32 : Int @U32
I32 : Int @I32
I64 : Int @I64
U64 : Int @U64
I128 : Int @I128
U128 : Int @U128
Ilen : Int @Ilen
Nat : Int @Nat
I8 : Int [ @I8 ]
U8 : Int [ @U8 ]
U16 : Int [ @U16 ]
I16 : Int [ @I16 ]
U32 : Int [ @U32 ]
I32 : Int [ @I32 ]
I64 : Int [ @I64 ]
U64 : Int [ @U64 ]
I128 : Int [ @I128 ]
U128 : Int [ @U128 ]
Ilen : Int [ @Ilen ]
Nat : Int [ @Nat ]
## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values.
##
@ -574,9 +574,9 @@ divRound : Int, Int -> Int
## Bitwise
xor : Int -> Int -> Int
xor : Int, Int -> Int
and : Int -> Int -> Int
and : Int, Int -> Int
not : Int -> Int

View file

@ -1,7 +1,7 @@
interface Str2
exposes [ Str2, decimal, split, isEmpty, startsWith, endsWith, contains, anyGraphemes, allGraphemes, join, joinWith, padGraphemesStart, padGraphemesEnd, graphemes, reverseGraphemes, isCaseInsensitiveEq, isCaseInsensitiveNeq, walkGraphemes, isCapitalized, isAllUppercase, isAllLowercase, toUtf8, toUtf16, toUtf32, walkUtf8, walkUtf16, walkUtf32, walkRevUtf8, walkRevUtf16, walkRevUtf32 ]
interface Str
exposes [ Str, decimal, split, isEmpty, startsWith, endsWith, contains, anyGraphemes, allGraphemes, join, joinWith, padGraphemesStart, padGraphemesEnd, graphemes, reverseGraphemes, isCaseInsensitiveEq, isCaseInsensitiveNeq, walkGraphemes, isCapitalized, isAllUppercase, isAllLowercase, toUtf8, toUtf16, toUtf32, walkUtf8, walkUtf16, walkUtf32, walkRevUtf8, walkRevUtf16, walkRevUtf32 ]
imports []
## Types
## # Types
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
## to the basics.

View file

@ -430,6 +430,20 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
),
);
// log : Float a -> Float a
let log_needs_positive = SolvedType::TagUnion(
vec![(TagName::Global("LogNeedsPositive".into()), vec![])],
Box::new(SolvedType::Wildcard),
);
add_type(
Symbol::NUM_LOG,
top_level_function(
vec![float_type(flex(TVAR1))],
Box::new(result_type(float_type(flex(TVAR1)), log_needs_positive)),
),
);
// round : Float a -> Int b
add_type(
Symbol::NUM_ROUND,
@ -722,6 +736,15 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
),
);
// product : List (Num a) -> Num a
add_type(
Symbol::LIST_PRODUCT,
top_level_function(
vec![list_type(num_type(flex(TVAR1)))],
Box::new(num_type(flex(TVAR1))),
),
);
// walk : List elem, (elem -> accum -> accum), accum -> accum
add_type(
Symbol::LIST_WALK,

View file

@ -65,6 +65,13 @@ impl IntroducedVariables {
}
}
fn malformed(env: &mut Env, region: Region, name: &str) {
use roc_problem::can::RuntimeError::*;
let problem = MalformedTypeName((*name).into(), region);
env.problem(roc_problem::can::Problem::RuntimeError(problem));
}
pub fn canonicalize_annotation(
env: &mut Env,
scope: &mut Scope,
@ -446,7 +453,16 @@ fn can_annotation_help(
local_aliases,
references,
),
Wildcard | Malformed(_) => {
Wildcard => {
let var = var_store.fresh();
introduced_variables.insert_wildcard(var);
Type::Variable(var)
}
Malformed(string) => {
malformed(env, region, string);
let var = var_store.fresh();
introduced_variables.insert_wildcard(var);
@ -542,8 +558,9 @@ fn can_assigned_fields<'a>(
field = nested;
continue 'inner;
}
Malformed(_) => {
// TODO report this?
Malformed(string) => {
malformed(env, region, string);
// completely skip this element, advance to the next tag
continue 'outer;
}
@ -645,8 +662,9 @@ fn can_tags<'a>(
tag = nested;
continue 'inner;
}
Tag::Malformed(_) => {
// TODO report this?
Tag::Malformed(string) => {
malformed(env, region, string);
// completely skip this element, advance to the next tag
continue 'outer;
}

View file

@ -77,6 +77,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_CONCAT => list_concat,
LIST_CONTAINS => list_contains,
LIST_SUM => list_sum,
LIST_PRODUCT => list_product,
LIST_PREPEND => list_prepend,
LIST_JOIN => list_join,
LIST_MAP => list_map,
@ -138,6 +139,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
NUM_REM => num_rem,
NUM_IS_MULTIPLE_OF => num_is_multiple_of,
NUM_SQRT => num_sqrt,
NUM_LOG => num_log,
NUM_ROUND => num_round,
NUM_IS_ODD => num_is_odd,
NUM_IS_EVEN => num_is_even,
@ -217,6 +219,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
Symbol::LIST_CONCAT => list_concat,
Symbol::LIST_CONTAINS => list_contains,
Symbol::LIST_SUM => list_sum,
Symbol::LIST_PRODUCT => list_product,
Symbol::LIST_PREPEND => list_prepend,
Symbol::LIST_JOIN => list_join,
Symbol::LIST_MAP => list_map,
@ -274,6 +277,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
Symbol::NUM_REM => num_rem,
Symbol::NUM_IS_MULTIPLE_OF => num_is_multiple_of,
Symbol::NUM_SQRT => num_sqrt,
Symbol::NUM_LOG => num_log,
Symbol::NUM_ROUND => num_round,
Symbol::NUM_IS_ODD => num_is_odd,
Symbol::NUM_IS_EVEN => num_is_even,
@ -411,8 +415,8 @@ fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
/// Num.maxInt : Int
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
let int_var = var_store.fresh();
let int_percision_var = var_store.fresh();
let body = Int(int_var, int_percision_var, i64::MAX.into());
let int_precision_var = var_store.fresh();
let body = Int(int_var, int_precision_var, i64::MAX.into());
Def {
annotation: None,
@ -426,8 +430,8 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// Num.minInt : Int
fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
let int_var = var_store.fresh();
let int_percision_var = var_store.fresh();
let body = Int(int_var, int_percision_var, i64::MIN.into());
let int_precision_var = var_store.fresh();
let body = Int(int_var, int_precision_var, i64::MIN.into());
Def {
annotation: None,
@ -1131,50 +1135,82 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let float_var = var_store.fresh();
let unbound_zero_var = var_store.fresh();
let percision_var = var_store.fresh();
let precision_var = var_store.fresh();
let ret_var = var_store.fresh();
let body = If {
branch_var: ret_var,
cond_var: bool_var,
branches: vec![(
// if-condition
no_region(
// Num.neq denominator 0
RunLowLevel {
op: LowLevel::NotEq,
args: vec![
(float_var, Var(Symbol::ARG_1)),
(float_var, Float(unbound_zero_var, percision_var, 0.0)),
],
ret_var: bool_var,
},
),
// denominator was not zero
no_region(
// Ok (Float.#divUnchecked numerator denominator)
tag(
"Ok",
vec![
// Num.#divUnchecked numerator denominator
RunLowLevel {
op: LowLevel::NumSqrtUnchecked,
args: vec![(float_var, Var(Symbol::ARG_1))],
ret_var: float_var,
},
],
var_store,
),
),
)],
final_else: Box::new(
// denominator was zero
no_region(RunLowLevel {
op: LowLevel::NumGte,
args: vec![
(float_var, Var(Symbol::ARG_1)),
(float_var, Float(unbound_zero_var, precision_var, 0.0)),
],
ret_var: bool_var,
}),
no_region(tag(
"Err",
vec![tag("DivByZero", Vec::new(), var_store)],
"Ok",
vec![RunLowLevel {
op: LowLevel::NumSqrtUnchecked,
args: vec![(float_var, Var(Symbol::ARG_1))],
ret_var: float_var,
}],
var_store,
)),
),
)],
final_else: Box::new(no_region(tag(
"Err",
vec![tag("SqrtOfNegative", Vec::new(), var_store)],
var_store,
))),
};
defn(
symbol,
vec![(float_var, Symbol::ARG_1)],
var_store,
body,
ret_var,
)
}
/// Num.log : Float -> Result Float [ LogNeedsPositive ]*
fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let float_var = var_store.fresh();
let unbound_zero_var = var_store.fresh();
let precision_var = var_store.fresh();
let ret_var = var_store.fresh();
let body = If {
branch_var: ret_var,
cond_var: bool_var,
branches: vec![(
no_region(RunLowLevel {
op: LowLevel::NumGt,
args: vec![
(float_var, Var(Symbol::ARG_1)),
(float_var, Float(unbound_zero_var, precision_var, 0.0)),
],
ret_var: bool_var,
}),
no_region(tag(
"Ok",
vec![RunLowLevel {
op: LowLevel::NumLogUnchecked,
args: vec![(float_var, Var(Symbol::ARG_1))],
ret_var: float_var,
}],
var_store,
)),
)],
final_else: Box::new(no_region(tag(
"Err",
vec![tag("LogNeedsPositive", Vec::new(), var_store)],
var_store,
))),
};
defn(
@ -1385,8 +1421,8 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// Num.maxI128: I128
fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
let int_var = var_store.fresh();
let int_percision_var = var_store.fresh();
let body = Int(int_var, int_percision_var, i128::MAX);
let int_precision_var = var_store.fresh();
let body = Int(int_var, int_precision_var, i128::MAX);
let std = roc_builtins::std::types();
let solved = std.get(&symbol).unwrap();
@ -2116,22 +2152,12 @@ fn list_walk_backwards(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// List.sum : List (Num a) -> Num a
fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();
let result_var = var_store.fresh();
lowlevel_1(symbol, LowLevel::ListSum, var_store)
}
let body = RunLowLevel {
op: LowLevel::ListSum,
args: vec![(list_var, Var(Symbol::ARG_1))],
ret_var: result_var,
};
defn(
symbol,
vec![(list_var, Symbol::ARG_1)],
var_store,
body,
result_var,
)
/// List.product : List (Num a) -> Num a
fn list_product(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::ListProduct, var_store)
}
/// List.keepIf : List elem, (elem -> Bool) -> List elem
@ -2661,7 +2687,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let num_var = var_store.fresh();
let unbound_zero_var = var_store.fresh();
let percision_var = var_store.fresh();
let precision_var = var_store.fresh();
let ret_var = var_store.fresh();
let body = If {
@ -2675,7 +2701,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::NotEq,
args: vec![
(num_var, Var(Symbol::ARG_2)),
(num_var, Float(unbound_zero_var, percision_var, 0.0)),
(num_var, Float(unbound_zero_var, precision_var, 0.0)),
],
ret_var: bool_var,
},
@ -2724,7 +2750,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let num_var = var_store.fresh();
let unbound_zero_var = var_store.fresh();
let unbound_zero_percision_var = var_store.fresh();
let unbound_zero_precision_var = var_store.fresh();
let ret_var = var_store.fresh();
let body = If {
@ -2740,7 +2766,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
(num_var, Var(Symbol::ARG_2)),
(
num_var,
Int(unbound_zero_var, unbound_zero_percision_var, 0),
Int(unbound_zero_var, unbound_zero_precision_var, 0),
),
],
ret_var: bool_var,
@ -2795,7 +2821,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();
let len_var = var_store.fresh();
let zero_var = var_store.fresh();
let zero_percision_var = var_store.fresh();
let zero_precision_var = var_store.fresh();
let list_elem_var = var_store.fresh();
let ret_var = var_store.fresh();
@ -2810,7 +2836,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel {
op: LowLevel::NotEq,
args: vec![
(len_var, Int(zero_var, zero_percision_var, 0)),
(len_var, Int(zero_var, zero_precision_var, 0)),
(
len_var,
RunLowLevel {
@ -2834,7 +2860,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::ListGetUnsafe,
args: vec![
(list_var, Var(Symbol::ARG_1)),
(len_var, Int(zero_var, zero_percision_var, 0)),
(len_var, Int(zero_var, zero_precision_var, 0)),
],
ret_var: list_elem_var,
},
@ -2876,7 +2902,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();
let len_var = var_store.fresh();
let num_var = var_store.fresh();
let num_percision_var = var_store.fresh();
let num_precision_var = var_store.fresh();
let list_elem_var = var_store.fresh();
let ret_var = var_store.fresh();
@ -2891,7 +2917,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel {
op: LowLevel::NotEq,
args: vec![
(len_var, Int(num_var, num_percision_var, 0)),
(len_var, Int(num_var, num_precision_var, 0)),
(
len_var,
RunLowLevel {
@ -2930,7 +2956,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
ret_var: len_var,
},
),
(arg_var, Int(num_var, num_percision_var, 1)),
(arg_var, Int(num_var, num_precision_var, 1)),
],
ret_var: len_var,
},

View file

@ -1481,7 +1481,7 @@ fn to_pending_def<'a>(
}
}
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) | Nested(sub_def) => {
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {
to_pending_def(env, var_store, sub_def, scope, pattern_type)
}

View file

@ -408,46 +408,7 @@ pub fn canonicalize_expr<'a>(
}
ast::Expr::Var { module_name, ident } => {
canonicalize_lookup(env, scope, module_name, ident, region)
} //ast::Expr::InterpolatedStr(pairs, suffix) => {
// let mut output = Output::new();
// let can_pairs: Vec<(String, Located<Expr>)> = pairs
// .into_iter()
// .map(|(string, loc_ident)| {
// // From a language design perspective, we only permit idents in interpolation.
// // However, in a canonical Expr we store it as a full Expr, not a Symbol.
// // This is so that we can resolve it to either Var or Unrecognized; if we
// // stored it as a Symbol, we couldn't record runtime errors here.
// let can_expr = match resolve_ident(
// &env,
// &scope,
// loc_ident.value,
// &mut output.references,
// ) {
// Ok(symbol) => Var(symbol),
// Err(ident) => {
// let loc_ident = Located {
// region: loc_ident.region,
// value: ident,
// };
// env.problem(Problem::LookupNotInScope(loc_ident.clone()));
// RuntimeError(LookupNotInScope(loc_ident))
// }
// };
// (
// string,
// Located {
// region: loc_ident.region,
// value: can_expr,
// },
// )
// })
// .collect();
// (InterpolatedStr(can_pairs, suffix), output)
//}
}
ast::Expr::Defs(loc_defs, loc_ret) => {
can_defs_with_return(
env,
@ -767,11 +728,6 @@ pub fn canonicalize_expr<'a>(
(RuntimeError(problem), Output::default())
}
ast::Expr::Nested(sub_expr) => {
let (answer, output) = canonicalize_expr(env, var_store, scope, region, sub_expr);
(answer.value, output)
}
ast::Expr::NonBase10Int {
string,
base,

View file

@ -4,7 +4,7 @@ use roc_module::ident::ModuleName;
use roc_module::operator::BinOp::Pizza;
use roc_module::operator::{BinOp, CalledVia};
use roc_parse::ast::Expr::{self, *};
use roc_parse::ast::{AssignedField, Def, Pattern, WhenBranch};
use roc_parse::ast::{AssignedField, Def, WhenBranch};
use roc_region::all::{Located, Region};
// BinOp precedence logic adapted from Gluon by Markus Westerlind, MIT licensed
@ -59,7 +59,7 @@ fn new_op_call_expr<'a>(
Located { value, region }
}
fn desugar_defs<'a>(
fn desugar_def_helps<'a>(
arena: &'a Bump,
region: Region,
defs: &'a [&'a Located<Def<'a>>],
@ -88,39 +88,23 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
use roc_parse::ast::Def::*;
match def {
Body(loc_pattern, loc_expr) | Nested(Body(loc_pattern, loc_expr)) => {
Body(loc_pattern, desugar_expr(arena, loc_expr))
}
SpaceBefore(def, _)
| SpaceAfter(def, _)
| Nested(SpaceBefore(def, _))
| Nested(SpaceAfter(def, _)) => desugar_def(arena, def),
Nested(Nested(def)) => desugar_def(arena, def),
alias @ Alias { .. } => Nested(alias),
Nested(alias @ Alias { .. }) => Nested(alias),
ann @ Annotation(_, _) => Nested(ann),
Nested(ann @ Annotation(_, _)) => Nested(ann),
Body(loc_pattern, loc_expr) => Body(loc_pattern, desugar_expr(arena, loc_expr)),
SpaceBefore(def, _) | SpaceAfter(def, _) => desugar_def(arena, def),
alias @ Alias { .. } => *alias,
ann @ Annotation(_, _) => *ann,
AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr,
}
| Nested(AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr,
}) => AnnotatedBody {
} => AnnotatedBody {
ann_pattern,
ann_type,
comment: *comment,
body_pattern: *body_pattern,
body_expr: desugar_expr(arena, body_expr),
},
Nested(NotYetImplemented(s)) => todo!("{}", s),
NotYetImplemented(s) => todo!("{}", s),
}
}
@ -130,33 +114,22 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> {
match &loc_expr.value {
Float(_)
| Nested(Float(_))
| Num(_)
| Nested(Num(_))
| NonBase10Int { .. }
| Nested(NonBase10Int { .. })
| Str(_)
| Nested(Str(_))
| AccessorFunction(_)
| Nested(AccessorFunction(_))
| Var { .. }
| Nested(Var { .. })
| MalformedIdent(_, _)
| Nested(MalformedIdent(_, _))
| MalformedClosure
| Nested(MalformedClosure)
| PrecedenceConflict { .. }
| Nested(PrecedenceConflict { .. })
| GlobalTag(_)
| Nested(GlobalTag(_))
| PrivateTag(_)
| Nested(PrivateTag(_)) => loc_expr,
| PrivateTag(_) => loc_expr,
Access(sub_expr, paths) | Nested(Access(sub_expr, paths)) => {
Access(sub_expr, paths) => {
let region = loc_expr.region;
let loc_sub_expr = Located {
region,
value: Nested(sub_expr),
value: **sub_expr,
};
let value = Access(&desugar_expr(arena, arena.alloc(loc_sub_expr)).value, paths);
@ -165,11 +138,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
List {
items,
final_comments,
}
| Nested(List {
items,
final_comments,
}) => {
} => {
let mut new_items = Vec::with_capacity_in(items.len(), arena);
for item in items.iter() {
@ -189,11 +158,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
Record {
fields,
final_comments,
}
| Nested(Record {
fields,
final_comments,
}) => {
} => {
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
for field in fields.iter() {
@ -220,12 +185,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
fields,
update,
final_comments,
}
| Nested(RecordUpdate {
fields,
update,
final_comments,
}) => {
} => {
// NOTE the `update` field is always a `Var { .. }` and does not need to be desugared
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
@ -249,14 +209,11 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
},
})
}
Closure(loc_patterns, loc_ret) | Nested(Closure(loc_patterns, loc_ret)) => {
arena.alloc(Located {
region: loc_expr.region,
value: Closure(loc_patterns, desugar_expr(arena, loc_ret)),
})
}
Backpassing(loc_patterns, loc_body, loc_ret)
| Nested(Backpassing(loc_patterns, loc_body, loc_ret)) => {
Closure(loc_patterns, loc_ret) => arena.alloc(Located {
region: loc_expr.region,
value: Closure(loc_patterns, desugar_expr(arena, loc_ret)),
}),
Backpassing(loc_patterns, loc_body, loc_ret) => {
// loc_patterns <- loc_body
//
// loc_ret
@ -293,13 +250,9 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
}
}
}
BinOps(lefts, right) | Nested(BinOps(lefts, right)) => {
desugar_bin_ops(arena, loc_expr.region, lefts, right)
}
Defs(defs, loc_ret) | Nested(Defs(defs, loc_ret)) => {
desugar_defs(arena, loc_expr.region, *defs, loc_ret)
}
Apply(loc_fn, loc_args, called_via) | Nested(Apply(loc_fn, loc_args, called_via)) => {
BinOps(lefts, right) => desugar_bin_ops(arena, loc_expr.region, lefts, right),
Defs(defs, loc_ret) => desugar_def_helps(arena, loc_expr.region, *defs, loc_ret),
Apply(loc_fn, loc_args, called_via) => {
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
for loc_arg in loc_args.iter() {
@ -313,7 +266,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
region: loc_expr.region,
})
}
When(loc_cond_expr, branches) | Nested(When(loc_cond_expr, branches)) => {
When(loc_cond_expr, branches) => {
let loc_desugared_cond = &*arena.alloc(desugar_expr(arena, &loc_cond_expr));
let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena);
@ -321,15 +274,10 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
let desugared = desugar_expr(arena, &branch.value);
let mut alternatives = Vec::with_capacity_in(branch.patterns.len(), arena);
for loc_pattern in branch.patterns.iter() {
alternatives.push(Located {
region: loc_pattern.region,
value: Pattern::Nested(&loc_pattern.value),
})
}
alternatives.extend(branch.patterns.iter().copied());
let desugared_guard = if let Some(guard) = &branch.guard {
Some(desugar_expr(arena, guard).clone())
Some(*desugar_expr(arena, guard))
} else {
None
};
@ -338,10 +286,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
desugared_branches.push(&*arena.alloc(WhenBranch {
patterns: alternatives,
value: Located {
region: desugared.region,
value: Nested(&desugared.value),
},
value: *desugared,
guard: desugared_guard,
}));
}
@ -353,7 +298,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
region: loc_expr.region,
})
}
UnaryOp(loc_arg, loc_op) | Nested(UnaryOp(loc_arg, loc_op)) => {
UnaryOp(loc_arg, loc_op) => {
use roc_module::operator::UnaryOp::*;
let region = loc_op.region;
@ -379,24 +324,18 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
region: loc_expr.region,
})
}
SpaceBefore(expr, _)
| Nested(SpaceBefore(expr, _))
| SpaceAfter(expr, _)
| Nested(SpaceAfter(expr, _))
| ParensAround(expr)
| Nested(ParensAround(expr))
| Nested(Nested(expr)) => {
SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => {
// Since we've already begun canonicalization, spaces and parens
// are no longer needed and should be dropped.
desugar_expr(
arena,
arena.alloc(Located {
value: Nested(expr),
value: **expr,
region: loc_expr.region,
}),
)
}
If(if_thens, final_else_branch) | Nested(If(if_thens, final_else_branch)) => {
If(if_thens, final_else_branch) => {
// If does not get desugared into `when` so we can give more targetted error messages during type checking.
let desugared_final_else = &*arena.alloc(desugar_expr(arena, &final_else_branch));
@ -404,8 +343,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
for (condition, then_branch) in if_thens.iter() {
desugared_if_thens.push((
desugar_expr(arena, condition).clone(),
desugar_expr(arena, then_branch).clone(),
*desugar_expr(arena, condition),
*desugar_expr(arena, then_branch),
));
}

View file

@ -238,7 +238,7 @@ pub fn canonicalize_pattern<'a>(
ptype => unsupported_pattern(env, ptype, region),
},
SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) | Nested(sub_pattern) => {
SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) => {
return canonicalize_pattern(env, var_store, scope, pattern_type, sub_pattern, region)
}
RecordDestructure(patterns) => {

View file

@ -19,7 +19,6 @@ impl<'a> Formattable<'a> for Def<'a> {
SpaceBefore(sub_def, spaces) | SpaceAfter(sub_def, spaces) => {
spaces.iter().any(|s| s.is_comment()) || sub_def.is_multiline()
}
Nested(def) => def.is_multiline(),
NotYetImplemented(s) => todo!("{}", s),
}
}
@ -99,7 +98,6 @@ impl<'a> Formattable<'a> for Def<'a> {
sub_def.format(buf, indent);
fmt_spaces(buf, spaces.iter(), indent);
}
Nested(def) => def.format(buf, indent),
NotYetImplemented(s) => todo!("{}", s),
}
}

View file

@ -75,7 +75,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
expr: loc_subexpr, ..
}) => loc_subexpr.is_multiline(),
ParensAround(subexpr) | Nested(subexpr) => subexpr.is_multiline(),
ParensAround(subexpr) => subexpr.is_multiline(),
Closure(loc_patterns, loc_body) => {
// check the body first because it's more likely to be multiline
@ -298,9 +298,6 @@ impl<'a> Formattable<'a> for Expr<'a> {
sub_expr.format_with_options(buf, parens, newlines, indent);
}
Nested(nested_expr) => {
nested_expr.format_with_options(buf, parens, newlines, indent);
}
AccessorFunction(key) => {
buf.push('.');
buf.push_str(key);
@ -508,8 +505,6 @@ fn empty_line_before_expr<'a>(expr: &'a Expr<'a>) -> bool {
false
}
Nested(nested_expr) => empty_line_before_expr(nested_expr),
_ => false,
}
}

View file

@ -22,8 +22,6 @@ impl<'a> Formattable<'a> for Pattern<'a> {
spaces.iter().any(|s| s.is_comment())
}
Pattern::Nested(nested_pat) => nested_pat.is_multiline(),
Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()),
Pattern::RequiredField(_, subpattern) => subpattern.is_multiline(),
@ -153,10 +151,6 @@ impl<'a> Formattable<'a> for Pattern<'a> {
}
}
Nested(sub_pattern) => {
sub_pattern.format_with_options(buf, parens, newlines, indent);
}
// Malformed
Malformed(string) | MalformedIdent(string, _) => buf.push_str(string),
QualifiedIdentifier { module_name, ident } => {

View file

@ -28,7 +28,7 @@ mod test_fmt {
assert_eq!(buf, expected)
}
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", input, error)
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", input, error)
};
}
@ -1833,23 +1833,23 @@ mod test_fmt {
indoc!(
r#"
when b is
1 | 2 |
3
->
1 | 2 |
3
->
4
5 | 6 | 7 ->
4
5 | 6 | 7 ->
8
9
| 10 -> 11
8
9
| 10 -> 11
12 | 13 ->
when c is
14 | 15 -> 16
17
| 18 -> 19
20 -> 21
12 | 13 ->
when c is
14 | 15 -> 16
17
| 18 -> 19
20 -> 21
"#
),

View file

@ -7,8 +7,8 @@ use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map,
list_map2, list_map3, list_map_with_index, list_prepend, list_repeat, list_reverse, list_set,
list_single, list_sum, list_walk, list_walk_backwards,
list_map2, list_map3, list_map_with_index, list_prepend, list_product, list_repeat,
list_reverse, list_set, list_single, list_sum, list_walk, list_walk_backwards,
};
use crate::llvm::build_str::{
str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8,
@ -90,9 +90,9 @@ pub enum OptLevel {
Optimize,
}
impl Into<OptimizationLevel> for OptLevel {
fn into(self) -> OptimizationLevel {
match self {
impl From<OptLevel> for OptimizationLevel {
fn from(level: OptLevel) -> Self {
match level {
OptLevel::Normal => OptimizationLevel::None,
OptLevel::Optimize => OptimizationLevel::Aggressive,
}
@ -337,6 +337,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
f64_type.fn_type(&[f64_type.into()], false),
);
add_intrinsic(
module,
LLVM_LOG_F64,
f64_type.fn_type(&[f64_type.into()], false),
);
add_intrinsic(
module,
LLVM_LROUND_I64_F64,
@ -455,6 +461,7 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";
static LLVM_SQRT_F64: &str = "llvm.sqrt.f64";
static LLVM_LOG_F64: &str = "llvm.log.f64";
static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64";
static LLVM_FABS_F64: &str = "llvm.fabs.f64";
static LLVM_SIN_F64: &str = "llvm.sin.f64";
@ -2030,7 +2037,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
// access itself!
// scope = scope.clone();
scope.insert(*symbol, (layout.clone(), val));
scope.insert(*symbol, (*layout, val));
stack.push(*symbol);
}
@ -2063,8 +2070,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
} => {
// when the fail case is just Rethrow, there is no cleanup work to do
// so we can just treat this invoke as a normal call
let stmt =
roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), layout.clone(), pass);
let stmt = roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass);
build_exp_stmt(env, layout_ids, scope, parent, &stmt)
}
@ -2088,7 +2094,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
scope,
parent,
*symbol,
layout.clone(),
*layout,
function_value.into(),
call.arguments,
None,
@ -2108,7 +2114,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
scope,
parent,
*symbol,
layout.clone(),
*layout,
function_ptr.into(),
call.arguments,
None,
@ -2135,7 +2141,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
scope,
parent,
*symbol,
layout.clone(),
*layout,
function_ptr.into(),
call.arguments,
Some(closure_data),
@ -2194,7 +2200,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
basic_type_from_layout(env.arena, env.context, &ret_layout, env.ptr_bytes);
let switch_args = SwitchArgsIr {
cond_layout: cond_layout.clone(),
cond_layout: *cond_layout,
cond_symbol: *cond_symbol,
branches,
default_branch: default_branch.1,
@ -2242,7 +2248,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
for (ptr, param) in joinpoint_args.iter().zip(parameters.iter()) {
let value = env.builder.build_load(*ptr, "load_jp_argument");
scope.insert(param.symbol, (param.layout.clone(), value));
scope.insert(param.symbol, (param.layout, value));
}
// put the continuation in
@ -2277,7 +2283,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
match modify {
Inc(symbol, inc_amount) => {
let (value, layout) = load_symbol_and_layout(scope, symbol);
let layout = layout.clone();
let layout = *layout;
if layout.contains_refcounted() {
increment_refcount_layout(
@ -3177,7 +3183,7 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
let function_pointer_type = {
let function_layout =
ClosureLayout::extend_function_layout(arena, arguments, closure.clone(), result);
ClosureLayout::extend_function_layout(arena, arguments, *closure, result);
// this is already a (function) pointer type
basic_type_from_layout(arena, context, &function_layout, env.ptr_bytes)
@ -3252,7 +3258,7 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
);
// STEP 4: build a {} -> u64 function that gives the size of the closure
let layout = Layout::Closure(arguments, closure.clone(), result);
let layout = Layout::Closure(arguments, *closure, result);
build_host_exposed_alias_size(env, def_name, alias_symbol, &layout);
}
@ -3455,7 +3461,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
// Add args to scope
for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) {
set_name(arg_val, arg_symbol.ident_string(&env.interns));
scope.insert(*arg_symbol, (layout.clone(), arg_val));
scope.insert(*arg_symbol, (*layout, arg_val));
}
let body = build_exp_stmt(env, layout_ids, &mut scope, fn_val, &proc.body);
@ -3931,6 +3937,13 @@ fn run_low_level<'a, 'ctx, 'env>(
list_sum(env, parent, list, layout)
}
ListProduct => {
debug_assert_eq!(args.len(), 1);
let list = load_symbol(scope, &args[0]);
list_product(env, parent, list, layout)
}
ListAppend => {
// List.append : List elem, elem -> List elem
debug_assert_eq!(args.len(), 2);
@ -3963,8 +3976,8 @@ fn run_low_level<'a, 'ctx, 'env>(
list_join(env, inplace, parent, list, outer_list_layout)
}
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor
| NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
debug_assert_eq!(args.len(), 1);
let (arg, arg_layout) = load_symbol_and_layout(scope, &args[0]);
@ -4559,7 +4572,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
{
env.builder.position_at_end(pass_block);
scope.insert(symbol, (ret_layout.clone(), call_result));
scope.insert(symbol, (*ret_layout, call_result));
build_exp_stmt(env, layout_ids, scope, parent, pass);
@ -5273,6 +5286,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]),
NumSqrtUnchecked => env.call_intrinsic(LLVM_SQRT_F64, &[arg.into()]),
NumLogUnchecked => env.call_intrinsic(LLVM_LOG_F64, &[arg.into()]),
NumRound => env.call_intrinsic(LLVM_LROUND_I64_F64, &[arg.into()]),
NumSin => env.call_intrinsic(LLVM_SIN_F64, &[arg.into()]),
NumCos => env.call_intrinsic(LLVM_COS_F64, &[arg.into()]),

View file

@ -717,11 +717,7 @@ pub fn dict_walk<'a, 'ctx, 'env>(
env,
layout_ids,
stepper_layout,
&[
key_layout.clone(),
value_layout.clone(),
accum_layout.clone(),
],
&[*key_layout, *value_layout, *accum_layout],
)
.as_global_value()
.as_pointer_value();

View file

@ -70,7 +70,7 @@ fn build_hash_layout<'a, 'ctx, 'env>(
unreachable!("recursion pointers should never be hashed directly")
}
WhenRecursive::Loop(union_layout) => {
let layout = Layout::Union(union_layout.clone());
let layout = Layout::Union(union_layout);
let bt = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
@ -287,7 +287,7 @@ fn hash_struct<'a, 'ctx, 'env>(
unreachable!("The current layout should not be recursive, but is")
}
WhenRecursive::Loop(union_layout) => {
let field_layout = Layout::Union(union_layout.clone());
let field_layout = Layout::Union(*union_layout);
let bt = basic_type_from_layout(
env.arena,
@ -811,7 +811,7 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
env,
layout_ids,
field_layouts,
WhenRecursive::Loop(union_layout.clone()),
WhenRecursive::Loop(*union_layout),
seed,
struct_value,
)

View file

@ -423,7 +423,7 @@ pub fn list_reverse<'a, 'ctx, 'env>(
let ctx = env.context;
let wrapper_struct = list.into_struct_value();
let (input_inplace, element_layout) = match list_layout.clone() {
let (input_inplace, element_layout) = match *list_layout {
Layout::Builtin(Builtin::EmptyList) => (
InPlace::InPlace,
// this pointer will never actually be dereferenced
@ -434,7 +434,7 @@ pub fn list_reverse<'a, 'ctx, 'env>(
MemoryMode::Unique => InPlace::InPlace,
MemoryMode::Refcounted => InPlace::Clone,
},
elem_layout.clone(),
*elem_layout,
),
_ => unreachable!("Invalid layout {:?} in List.reverse", list_layout),
@ -788,6 +788,81 @@ pub fn list_sum<'a, 'ctx, 'env>(
builder.build_load(accum_alloca, "load_final_acum")
}
/// List.product : List (Num a) -> Num a
pub fn list_product<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
list: BasicValueEnum<'ctx>,
default_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let ctx = env.context;
let builder = env.builder;
let list_wrapper = list.into_struct_value();
let len = list_len(env.builder, list_wrapper);
let accum_type = basic_type_from_layout(env.arena, ctx, default_layout, env.ptr_bytes);
let accum_alloca = builder.build_alloca(accum_type, "alloca_walk_right_accum");
let default: BasicValueEnum = match accum_type {
BasicTypeEnum::IntType(int_type) => int_type.const_int(1, false).into(),
BasicTypeEnum::FloatType(float_type) => float_type.const_float(1.0).into(),
_ => unreachable!(""),
};
builder.build_store(accum_alloca, default);
let then_block = ctx.append_basic_block(parent, "then");
let cont_block = ctx.append_basic_block(parent, "branchcont");
let condition = builder.build_int_compare(
IntPredicate::UGT,
len,
ctx.i64_type().const_zero(),
"list_non_empty",
);
builder.build_conditional_branch(condition, then_block, cont_block);
builder.position_at_end(then_block);
let elem_ptr_type = get_ptr_type(&accum_type, AddressSpace::Generic);
let list_ptr = load_list_ptr(builder, list_wrapper, elem_ptr_type);
let walk_right_loop = |_, elem: BasicValueEnum<'ctx>| {
// load current accumulator
let current = builder.build_load(accum_alloca, "retrieve_accum");
let new_current = build_num_binop(
env,
parent,
current,
default_layout,
elem,
default_layout,
roc_module::low_level::LowLevel::NumMul,
);
builder.build_store(accum_alloca, new_current);
};
incrementing_elem_loop(
builder,
ctx,
parent,
list_ptr,
len,
"#index",
walk_right_loop,
);
builder.build_unconditional_branch(cont_block);
builder.position_at_end(cont_block);
builder.build_load(accum_alloca, "load_final_acum")
}
/// List.walk : List elem, (elem -> accum -> accum), accum -> accum
pub fn list_walk<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
@ -868,7 +943,7 @@ fn list_walk_generic<'a, 'ctx, 'env>(
env,
layout_ids,
func_layout,
&[element_layout.clone(), default_layout.clone()],
&[*element_layout, *default_layout],
)
.as_global_value()
.as_pointer_value();
@ -959,7 +1034,7 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
env.builder.build_store(transform_ptr, transform);
let stepper_caller =
build_transform_caller(env, layout_ids, transform_layout, &[element_layout.clone()])
build_transform_caller(env, layout_ids, transform_layout, &[*element_layout])
.as_global_value()
.as_pointer_value();
@ -1066,7 +1141,7 @@ pub fn list_keep_result<'a, 'ctx, 'env>(
env.builder.build_store(transform_ptr, transform);
let stepper_caller =
build_transform_caller(env, layout_ids, transform_layout, &[before_layout.clone()])
build_transform_caller(env, layout_ids, transform_layout, &[*before_layout])
.as_global_value()
.as_pointer_value();
@ -1130,7 +1205,7 @@ pub fn list_map<'a, 'ctx, 'env>(
list,
element_layout,
bitcode::LIST_MAP,
&[element_layout.clone()],
&[*element_layout],
)
}
@ -1151,7 +1226,7 @@ pub fn list_map_with_index<'a, 'ctx, 'env>(
list,
element_layout,
bitcode::LIST_MAP_WITH_INDEX,
&[Layout::Builtin(Builtin::Usize), element_layout.clone()],
&[Layout::Builtin(Builtin::Usize), *element_layout],
)
}
@ -1255,7 +1330,7 @@ pub fn list_map2<'a, 'ctx, 'env>(
let transform_ptr = builder.build_alloca(transform.get_type(), "transform_ptr");
env.builder.build_store(transform_ptr, transform);
let argument_layouts = [element1_layout.clone(), element2_layout.clone()];
let argument_layouts = [*element1_layout, *element2_layout];
let stepper_caller =
build_transform_caller(env, layout_ids, transform_layout, &argument_layouts)
.as_global_value()
@ -1351,11 +1426,7 @@ pub fn list_map3<'a, 'ctx, 'env>(
let transform_ptr = builder.build_alloca(transform.get_type(), "transform_ptr");
env.builder.build_store(transform_ptr, transform);
let argument_layouts = [
element1_layout.clone(),
element2_layout.clone(),
element3_layout.clone(),
];
let argument_layouts = [*element1_layout, *element2_layout, *element3_layout];
let stepper_caller =
build_transform_caller(env, layout_ids, transform_layout, &argument_layouts)
.as_global_value()

View file

@ -102,7 +102,7 @@ fn build_eq_builtin<'a, 'ctx, 'env>(
Builtin::List(_, elem) => build_list_eq(
env,
layout_ids,
&Layout::Builtin(builtin.clone()),
&Layout::Builtin(*builtin),
elem,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -170,7 +170,7 @@ fn build_eq<'a, 'ctx, 'env>(
}
WhenRecursive::Loop(union_layout) => {
let layout = Layout::Union(union_layout.clone());
let layout = Layout::Union(union_layout);
let bt = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
@ -188,7 +188,7 @@ fn build_eq<'a, 'ctx, 'env>(
build_tag_eq(
env,
layout_ids,
WhenRecursive::Loop(union_layout.clone()),
WhenRecursive::Loop(union_layout),
&layout,
&union_layout,
field1_cast.into(),
@ -262,7 +262,7 @@ fn build_neq_builtin<'a, 'ctx, 'env>(
let is_equal = build_list_eq(
env,
layout_ids,
&Layout::Builtin(builtin.clone()),
&Layout::Builtin(*builtin),
elem,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -690,7 +690,7 @@ fn build_struct_eq_help<'a, 'ctx, 'env>(
unreachable!("The current layout should not be recursive, but is")
}
WhenRecursive::Loop(union_layout) => {
let field_layout = Layout::Union(union_layout.clone());
let field_layout = Layout::Union(*union_layout);
let bt = basic_type_from_layout(
env.arena,
@ -717,7 +717,7 @@ fn build_struct_eq_help<'a, 'ctx, 'env>(
field2_cast.into(),
&field_layout,
&field_layout,
WhenRecursive::Loop(union_layout.clone()),
WhenRecursive::Loop(*union_layout),
)
.into_int_value()
}
@ -1234,7 +1234,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
env,
layout_ids,
field_layouts,
WhenRecursive::Loop(union_layout.clone()),
WhenRecursive::Loop(*union_layout),
struct1,
struct2,
)

View file

@ -454,7 +454,7 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>(
env,
layout_ids,
mode,
&WhenRecursive::Loop(variant.clone()),
&WhenRecursive::Loop(*variant),
tags,
value.into_pointer_value(),
true,
@ -470,7 +470,7 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>(
env,
layout_ids,
mode,
&WhenRecursive::Loop(variant.clone()),
&WhenRecursive::Loop(*variant),
&*env.arena.alloc([other_fields]),
value.into_pointer_value(),
true,
@ -484,7 +484,7 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>(
env,
layout_ids,
mode,
&WhenRecursive::Loop(variant.clone()),
&WhenRecursive::Loop(*variant),
&*env.arena.alloc([*fields]),
value.into_pointer_value(),
true,
@ -497,7 +497,7 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>(
env,
layout_ids,
mode,
&WhenRecursive::Loop(variant.clone()),
&WhenRecursive::Loop(*variant),
tags,
value.into_pointer_value(),
false,
@ -549,7 +549,7 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>(
unreachable!("recursion pointers should never be hashed directly")
}
WhenRecursive::Loop(union_layout) => {
let layout = Layout::Union(union_layout.clone());
let layout = Layout::Union(*union_layout);
let bt = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);

View file

@ -8,9 +8,9 @@ pub enum RocCallResult<T> {
Failure(*mut c_char),
}
impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
fn into(self) -> Result<T, String> {
match self {
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
fn from(call_result: RocCallResult<T>) -> Self {
match call_result {
Success(value) => Ok(value),
Failure(failure) => Err({
let raw = unsafe { CString::from_raw(failure) };

View file

@ -1,6 +1,6 @@
#![warn(clippy::all, clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
use bumpalo::{collections::Vec, Bump};
use roc_builtins::bitcode;
@ -105,7 +105,7 @@ where
} => {
// for now, treat invoke as a normal call
let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), layout.clone(), pass);
let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass);
self.build_stmt(&stmt)
}
Stmt::Switch {
@ -252,32 +252,20 @@ where
x => Err(format!("layout, {:?}, not implemented yet", x)),
}
}
LowLevel::NumAcos => self.build_fn_call(
sym,
bitcode::NUM_ACOS.to_string(),
args,
&[layout.clone()],
layout,
),
LowLevel::NumAsin => self.build_fn_call(
sym,
bitcode::NUM_ASIN.to_string(),
args,
&[layout.clone()],
layout,
),
LowLevel::NumAtan => self.build_fn_call(
sym,
bitcode::NUM_ATAN.to_string(),
args,
&[layout.clone()],
layout,
),
LowLevel::NumAcos => {
self.build_fn_call(sym, bitcode::NUM_ACOS.to_string(), args, &[*layout], layout)
}
LowLevel::NumAsin => {
self.build_fn_call(sym, bitcode::NUM_ASIN.to_string(), args, &[*layout], layout)
}
LowLevel::NumAtan => {
self.build_fn_call(sym, bitcode::NUM_ATAN.to_string(), args, &[*layout], layout)
}
LowLevel::NumPowInt => self.build_fn_call(
sym,
bitcode::NUM_POW_INT.to_string(),
args,
&[layout.clone(), layout.clone()],
&[*layout, *layout],
layout,
),
LowLevel::NumSub => {
@ -472,7 +460,7 @@ where
} => {
// for now, treat invoke as a normal call
let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), layout.clone(), pass);
let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass);
self.scan_ast(&stmt);
}

View file

@ -12,8 +12,6 @@ mod helpers;
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
mod gen_num {
//use roc_std::RocOrder;
#[test]
fn i64_values() {
assert_evals_to!("0", 0, i64);

View file

@ -5,6 +5,7 @@ use roc_can::builtins::builtin_defs_map;
use roc_collections::all::MutMap;
use tempfile::tempdir;
#[allow(dead_code)]
fn promote_expr_to_module(src: &str) -> String {
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
@ -18,6 +19,7 @@ fn promote_expr_to_module(src: &str) -> String {
buffer
}
#[allow(dead_code)]
pub fn helper<'a>(
arena: &'a bumpalo::Bump,
src: &str,

View file

@ -120,7 +120,7 @@ fn generate_module_doc<'a>(
(acc, None)
}
Body(_, _) | Nested(_) => (acc, None),
Body(_, _) => (acc, None),
NotYetImplemented(s) => todo!("{}", s),
}

View file

@ -784,6 +784,10 @@ enum Msg<'a> {
},
FailedToParse(ParseProblem<'a, SyntaxError<'a>>),
FailedToReadFile {
filename: PathBuf,
error: io::ErrorKind,
},
}
#[derive(Debug)]
@ -996,18 +1000,16 @@ pub enum LoadingProblem<'a> {
FileProblem {
filename: PathBuf,
error: io::ErrorKind,
msg: &'static str,
},
ParsingFailed(ParseProblem<'a, SyntaxError<'a>>),
UnexpectedHeader(String),
/// there is no platform (likely running an Interface module)
NoPlatform(String),
MsgChannelDied,
ErrJoiningWorkerThreads,
TriedToImportAppModule,
/// a formatted report of parsing failure
ParsingFailedReport(String),
/// a formatted report
FormattedReport(String),
}
pub enum Phases {
@ -1399,6 +1401,14 @@ where
Err(LoadingProblem::ParsingFailed(problem)) => {
msg_tx.send(Msg::FailedToParse(problem)).unwrap();
}
Err(LoadingProblem::FileProblem {
filename,
error,
}) => {
msg_tx
.send(Msg::FailedToReadFile { filename, error })
.unwrap();
}
Err(other) => {
return Err(other);
}
@ -1457,6 +1467,16 @@ where
let worker_listeners = worker_listeners.into_bump_slice();
let msg_tx = msg_tx.clone();
macro_rules! shut_down_worker_threads {
() => {
for listener in worker_listeners {
listener
.send(WorkerMsg::Shutdown)
.map_err(|_| LoadingProblem::MsgChannelDied)?;
}
};
}
// The root module will have already queued up messages to process,
// and processing those messages will in turn queue up more messages.
for msg in msg_rx.iter() {
@ -1490,12 +1510,7 @@ where
// We're done! There should be no more messages pending.
debug_assert!(msg_rx.is_empty());
// Shut down all the worker threads.
for listener in worker_listeners {
listener
.send(WorkerMsg::Shutdown)
.map_err(|_| LoadingProblem::MsgChannelDied)?;
}
shut_down_worker_threads!();
return Ok(LoadResult::Monomorphized(finish_specialization(
state,
@ -1503,50 +1518,29 @@ where
exposed_to_host,
)?));
}
Msg::FailedToReadFile { filename, error } => {
shut_down_worker_threads!();
let buf = to_file_problem_report(&filename, error);
return Err(LoadingProblem::FormattedReport(buf));
}
Msg::FailedToParse(problem) => {
// Shut down all the worker threads.
for listener in worker_listeners {
listener
.send(WorkerMsg::Shutdown)
.map_err(|_| LoadingProblem::MsgChannelDied)?;
}
shut_down_worker_threads!();
use roc_reporting::report::{
parse_problem, RocDocAllocator, DEFAULT_PALETTE,
};
// TODO this is not in fact safe
let src = unsafe { from_utf8_unchecked(problem.bytes) };
let src_lines: Vec<&str> = src.split('\n').collect();
let palette = DEFAULT_PALETTE;
let mut module_ids = Arc::try_unwrap(state.arc_modules)
let module_ids = Arc::try_unwrap(state.arc_modules)
.unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to module_ids")
})
.into_inner()
.into_module_ids();
let module_id =
module_ids.get_or_insert(&"find module name somehow?".into());
let interns = Interns {
let buf = to_parse_problem_report(
problem,
module_ids,
all_ident_ids: state.constrained_ident_ids,
};
// Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
let starting_line = 0;
let report =
parse_problem(&alloc, problem.filename.clone(), starting_line, problem);
let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette);
return Err(LoadingProblem::ParsingFailedReport(buf));
state.constrained_ident_ids,
);
return Err(LoadingProblem::FormattedReport(buf));
}
msg => {
// This is where most of the main thread's work gets done.
@ -1950,7 +1944,7 @@ fn update<'a>(
};
for (layout, pend) in specs {
existing.insert(layout.clone(), pend.clone());
existing.insert(*layout, pend.clone());
}
}
}
@ -2086,6 +2080,9 @@ fn update<'a>(
Msg::FailedToParse(_) => {
unreachable!();
}
Msg::FailedToReadFile { .. } => {
unreachable!();
}
}
}
@ -2142,72 +2139,8 @@ fn finish_specialization(
}
Valid(To::NewPackage(p_or_p)) => p_or_p,
other => {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
let module_id = state.root_id;
let palette = DEFAULT_PALETTE;
// Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&[], module_id, &interns);
let report = {
match other {
Valid(_) => unreachable!(),
NotSpecified => {
let doc = alloc.stack(vec![
alloc.reflow("I could not find a platform based on your input file."),
alloc.reflow(r"Does the module header contain an entry that looks like this:"),
alloc
.parser_suggestion(" packages { base: \"platform\" }")
.indent(4),
alloc.reflow("See also TODO."),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "NO PLATFORM".to_string(),
}
}
RootIsInterface => {
let doc = alloc.stack(vec![
alloc.reflow(r"The input file is a interface file, but only app modules can be ran."),
alloc.concat(vec![
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies,"),
alloc.reflow(r"but won't output any executable."),
])
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "NO PLATFORM".to_string(),
}
}
RootIsPkgConfig => {
let doc = alloc.stack(vec![
alloc.reflow(r"The input file is a package config file, but only app modules can be ran."),
alloc.concat(vec![
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies,"),
alloc.reflow(r"but won't output any executable."),
])
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "NO PLATFORM".to_string(),
}
}
}
};
let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette);
return Err(LoadingProblem::NoPlatform(buf));
let buf = to_missing_platform_report(state.root_id, other);
return Err(LoadingProblem::FormattedReport(buf));
}
};
@ -2332,6 +2265,10 @@ fn load_pkg_config<'a>(
)))
}
Ok((ast::Module::Platform { header }, parser_state)) => {
let delta = bytes.len() - parser_state.bytes.len();
let chomped = &bytes[..delta];
let header_src = unsafe { std::str::from_utf8_unchecked(chomped) };
// make a Pkg-Config module that ultimately exposes `main` to the host
let pkg_config_module_msg = fabricate_pkg_config_module(
arena,
@ -2342,6 +2279,7 @@ fn load_pkg_config<'a>(
module_ids.clone(),
ident_ids_by_module.clone(),
&header,
header_src,
pkg_module_timing,
)
.1;
@ -2360,7 +2298,7 @@ fn load_pkg_config<'a>(
Ok(Msg::Many(vec![effects_module_msg, pkg_config_module_msg]))
}
Err(fail) => Err(LoadingProblem::ParsingFailed(
SyntaxError::Header(fail).into_parse_problem(filename, bytes),
SyntaxError::Header(fail).into_parse_problem(filename, "", bytes),
)),
}
}
@ -2368,7 +2306,6 @@ fn load_pkg_config<'a>(
Err(err) => Err(LoadingProblem::FileProblem {
filename,
error: err.kind(),
msg: "while reading a Pkg-Config.roc file",
}),
}
}
@ -2633,7 +2570,7 @@ fn parse_header<'a>(
module_timing,
)),
Err(fail) => Err(LoadingProblem::ParsingFailed(
SyntaxError::Header(fail).into_parse_problem(filename, src_bytes),
SyntaxError::Header(fail).into_parse_problem(filename, "", src_bytes),
)),
}
}
@ -2670,7 +2607,6 @@ fn load_filename<'a>(
Err(err) => Err(LoadingProblem::FileProblem {
filename,
error: err.kind(),
msg: "in `load_filename`",
}),
}
}
@ -2939,18 +2875,24 @@ fn send_header<'a>(
)
}
// TODO refactor so more logic is shared with `send_header`
#[allow(clippy::too_many_arguments)]
fn send_header_two<'a>(
arena: &'a Bump,
#[derive(Debug)]
struct PlatformHeaderInfo<'a> {
filename: PathBuf,
is_root_module: bool,
shorthand: &'a str,
header_src: &'a str,
app_module_id: ModuleId,
packages: &'a [Located<PackageEntry<'a>>],
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
requires: &'a [Located<TypedIdent<'a>>],
imports: &'a [Located<ImportsEntry<'a>>],
}
// TODO refactor so more logic is shared with `send_header`
#[allow(clippy::too_many_arguments)]
fn send_header_two<'a>(
arena: &'a Bump,
info: PlatformHeaderInfo<'a>,
parse_state: parser::State<'a>,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
@ -2958,6 +2900,18 @@ fn send_header_two<'a>(
) -> (ModuleId, Msg<'a>) {
use inlinable_string::InlinableString;
let PlatformHeaderInfo {
filename,
shorthand,
is_root_module,
header_src,
app_module_id,
packages,
provides,
requires,
imports,
} = info;
let declared_name: InlinableString = "".into();
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
@ -3149,7 +3103,7 @@ fn send_header_two<'a>(
package_qualified_imported_modules,
deps_by_name,
exposes: exposed,
header_src: "#builtin effect header",
header_src,
parse_state,
exposed_imports: scope,
module_timing,
@ -3278,21 +3232,27 @@ fn fabricate_pkg_config_module<'a>(
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
header: &PlatformHeader<'a>,
header_src: &'a str,
module_timing: ModuleTiming,
) -> (ModuleId, Msg<'a>) {
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] =
header.provides.clone().into_bump_slice();
let info = PlatformHeaderInfo {
filename,
is_root_module: false,
shorthand,
header_src,
app_module_id,
packages: &[],
provides,
requires: header.requires.clone().into_bump_slice(),
imports: header.imports.clone().into_bump_slice(),
};
send_header_two(
arena,
filename,
false,
shorthand,
app_module_id,
&[],
provides,
header.requires.clone().into_bump_slice(),
header.imports.clone().into_bump_slice(),
info,
parse_state,
module_ids,
ident_ids_by_module,
@ -3683,9 +3643,11 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
let parsed_defs = match module_defs().parse(&arena, parse_state) {
Ok((_, success, _state)) => success,
Err((_, fail, _)) => {
return Err(LoadingProblem::ParsingFailed(
fail.into_parse_problem(header.module_path, source),
));
return Err(LoadingProblem::ParsingFailed(fail.into_parse_problem(
header.module_path,
header.header_src,
source,
)));
}
};
@ -3811,12 +3773,7 @@ fn make_specializations<'a>(
// TODO: for now this final specialization pass is sequential,
// with no parallelization at all. We should try to parallelize
// this, but doing so will require a redesign of Procs.
procs = roc_mono::ir::specialize_all(
&mut mono_env,
procs,
&mut layout_cache,
// &finished_info.vars_by_symbol,
);
procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut layout_cache);
let external_specializations_requested = procs.externals_we_need.clone();
let procedures = procs.get_specialized_procs_without_rc(mono_env.arena);
@ -4180,3 +4137,179 @@ where
Ok(())
}
fn to_file_problem_report(filename: &Path, error: io::ErrorKind) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
let src_lines: Vec<&str> = Vec::new();
let mut module_ids = ModuleIds::default();
let module_id = module_ids.get_or_insert(&"find module name somehow?".into());
let interns = Interns::default();
// Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
let report = match error {
io::ErrorKind::NotFound => {
let doc = alloc.stack(vec![
alloc.reflow(r"I am looking for this file, but it's not there:"),
alloc
.parser_suggestion(filename.to_str().unwrap())
.indent(4),
alloc.concat(vec![
alloc.reflow(r"Is the file supposed to be there? "),
alloc.reflow("Maybe there is a typo in the file name?"),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "FILE NOT FOUND".to_string(),
}
}
io::ErrorKind::PermissionDenied => {
let doc = alloc.stack(vec![
alloc.reflow(r"I don't have the required permissions to read this file:"),
alloc
.parser_suggestion(filename.to_str().unwrap())
.indent(4),
alloc.concat(vec![
alloc.reflow(r"Is it the right file? Maybe change its permissions?")
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "PERMISSION DENIED".to_string(),
}
}
_ => {
let error = std::io::Error::from(error);
let formatted = format!("{}", error);
let doc = alloc.concat(vec![
alloc.reflow(r"I tried to read this file, but ran into a "),
alloc.text(formatted),
alloc.reflow(r" problem."),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "FILE PROBLEM".to_string(),
}
}
};
let mut buf = String::new();
let palette = DEFAULT_PALETTE;
report.render_color_terminal(&mut buf, &alloc, &palette);
buf
}
fn to_parse_problem_report<'a>(
problem: ParseProblem<'a, SyntaxError<'a>>,
mut module_ids: ModuleIds,
all_ident_ids: MutMap<ModuleId, IdentIds>,
) -> String {
use roc_reporting::report::{parse_problem, RocDocAllocator, DEFAULT_PALETTE};
// TODO this is not in fact safe
let src = unsafe { from_utf8_unchecked(problem.bytes) };
let mut src_lines: Vec<&str> = problem.prefix.lines().collect();
src_lines.extend(src.lines().skip(1));
let module_id = module_ids.get_or_insert(&"find module name somehow?".into());
let interns = Interns {
module_ids,
all_ident_ids,
};
// Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
let starting_line = 0;
let report = parse_problem(&alloc, problem.filename.clone(), starting_line, problem);
let mut buf = String::new();
let palette = DEFAULT_PALETTE;
report.render_color_terminal(&mut buf, &alloc, &palette);
buf
}
fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
use PlatformPath::*;
// Report parsing and canonicalization problems
let interns = Interns::default();
let alloc = RocDocAllocator::new(&[], module_id, &interns);
let report = {
match other {
Valid(_) => unreachable!(),
NotSpecified => {
let doc = alloc.stack(vec![
alloc.reflow("I could not find a platform based on your input file."),
alloc.reflow(r"Does the module header contain an entry that looks like this:"),
alloc
.parser_suggestion(" packages { base: \"platform\" }")
.indent(4),
alloc.reflow("See also TODO."),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "NO PLATFORM".to_string(),
}
}
RootIsInterface => {
let doc = alloc.stack(vec![
alloc.reflow(r"The input file is a interface file, but only app modules can be ran."),
alloc.concat(vec![
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies,"),
alloc.reflow(r"but won't output any executable."),
])
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "NO PLATFORM".to_string(),
}
}
RootIsPkgConfig => {
let doc = alloc.stack(vec![
alloc.reflow(r"The input file is a package config file, but only app modules can be ran."),
alloc.concat(vec![
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies,"),
alloc.reflow(r"but won't output any executable."),
])
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "NO PLATFORM".to_string(),
}
}
}
};
let palette = DEFAULT_PALETTE;
let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette);
buf
}

View file

@ -0,0 +1,8 @@
interface MissingDep
exposes [ unit ]
imports [ ThisFileIsMissing ]
Unit : [ Unit ]
unit : Unit
unit = Unit

View file

@ -32,87 +32,94 @@ mod test_load {
// HELPERS
fn multiple_modules(files: Vec<(&str, &str)>) -> LoadedModule {
multiple_modules_help(files).unwrap()
fn multiple_modules(files: Vec<(&str, &str)>) -> Result<LoadedModule, String> {
use roc_load::file::LoadingProblem;
let arena = Bump::new();
let arena = &arena;
match multiple_modules_help(arena, files) {
Err(io_error) => panic!("IO trouble: {:?}", io_error),
Ok(Err(LoadingProblem::FormattedReport(buf))) => Err(buf),
Ok(Err(loading_problem)) => Err(format!("{:?}", loading_problem)),
Ok(Ok(mut loaded_module)) => {
let home = loaded_module.module_id;
assert_eq!(
loaded_module.can_problems.remove(&home).unwrap_or_default(),
Vec::new()
);
assert_eq!(
loaded_module
.type_problems
.remove(&home)
.unwrap_or_default(),
Vec::new()
);
Ok(loaded_module)
}
}
}
fn multiple_modules_help(mut files: Vec<(&str, &str)>) -> Result<LoadedModule, std::io::Error> {
fn multiple_modules_help<'a>(
arena: &'a Bump,
mut files: Vec<(&str, &str)>,
) -> Result<Result<LoadedModule, roc_load::file::LoadingProblem<'a>>, std::io::Error> {
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
let arena = Bump::new();
let arena = &arena;
let stdlib = roc_builtins::std::standard_stdlib();
let mut file_handles: Vec<_> = Vec::new();
let exposed_types = MutMap::default();
let loaded = {
// create a temporary directory
let dir = tempdir()?;
let app_module = files.pop().unwrap();
let interfaces = files;
// create a temporary directory
let dir = tempdir()?;
debug_assert!(
app_module.1.starts_with("app"),
"The final module should be the application module"
);
let app_module = files.pop().unwrap();
let interfaces = files;
for (name, source) in interfaces {
let mut filename = PathBuf::from(name);
filename.set_extension("roc");
let file_path = dir.path().join(filename.clone());
let mut file = File::create(file_path)?;
writeln!(file, "{}", source)?;
file_handles.push(file);
}
debug_assert!(
app_module.1.starts_with("app"),
"The final module should be the application module"
);
let result = {
let (name, source) = app_module;
for (name, source) in interfaces {
let mut filename = PathBuf::from(name);
filename.set_extension("roc");
let file_path = dir.path().join(filename.clone());
let mut file = File::create(file_path)?;
writeln!(file, "{}", source)?;
file_handles.push(file);
}
let filename = PathBuf::from(name);
let file_path = dir.path().join(filename);
let full_file_path = file_path.clone();
let mut file = File::create(file_path)?;
writeln!(file, "{}", source)?;
file_handles.push(file);
let result = {
let (name, source) = app_module;
roc_load::file::load_and_typecheck(
arena,
full_file_path,
&stdlib,
dir.path(),
exposed_types,
8,
builtin_defs_map,
)
};
let filename = PathBuf::from(name);
let file_path = dir.path().join(filename);
let full_file_path = file_path.clone();
let mut file = File::create(file_path)?;
writeln!(file, "{}", source)?;
file_handles.push(file);
dir.close()?;
result
roc_load::file::load_and_typecheck(
arena,
full_file_path,
arena.alloc(stdlib),
dir.path(),
exposed_types,
8,
builtin_defs_map,
)
};
let mut loaded_module = loaded.expect("failed to load module");
dir.close()?;
let home = loaded_module.module_id;
assert_eq!(
loaded_module.can_problems.remove(&home).unwrap_or_default(),
Vec::new()
);
assert_eq!(
loaded_module
.type_problems
.remove(&home)
.unwrap_or_default(),
Vec::new()
);
Ok(loaded_module)
Ok(result)
}
fn load_fixture(
@ -134,9 +141,9 @@ mod test_load {
);
let mut loaded_module = match loaded {
Ok(x) => x,
Err(roc_load::file::LoadingProblem::ParsingFailedReport(report)) => {
Err(roc_load::file::LoadingProblem::FormattedReport(report)) => {
println!("{}", report);
panic!();
panic!("{}", report);
}
Err(e) => panic!("{:?}", e),
};
@ -285,7 +292,8 @@ mod test_load {
),
),
];
multiple_modules(modules);
assert!(multiple_modules(modules).is_ok());
}
#[test]
@ -517,61 +525,69 @@ mod test_load {
);
}
// #[test]
// fn load_records() {
// use roc::types::{ErrorType, Mismatch, Problem, TypeExt};
#[test]
fn parse_problem() {
let modules = vec![(
"Main",
indoc!(
r#"
app "test-app" packages { blah: "./blah" } provides [ main ] to blah
// let subs_by_module = MutMap::default();
// let loaded_module =
// load_fixture("interface_with_deps", "Records", subs_by_module);
main = [
"#
),
)];
// // NOTE: `a` here is unconstrained, so unifies with <type error>
// let expected_types = hashmap! {
// "Records.intVal" => "a",
// };
match multiple_modules(modules) {
Err(report) => assert_eq!(
report,
indoc!(
"
\u{1b}[36m UNFINISHED LIST \u{1b}[0m
I cannot find the end of this list:
// let a = ErrorType::FlexVar("a".into());
\u{1b}[36m3\u{1b}[0m\u{1b}[36m\u{1b}[0m \u{1b}[37mmain = [\u{1b}[0m
\u{1b}[31m^\u{1b}[0m
// let mut record = SendMap::default();
// record.insert("x".into(), a);
You could change it to something like \u{1b}[33m[ 1, 2, 3 ]\u{1b}[0m or even just \u{1b}[33m[]\u{1b}[0m.
Anything where there is an open and a close square bracket, and where
the elements of the list are separated by commas.
// let problem = Problem::Mismatch(
// Mismatch::TypeMismatch,
// ErrorType::Record(SendMap::default(), TypeExt::Closed),
// ErrorType::Record(record, TypeExt::FlexOpen("b".into())),
// );
\u{1b}[4mNote\u{1b}[0m: I may be confused by indentation"
)
),
Ok(_) => unreachable!("we expect failure here"),
}
}
// assert_eq!(loaded_module.problems, vec![problem]);
// assert_eq!(expected_types.len(), loaded_module.declarations.len());
#[test]
#[should_panic(
expected = "FileProblem { filename: \"tests/fixtures/build/interface_with_deps/invalid$name.roc\", error: NotFound }"
)]
fn file_not_found() {
let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "invalid$name", subs_by_module);
// let mut subs = loaded_module.solved.into_inner();
expect_types(
loaded_module,
hashmap! {
"str" => "Str",
},
);
}
// for decl in loaded_module.declarations {
// let def = match decl {
// Declare(def) => def,
// rec_decl @ DeclareRec(_) => {
// panic!(
// "Unexpected recursive def in module declarations: {:?}",
// rec_decl
// );
// }
// cycle @ InvalidCycle(_, _) => {
// panic!("Unexpected cyclic def in module declarations: {:?}", cycle);
// }
// };
#[test]
#[should_panic(expected = "FILE NOT FOUND")]
fn imported_file_not_found() {
let subs_by_module = MutMap::default();
let loaded_module = load_fixture("no_deps", "MissingDep", subs_by_module);
// for (symbol, expr_var) in def.pattern_vars {
// let content = subs.get(expr_var).content;
// name_all_type_vars(expr_var, &mut subs);
// let actual_str = content_to_string(content, &mut subs);
// let expected_type = expected_types.get(symbol.as_str()).unwrap_or_else(|| {
// panic!("Defs included an unexpected symbol: {:?}", symbol)
// });
// assert_eq!((&symbol, expected_type), (&symbol, &actual_str.as_str()));
// }
// }
// }
expect_types(
loaded_module,
hashmap! {
"str" => "Str",
},
);
}
}

View file

@ -117,21 +117,21 @@ impl From<InlinableString> for ModuleName {
}
}
impl Into<InlinableString> for ModuleName {
fn into(self) -> InlinableString {
self.0
impl From<ModuleName> for InlinableString {
fn from(name: ModuleName) -> Self {
name.0
}
}
impl<'a> Into<&'a InlinableString> for &'a ModuleName {
fn into(self) -> &'a InlinableString {
&self.0
impl<'a> From<&'a ModuleName> for &'a InlinableString {
fn from(name: &'a ModuleName) -> Self {
&name.0
}
}
impl<'a> Into<Box<str>> for ModuleName {
fn into(self) -> Box<str> {
self.0.to_string().into()
impl From<ModuleName> for Box<str> {
fn from(name: ModuleName) -> Self {
name.0.to_string().into()
}
}
@ -197,9 +197,9 @@ impl<'a> From<String> for Lowercase {
}
}
impl Into<InlinableString> for Lowercase {
fn into(self) -> InlinableString {
self.0
impl From<Lowercase> for InlinableString {
fn from(lowercase: Lowercase) -> Self {
lowercase.0
}
}
@ -234,21 +234,21 @@ impl From<InlinableString> for Ident {
}
}
impl Into<InlinableString> for Ident {
fn into(self) -> InlinableString {
self.0
impl From<Ident> for InlinableString {
fn from(ident: Ident) -> Self {
ident.0
}
}
impl<'a> Into<&'a InlinableString> for &'a Ident {
fn into(self) -> &'a InlinableString {
&self.0
impl<'a> From<&'a Ident> for &'a InlinableString {
fn from(ident: &'a Ident) -> Self {
&ident.0
}
}
impl<'a> Into<Box<str>> for Ident {
fn into(self) -> Box<str> {
self.0.to_string().into()
impl From<Ident> for Box<str> {
fn from(ident: Ident) -> Self {
ident.0.to_string().into()
}
}

View file

@ -1,6 +1,6 @@
#![warn(clippy::all, clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
pub mod ident;
pub mod low_level;

View file

@ -34,6 +34,7 @@ pub enum LowLevel {
ListWalk,
ListWalkBackwards,
ListSum,
ListProduct,
ListKeepOks,
ListKeepErrs,
DictSize,
@ -71,6 +72,7 @@ pub enum LowLevel {
NumSin,
NumCos,
NumSqrtUnchecked,
NumLogUnchecked,
NumRound,
NumToFloat,
NumPow,

View file

@ -804,59 +804,60 @@ define_builtins! {
43 NUM_MOD_INT: "modInt"
44 NUM_MOD_FLOAT: "modFloat"
45 NUM_SQRT: "sqrt"
46 NUM_ROUND: "round"
47 NUM_COMPARE: "compare"
48 NUM_POW: "pow"
49 NUM_CEILING: "ceiling"
50 NUM_POW_INT: "powInt"
51 NUM_FLOOR: "floor"
52 NUM_ADD_WRAP: "addWrap"
53 NUM_ADD_CHECKED: "addChecked"
54 NUM_ATAN: "atan"
55 NUM_ACOS: "acos"
56 NUM_ASIN: "asin"
57 NUM_AT_SIGNED128: "@Signed128"
58 NUM_SIGNED128: "Signed128" imported
59 NUM_AT_SIGNED64: "@Signed64"
60 NUM_SIGNED64: "Signed64" imported
61 NUM_AT_SIGNED32: "@Signed32"
62 NUM_SIGNED32: "Signed32" imported
63 NUM_AT_SIGNED16: "@Signed16"
64 NUM_SIGNED16: "Signed16" imported
65 NUM_AT_SIGNED8: "@Signed8"
66 NUM_SIGNED8: "Signed8" imported
67 NUM_AT_UNSIGNED128: "@Unsigned128"
68 NUM_UNSIGNED128: "Unsigned128" imported
69 NUM_AT_UNSIGNED64: "@Unsigned64"
70 NUM_UNSIGNED64: "Unsigned64" imported
71 NUM_AT_UNSIGNED32: "@Unsigned32"
72 NUM_UNSIGNED32: "Unsigned32" imported
73 NUM_AT_UNSIGNED16: "@Unsigned16"
74 NUM_UNSIGNED16: "Unsigned16" imported
75 NUM_AT_UNSIGNED8: "@Unsigned8"
76 NUM_UNSIGNED8: "Unsigned8" imported
77 NUM_AT_BINARY64: "@Binary64"
78 NUM_BINARY64: "Binary64" imported
79 NUM_AT_BINARY32: "@Binary32"
80 NUM_BINARY32: "Binary32" imported
81 NUM_BITWISE_AND: "bitwiseAnd"
82 NUM_BITWISE_XOR: "bitwiseXor"
83 NUM_BITWISE_OR: "bitwiseOr"
84 NUM_SHIFT_LEFT: "shiftLeftBy"
85 NUM_SHIFT_RIGHT: "shiftRightBy"
86 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy"
87 NUM_SUB_WRAP: "subWrap"
88 NUM_SUB_CHECKED: "subChecked"
89 NUM_MUL_WRAP: "mulWrap"
90 NUM_MUL_CHECKED: "mulChecked"
91 NUM_INT: "Int" imported
92 NUM_FLOAT: "Float" imported
93 NUM_AT_NATURAL: "@Natural"
94 NUM_NATURAL: "Natural" imported
95 NUM_NAT: "Nat" imported
96 NUM_INT_CAST: "intCast"
97 NUM_MAX_I128: "maxI128"
98 NUM_IS_MULTIPLE_OF: "isMultipleOf"
46 NUM_LOG: "log"
47 NUM_ROUND: "round"
48 NUM_COMPARE: "compare"
49 NUM_POW: "pow"
50 NUM_CEILING: "ceiling"
51 NUM_POW_INT: "powInt"
52 NUM_FLOOR: "floor"
53 NUM_ADD_WRAP: "addWrap"
54 NUM_ADD_CHECKED: "addChecked"
55 NUM_ATAN: "atan"
56 NUM_ACOS: "acos"
57 NUM_ASIN: "asin"
58 NUM_AT_SIGNED128: "@Signed128"
59 NUM_SIGNED128: "Signed128" imported
60 NUM_AT_SIGNED64: "@Signed64"
61 NUM_SIGNED64: "Signed64" imported
62 NUM_AT_SIGNED32: "@Signed32"
63 NUM_SIGNED32: "Signed32" imported
64 NUM_AT_SIGNED16: "@Signed16"
65 NUM_SIGNED16: "Signed16" imported
66 NUM_AT_SIGNED8: "@Signed8"
67 NUM_SIGNED8: "Signed8" imported
68 NUM_AT_UNSIGNED128: "@Unsigned128"
69 NUM_UNSIGNED128: "Unsigned128" imported
70 NUM_AT_UNSIGNED64: "@Unsigned64"
71 NUM_UNSIGNED64: "Unsigned64" imported
72 NUM_AT_UNSIGNED32: "@Unsigned32"
73 NUM_UNSIGNED32: "Unsigned32" imported
74 NUM_AT_UNSIGNED16: "@Unsigned16"
75 NUM_UNSIGNED16: "Unsigned16" imported
76 NUM_AT_UNSIGNED8: "@Unsigned8"
77 NUM_UNSIGNED8: "Unsigned8" imported
78 NUM_AT_BINARY64: "@Binary64"
79 NUM_BINARY64: "Binary64" imported
80 NUM_AT_BINARY32: "@Binary32"
81 NUM_BINARY32: "Binary32" imported
82 NUM_BITWISE_AND: "bitwiseAnd"
83 NUM_BITWISE_XOR: "bitwiseXor"
84 NUM_BITWISE_OR: "bitwiseOr"
85 NUM_SHIFT_LEFT: "shiftLeftBy"
86 NUM_SHIFT_RIGHT: "shiftRightBy"
87 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy"
88 NUM_SUB_WRAP: "subWrap"
89 NUM_SUB_CHECKED: "subChecked"
90 NUM_MUL_WRAP: "mulWrap"
91 NUM_MUL_CHECKED: "mulChecked"
92 NUM_INT: "Int" imported
93 NUM_FLOAT: "Float" imported
94 NUM_AT_NATURAL: "@Natural"
95 NUM_NATURAL: "Natural" imported
96 NUM_NAT: "Nat" imported
97 NUM_INT_CAST: "intCast"
98 NUM_MAX_I128: "maxI128"
99 NUM_IS_MULTIPLE_OF: "isMultipleOf"
}
2 BOOL: "Bool" => {
@ -913,6 +914,7 @@ define_builtins! {
23 LIST_MAP_WITH_INDEX: "mapWithIndex"
24 LIST_MAP2: "map2"
25 LIST_MAP3: "map3"
26 LIST_PRODUCT: "product"
}
5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -22,7 +22,7 @@ pub fn infer_borrow<'a>(
};
for (key, proc) in procs {
param_map.visit_proc(arena, proc, key.clone());
param_map.visit_proc(arena, proc, *key);
}
let mut env = BorrowInfState {
@ -47,7 +47,7 @@ pub fn infer_borrow<'a>(
// mutually recursive functions (or just make all their arguments owned)
for (key, proc) in procs {
env.collect_proc(proc, key.1.clone());
env.collect_proc(proc, key.1);
}
if !env.modified {
@ -113,7 +113,7 @@ impl<'a> ParamMap<'a> {
Vec::from_iter_in(
ps.iter().map(|p| Param {
borrow: p.layout.is_refcounted(),
layout: p.layout.clone(),
layout: p.layout,
symbol: p.symbol,
}),
arena,
@ -125,7 +125,7 @@ impl<'a> ParamMap<'a> {
Vec::from_iter_in(
ps.iter().map(|(layout, symbol)| Param {
borrow: should_borrow_layout(layout),
layout: layout.clone(),
layout: *layout,
symbol: *symbol,
}),
arena,
@ -140,7 +140,7 @@ impl<'a> ParamMap<'a> {
Vec::from_iter_in(
ps.iter().map(|(layout, symbol)| Param {
borrow: false,
layout: layout.clone(),
layout: *layout,
symbol: *symbol,
}),
arena,
@ -367,7 +367,7 @@ impl<'a> BorrowInfState<'a> {
name, full_layout, ..
} => {
// get the borrow signature of the applied function
match self.param_map.get_symbol(*name, full_layout.clone()) {
match self.param_map.get_symbol(*name, *full_layout) {
Some(ps) => {
// the return value will be owned
self.own_var(z);
@ -499,7 +499,7 @@ impl<'a> BorrowInfState<'a> {
if self.current_proc == *g && x == *z {
// anonymous functions (for which the ps may not be known)
// can never be tail-recursive, so this is fine
if let Some(ps) = self.param_map.get_symbol(*g, full_layout.clone()) {
if let Some(ps) = self.param_map.get_symbol(*g, *full_layout) {
self.own_params_using_args(ys, ps)
}
}
@ -541,14 +541,14 @@ impl<'a> BorrowInfState<'a> {
Let(x, Expr::FunctionPointer(fsymbol, layout), _, b) => {
// ensure that the function pointed to is in the param map
if let Some(params) = self.param_map.get_symbol(*fsymbol, layout.clone()) {
if let Some(params) = self.param_map.get_symbol(*fsymbol, *layout) {
self.param_map
.items
.insert(Key::Declaration(*x, layout.clone()), params);
.insert(Key::Declaration(*x, *layout), params);
}
self.collect_stmt(b);
self.preserve_tail_call(*x, &Expr::FunctionPointer(*fsymbol, layout.clone()), b);
self.preserve_tail_call(*x, &Expr::FunctionPointer(*fsymbol, *layout), b);
}
Let(x, v, _, b) => {
@ -657,7 +657,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
ListWalkBackwards => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
ListSum => arena.alloc_slice_copy(&[borrowed]),
ListSum | ListProduct => arena.alloc_slice_copy(&[borrowed]),
// TODO when we have lists with capacity (if ever)
// List.append should own its first argument
@ -671,10 +671,9 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
| NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy
| NumShiftRightZfBy => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin | NumIntCast => {
arena.alloc_slice_copy(&[irrelevant])
}
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin
| NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),
StrToBytes => arena.alloc_slice_copy(&[owned]),

View file

@ -22,7 +22,7 @@ pub fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> Decision
.into_iter()
.map(|(guard, pattern, index)| Branch {
goal: index,
patterns: vec![(Path::Empty, guard, pattern)],
patterns: vec![(Vec::new(), guard, pattern)],
})
.collect();
@ -52,7 +52,7 @@ impl<'a> Guard<'a> {
pub enum DecisionTree<'a> {
Match(Label),
Decision {
path: Path,
path: Vec<PathInstruction>,
edges: Vec<(Test<'a>, DecisionTree<'a>)>,
default: Option<Box<DecisionTree<'a>>>,
},
@ -132,23 +132,12 @@ impl<'a> Hash for Test<'a> {
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Path {
Index {
index: u64,
tag_id: u8,
path: Box<Path>,
},
Unbox(Box<Path>),
Empty,
}
// ACTUALLY BUILD DECISION TREES
#[derive(Clone, Debug, PartialEq)]
struct Branch<'a> {
goal: Label,
patterns: Vec<(Path, Guard<'a>, Pattern<'a>)>,
patterns: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
}
fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
@ -172,13 +161,13 @@ fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
decision_tree.clone()
}
(_, None) => DecisionTree::Decision {
path,
path: path.clone(),
edges: decision_edges,
default: None,
},
(None, Some(_)) => to_decision_tree(fallback),
_ => DecisionTree::Decision {
path,
path: path.clone(),
edges: decision_edges,
default: Some(Box::new(to_decision_tree(fallback))),
},
@ -218,8 +207,8 @@ fn flatten_patterns(branch: Branch) -> Branch {
}
fn flatten<'a>(
path_pattern: (Path, Guard<'a>, Pattern<'a>),
path_patterns: &mut Vec<(Path, Guard<'a>, Pattern<'a>)>,
path_pattern: (Vec<PathInstruction>, Guard<'a>, Pattern<'a>),
path_patterns: &mut Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
) {
match path_pattern.2 {
Pattern::AppliedTag {
@ -241,8 +230,9 @@ fn flatten<'a>(
// Theory: unbox doesn't have any value for us, because one-element tag unions
// don't store the tag anyway.
if arguments.len() == 1 {
// NOTE here elm will unbox, but we don't use that
path_patterns.push((
Path::Unbox(Box::new(path)),
path,
path_pattern.1.clone(),
Pattern::AppliedTag {
union,
@ -254,13 +244,15 @@ fn flatten<'a>(
));
} else {
for (index, (arg_pattern, _)) in arguments.iter().enumerate() {
let mut new_path = path.clone();
new_path.push(PathInstruction {
index: index as u64,
tag_id,
});
flatten(
(
Path::Index {
index: index as u64,
tag_id,
path: Box::new(path.clone()),
},
new_path,
// same guard here?
path_pattern.1.clone(),
arg_pattern.clone(),
@ -283,7 +275,7 @@ fn flatten<'a>(
/// path. If that is the case we give the resulting label and a mapping from free
/// variables to "how to get their value". So a pattern like (Just (x,_)) will give
/// us something like ("x" => value.0.0)
fn check_for_match(branches: &Vec<Branch>) -> Option<Label> {
fn check_for_match(branches: &[Branch]) -> Option<Label> {
match branches.get(0) {
Some(Branch { goal, patterns })
if patterns
@ -300,7 +292,7 @@ fn check_for_match(branches: &Vec<Branch>) -> Option<Label> {
fn gather_edges<'a>(
branches: Vec<Branch<'a>>,
path: &Path,
path: &[PathInstruction],
) -> (Vec<(Test<'a>, Vec<Branch<'a>>)>, Vec<Branch<'a>>) {
let relevant_tests = tests_at_path(path, &branches);
@ -326,7 +318,7 @@ fn gather_edges<'a>(
/// FIND RELEVANT TESTS
fn tests_at_path<'a>(selected_path: &Path, branches: &[Branch<'a>]) -> Vec<Test<'a>> {
fn tests_at_path<'a>(selected_path: &[PathInstruction], branches: &[Branch<'a>]) -> Vec<Test<'a>> {
// NOTE the ordering of the result is important!
let mut all_tests = Vec::new();
@ -360,7 +352,11 @@ fn tests_at_path<'a>(selected_path: &Path, branches: &[Branch<'a>]) -> Vec<Test<
unique
}
fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut Vec<Test<'a>>) {
fn test_at_path<'a>(
selected_path: &[PathInstruction],
branch: &Branch<'a>,
all_tests: &mut Vec<Test<'a>>,
) {
use Pattern::*;
use Test::*;
@ -413,10 +409,10 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
for destruct in destructs {
match &destruct.typ {
DestructType::Guard(guard) => {
arguments.push((guard.clone(), destruct.layout.clone()));
arguments.push((guard.clone(), destruct.layout));
}
DestructType::Required(_) => {
arguments.push((Pattern::Underscore, destruct.layout.clone()));
arguments.push((Pattern::Underscore, destruct.layout));
}
}
}
@ -469,7 +465,7 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
/// BUILD EDGES
fn edges_for<'a>(
path: &Path,
path: &[PathInstruction],
branches: Vec<Branch<'a>>,
test: Test<'a>,
) -> (Test<'a>, Vec<Branch<'a>>) {
@ -484,7 +480,7 @@ fn edges_for<'a>(
fn to_relevant_branch<'a>(
test: &Test<'a>,
path: &Path,
path: &[PathInstruction],
branch: &Branch<'a>,
new_branches: &mut Vec<Branch<'a>>,
) {
@ -524,9 +520,9 @@ fn to_relevant_branch<'a>(
fn to_relevant_branch_help<'a>(
test: &Test<'a>,
path: &Path,
mut start: Vec<(Path, Guard<'a>, Pattern<'a>)>,
end: Vec<(Path, Guard<'a>, Pattern<'a>)>,
path: &[PathInstruction],
mut start: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
end: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
branch: &Branch<'a>,
guard: Guard<'a>,
pattern: Pattern<'a>,
@ -550,15 +546,13 @@ fn to_relevant_branch_help<'a>(
DestructType::Required(_) => Pattern::Underscore,
};
(
Path::Index {
index: index as u64,
tag_id: *tag_id,
path: Box::new(path.clone()),
},
Guard::NoGuard,
pattern,
)
let mut new_path = path.to_vec();
new_path.push(PathInstruction {
index: index as u64,
tag_id: *tag_id,
});
(new_path, Guard::NoGuard, pattern)
});
start.extend(sub_positions);
start.extend(end);
@ -597,26 +591,21 @@ fn to_relevant_branch_help<'a>(
debug_assert_eq!(arguments.len(), 1);
let arg = arguments[0].clone();
{
start.push((
Path::Unbox(Box::new(path.clone())),
guard,
arg.0,
));
// NOTE here elm unboxes, but we ignore that
// Path::Unbox(Box::new(path.clone()))
start.push((path.to_vec(), guard, arg.0));
start.extend(end);
}
}
Wrapped::RecordOrSingleTagUnion => {
let sub_positions = arguments.into_iter().enumerate().map(
|(index, (pattern, _))| {
(
Path::Index {
index: index as u64,
tag_id,
path: Box::new(path.clone()),
},
Guard::NoGuard,
pattern,
)
let mut new_path = path.to_vec();
new_path.push(PathInstruction {
index: index as u64,
tag_id,
});
(new_path, Guard::NoGuard, pattern)
},
);
start.extend(sub_positions);
@ -625,15 +614,12 @@ fn to_relevant_branch_help<'a>(
Wrapped::MultiTagUnion => {
let sub_positions = arguments.into_iter().enumerate().map(
|(index, (pattern, _))| {
(
Path::Index {
index: 1 + index as u64,
tag_id,
path: Box::new(path.clone()),
},
Guard::NoGuard,
pattern,
)
let mut new_path = path.to_vec();
new_path.push(PathInstruction {
index: 1 + index as u64,
tag_id,
});
(new_path, Guard::NoGuard, pattern)
},
);
start.extend(sub_positions);
@ -715,22 +701,22 @@ fn to_relevant_branch_help<'a>(
enum Extract<'a> {
NotFound,
Found {
start: Vec<(Path, Guard<'a>, Pattern<'a>)>,
start: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
found_pattern: (Guard<'a>, Pattern<'a>),
end: Vec<(Path, Guard<'a>, Pattern<'a>)>,
end: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
},
}
fn extract<'a>(
selected_path: &Path,
path_patterns: Vec<(Path, Guard<'a>, Pattern<'a>)>,
selected_path: &[PathInstruction],
path_patterns: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
) -> Extract<'a> {
let mut start = Vec::new();
// TODO potential ordering problem
let mut it = path_patterns.into_iter();
while let Some(current) = it.next() {
if &current.0 == selected_path {
if current.0 == selected_path {
return Extract::Found {
start,
found_pattern: (current.1, current.2),
@ -746,7 +732,7 @@ fn extract<'a>(
/// FIND IRRELEVANT BRANCHES
fn is_irrelevant_to<'a>(selected_path: &Path, branch: &Branch<'a>) -> bool {
fn is_irrelevant_to<'a>(selected_path: &[PathInstruction], branch: &Branch<'a>) -> bool {
match branch
.patterns
.iter()
@ -775,7 +761,7 @@ fn needs_tests(pattern: &Pattern) -> bool {
/// PICK A PATH
fn pick_path<'a>(branches: &'a [Branch]) -> &'a Path {
fn pick_path<'a>(branches: &'a [Branch]) -> &'a Vec<PathInstruction> {
let mut all_paths = Vec::with_capacity(branches.len());
// is choice path
@ -804,9 +790,12 @@ fn pick_path<'a>(branches: &'a [Branch]) -> &'a Path {
}
}
fn bests_by_small_branching_factor<'a, I>(branches: &[Branch], mut all_paths: I) -> Vec<&'a Path>
fn bests_by_small_branching_factor<'a, I>(
branches: &[Branch],
mut all_paths: I,
) -> Vec<&'a Vec<PathInstruction>>
where
I: Iterator<Item = &'a Path>,
I: Iterator<Item = &'a Vec<PathInstruction>>,
{
match all_paths.next() {
None => panic!("Cannot choose the best of zero paths. This should never happen."),
@ -836,9 +825,12 @@ where
}
}
fn bests_by_small_defaults<'a, I>(branches: &[Branch], mut all_paths: I) -> Vec<&'a Path>
fn bests_by_small_defaults<'a, I>(
branches: &[Branch],
mut all_paths: I,
) -> Vec<&'a Vec<PathInstruction>>
where
I: Iterator<Item = &'a Path>,
I: Iterator<Item = &'a Vec<PathInstruction>>,
{
match all_paths.next() {
None => panic!("Cannot choose the best of zero paths. This should never happen."),
@ -870,7 +862,7 @@ where
/// PATH PICKING HEURISTICS
fn small_defaults(branches: &[Branch], path: &Path) -> usize {
fn small_defaults(branches: &[Branch], path: &[PathInstruction]) -> usize {
branches
.iter()
.filter(|b| is_irrelevant_to(path, b))
@ -878,8 +870,7 @@ fn small_defaults(branches: &[Branch], path: &Path) -> usize {
.sum()
}
fn small_branching_factor(branches: &[Branch], path: &Path) -> usize {
// TODO remove clone
fn small_branching_factor(branches: &[Branch], path: &[PathInstruction]) -> usize {
let (edges, fallback) = gather_edges(branches.to_vec(), path);
edges.len() + (if fallback.is_empty() { 0 } else { 1 })
@ -889,12 +880,12 @@ fn small_branching_factor(branches: &[Branch], path: &Path) -> usize {
enum Decider<'a, T> {
Leaf(T),
Chain {
test_chain: Vec<(Path, Test<'a>)>,
test_chain: Vec<(Vec<PathInstruction>, Test<'a>)>,
success: Box<Decider<'a, T>>,
failure: Box<Decider<'a, T>>,
},
FanOut {
path: Path,
path: Vec<PathInstruction>,
tests: Vec<(Test<'a>, Decider<'a, T>)>,
fallback: Box<Decider<'a, T>>,
},
@ -973,49 +964,22 @@ pub fn optimize_when<'a>(
stmt
}
#[derive(Debug)]
struct PathInstruction {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PathInstruction {
index: u64,
tag_id: u8,
}
fn reverse_path(mut path: &Path) -> Vec<PathInstruction> {
let mut result = Vec::new();
loop {
match path {
Path::Unbox(nested) => {
path = nested;
}
Path::Empty => break,
Path::Index {
index,
tag_id,
path: nested,
} => {
result.push(PathInstruction {
index: *index,
tag_id: *tag_id,
});
path = nested;
}
}
}
result.reverse();
result
}
fn path_to_expr_help<'a>(
env: &mut Env<'a, '_>,
mut symbol: Symbol,
path: &Path,
path: &[PathInstruction],
mut layout: Layout<'a>,
) -> (Symbol, StoresVec<'a>, Layout<'a>) {
let mut stores = bumpalo::collections::Vec::new_in(env.arena);
let instructions = reverse_path(path);
// let instructions = reverse_path(path);
let instructions = path;
let mut it = instructions.iter().peekable();
while let Some(PathInstruction { index, tag_id }) = it.next() {
@ -1027,13 +991,12 @@ fn path_to_expr_help<'a>(
debug_assert_eq!(*tag_id, 0);
debug_assert!(it.peek().is_none());
let field_layouts = vec![layout.clone()];
let field_layouts = vec![layout];
debug_assert!(*index < field_layouts.len() as u64);
debug_assert_eq!(field_layouts.len(), 1);
let inner_layout = field_layouts[*index as usize].clone();
let inner_expr = Expr::AccessAtIndex {
index: *index,
field_layouts: env.arena.alloc(field_layouts),
@ -1042,7 +1005,8 @@ fn path_to_expr_help<'a>(
};
symbol = env.unique_symbol();
stores.push((symbol, inner_layout.clone(), inner_expr));
let inner_layout = layout;
stores.push((symbol, inner_layout, inner_expr));
break;
}
@ -1096,7 +1060,7 @@ fn path_to_expr_help<'a>(
}
Layout::Struct(layouts) => layouts,
other => env.arena.alloc([other.clone()]),
other => env.arena.alloc([*other]),
};
debug_assert!(
@ -1109,8 +1073,8 @@ fn path_to_expr_help<'a>(
);
let inner_layout = match &field_layouts[index as usize] {
Layout::RecursivePointer => layout.clone(),
other => other.clone(),
Layout::RecursivePointer => layout,
other => *other,
};
let inner_expr = Expr::AccessAtIndex {
@ -1121,7 +1085,7 @@ fn path_to_expr_help<'a>(
};
symbol = env.unique_symbol();
stores.push((symbol, inner_layout.clone(), inner_expr));
stores.push((symbol, inner_layout, inner_expr));
layout = inner_layout;
}
@ -1135,11 +1099,11 @@ fn test_to_equality<'a>(
env: &mut Env<'a, '_>,
cond_symbol: Symbol,
cond_layout: &Layout<'a>,
path: &Path,
path: &[PathInstruction],
test: Test<'a>,
) -> (StoresVec<'a>, Symbol, Symbol, Layout<'a>) {
let (rhs_symbol, mut stores, _layout) =
path_to_expr_help(env, cond_symbol, &path, cond_layout.clone());
path_to_expr_help(env, cond_symbol, &path, *cond_layout);
match test {
Test::IsCtor {
@ -1273,7 +1237,7 @@ fn stores_and_condition<'a>(
env: &mut Env<'a, '_>,
cond_symbol: Symbol,
cond_layout: &Layout<'a>,
test_chain: Vec<(Path, Test<'a>)>,
test_chain: Vec<(Vec<PathInstruction>, Test<'a>)>,
) -> (Tests<'a>, Option<(Symbol, JoinPointId, Stmt<'a>)>) {
let mut tests = Vec::with_capacity(test_chain.len());
@ -1399,12 +1363,12 @@ fn compile_test_help<'a>(
} => {
let pass_info = BranchInfo::Constructor {
scrutinee,
layout: layout.clone(),
layout,
tag_id: pass,
};
let fail_info = BranchInfo::Constructor {
scrutinee,
layout: layout.clone(),
layout,
tag_id: fail,
};
@ -1418,7 +1382,7 @@ fn compile_test_help<'a>(
} => {
let pass_info = BranchInfo::Constructor {
scrutinee,
layout: layout.clone(),
layout,
tag_id,
};
@ -1473,11 +1437,11 @@ fn compile_tests<'a>(
// the guard is the final thing that we check, so needs to be layered on first!
if let Some((_, id, stmt)) = opt_guard {
cond = compile_guard(env, ret_layout.clone(), id, arena.alloc(stmt), fail, cond);
cond = compile_guard(env, ret_layout, id, arena.alloc(stmt), fail, cond);
}
for (new_stores, lhs, rhs, _layout) in tests.into_iter() {
cond = compile_test(env, ret_layout.clone(), new_stores, lhs, rhs, fail, cond);
cond = compile_test(env, ret_layout, new_stores, lhs, rhs, fail, cond);
}
cond
}
@ -1501,22 +1465,22 @@ impl<'a> ConstructorKnown<'a> {
fn from_test_chain(
cond_symbol: Symbol,
cond_layout: &Layout<'a>,
test_chain: &[(Path, Test)],
test_chain: &[(Vec<PathInstruction>, Test)],
) -> Self {
match test_chain {
[(path, test)] => match (path, test) {
(Path::Empty, Test::IsCtor { tag_id, union, .. }) => {
[(path, test)] => match test {
Test::IsCtor { tag_id, union, .. } if path.is_empty() => {
if union.alternatives.len() == 2 {
// excluded middle: we also know the tag_id in the fail branch
ConstructorKnown::Both {
layout: cond_layout.clone(),
layout: *cond_layout,
scrutinee: cond_symbol,
pass: *tag_id,
fail: (*tag_id == 0) as u8,
}
} else {
ConstructorKnown::OnlyPass {
layout: cond_layout.clone(),
layout: *cond_layout,
scrutinee: cond_symbol,
tag_id: *tag_id,
}
@ -1541,7 +1505,7 @@ fn decide_to_branching<'a>(
cond_layout: Layout<'a>,
ret_layout: Layout<'a>,
decider: Decider<'a, Choice<'a>>,
jumps: &Vec<(u64, JoinPointId, Stmt<'a>)>,
jumps: &[(u64, JoinPointId, Stmt<'a>)],
) -> Stmt<'a> {
use Choice::*;
use Decider::*;
@ -1569,8 +1533,8 @@ fn decide_to_branching<'a>(
procs,
layout_cache,
cond_symbol,
cond_layout.clone(),
ret_layout.clone(),
cond_layout,
ret_layout,
*success,
jumps,
);
@ -1580,8 +1544,8 @@ fn decide_to_branching<'a>(
procs,
layout_cache,
cond_symbol,
cond_layout.clone(),
ret_layout.clone(),
cond_layout,
ret_layout,
*failure,
jumps,
);
@ -1608,7 +1572,7 @@ fn decide_to_branching<'a>(
compile_test_help(
env,
chain_branch_info,
ret_layout.clone(),
ret_layout,
new_stores,
lhs,
rhs,
@ -1643,15 +1607,15 @@ fn decide_to_branching<'a>(
// switch on the tag discriminant (currently an i64 value)
// NOTE the tag discriminant is not actually loaded, `cond` can point to a tag
let (inner_cond_symbol, cond_stores_vec, inner_cond_layout) =
path_to_expr_help(env, cond_symbol, &path, cond_layout.clone());
path_to_expr_help(env, cond_symbol, &path, cond_layout);
let default_branch = decide_to_branching(
env,
procs,
layout_cache,
cond_symbol,
cond_layout.clone(),
ret_layout.clone(),
cond_layout,
ret_layout,
*fallback,
jumps,
);
@ -1667,8 +1631,8 @@ fn decide_to_branching<'a>(
procs,
layout_cache,
cond_symbol,
cond_layout.clone(),
ret_layout.clone(),
cond_layout,
ret_layout,
decider,
jumps,
);
@ -1689,7 +1653,7 @@ fn decide_to_branching<'a>(
BranchInfo::Constructor {
scrutinee: inner_cond_symbol,
layout: inner_cond_layout.clone(),
layout: inner_cond_layout,
tag_id,
}
} else {
@ -1704,7 +1668,7 @@ fn decide_to_branching<'a>(
let default_branch_info = if tag_id_sum > 0 && union_size > 0 {
BranchInfo::Constructor {
scrutinee: inner_cond_symbol,
layout: inner_cond_layout.clone(),
layout: inner_cond_layout,
tag_id: tag_id_sum as u8,
}
} else {
@ -1847,7 +1811,7 @@ fn tree_to_decider(tree: DecisionTree) -> Decider<u64> {
}
fn to_chain<'a>(
path: Path,
path: Vec<PathInstruction>,
test: Test<'a>,
success_tree: DecisionTree<'a>,
failure_tree: DecisionTree<'a>,

View file

@ -141,7 +141,7 @@ impl<'a, 'i> Env<'a, 'i> {
tag_id,
} => {
self.constructor_map.insert(*scrutinee, *tag_id as u64);
self.layout_map.insert(*scrutinee, layout.clone());
self.layout_map.insert(*scrutinee, *layout);
}
BranchInfo::None => (),
}
@ -167,7 +167,7 @@ impl<'a, 'i> Env<'a, 'i> {
}
Closure(arguments, closure_layout, result) => {
let fpointer = Layout::FunctionPointer(arguments, result);
let fields = self.arena.alloc([fpointer, closure_layout.layout.clone()]);
let fields = self.arena.alloc([fpointer, *closure_layout.layout]);
self.constructor_map.insert(symbol, 0);
self.layout_map.insert(symbol, Layout::Struct(fields));
}
@ -247,7 +247,7 @@ fn layout_for_constructor<'a>(
}
Closure(arguments, closure_layout, result) => {
let fpointer = Layout::FunctionPointer(arguments, result);
let fields = arena.alloc([fpointer, closure_layout.layout.clone()]);
let fields = arena.alloc([fpointer, *closure_layout.layout]);
HasFields(fields)
}
other => unreachable!("weird layout {:?}", other),
@ -315,9 +315,9 @@ fn work_for_constructor<'a>(
let alias_symbol = Env::manual_unique_symbol(env.home, env.ident_ids);
let layout = if let Layout::RecursivePointer = field_layout {
full_layout.clone()
*full_layout
} else {
field_layout.clone()
*field_layout
};
env.deferred.assignments.push((alias_symbol, expr, layout));
@ -375,7 +375,7 @@ pub fn expand_and_cancel_proc<'a>(
}
Layout::Closure(arguments, closure_layout, result) => {
let fpointer = Layout::FunctionPointer(arguments, result);
let fields = env.arena.alloc([fpointer, closure_layout.layout.clone()]);
let fields = env.arena.alloc([fpointer, *closure_layout.layout]);
env.insert_struct_info(*symbol, fields);
introduced.push(*symbol);
}
@ -412,7 +412,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
let mut result = {
match stmt {
Let(mut symbol, expr, layout, cont) => {
env.layout_map.insert(symbol, layout.clone());
env.layout_map.insert(symbol, *layout);
let mut expr = expr;
let mut layout = layout;
@ -423,7 +423,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
while !matches!(&expr, Expr::AccessAtIndex { .. } | Expr::Struct(_)) {
if let Stmt::Let(symbol1, expr1, layout1, cont1) = cont {
literal_stack.push((symbol, expr.clone(), layout.clone()));
literal_stack.push((symbol, expr.clone(), *layout));
symbol = *symbol1;
expr = expr1;
@ -479,7 +479,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
}
}
let stmt = Let(symbol, expr.clone(), layout.clone(), new_cont);
let stmt = Let(symbol, expr.clone(), *layout, new_cont);
let mut stmt = &*env.arena.alloc(stmt);
for (symbol, expr, layout) in literal_stack.into_iter().rev() {
@ -519,8 +519,8 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
let stmt = Switch {
cond_symbol: *cond_symbol,
cond_layout: cond_layout.clone(),
ret_layout: ret_layout.clone(),
cond_layout: *cond_layout,
ret_layout: *ret_layout,
branches: new_branches.into_bump_slice(),
default_branch: new_default,
};
@ -579,7 +579,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
let stmt = Invoke {
symbol: *symbol,
call: call.clone(),
layout: layout.clone(),
layout: *layout,
pass,
fail,
};

View file

@ -470,7 +470,7 @@ impl<'a> Context<'a> {
name, full_layout, ..
} => {
// get the borrow signature
match self.param_map.get_symbol(*name, full_layout.clone()) {
match self.param_map.get_symbol(*name, *full_layout) {
Some(ps) => {
let v = Expr::Call(crate::ir::Call {
call_type,
@ -695,7 +695,7 @@ impl<'a> Context<'a> {
let pair = ctx.visit_variable_declaration(
*symbol,
(*expr).clone(),
(*layout).clone(),
*layout,
b,
&b_live_vars,
);
@ -712,13 +712,7 @@ impl<'a> Context<'a> {
Let(symbol, expr, layout, cont) => {
let ctx = self.update_var_info(*symbol, layout, expr);
let (b, b_live_vars) = ctx.visit_stmt(cont);
ctx.visit_variable_declaration(
*symbol,
expr.clone(),
layout.clone(),
b,
&b_live_vars,
)
ctx.visit_variable_declaration(*symbol, expr.clone(), *layout, b, &b_live_vars)
}
Invoke {
@ -759,7 +753,7 @@ impl<'a> Context<'a> {
call: call.clone(),
pass,
fail,
layout: layout.clone(),
layout: *layout,
};
let cont = self.arena.alloc(invoke);
@ -783,7 +777,7 @@ impl<'a> Context<'a> {
name, full_layout, ..
} => {
// get the borrow signature
match self.param_map.get_symbol(*name, full_layout.clone()) {
match self.param_map.get_symbol(*name, *full_layout) {
Some(ps) => self.add_dec_after_application(
call.arguments,
ps,
@ -908,8 +902,8 @@ impl<'a> Context<'a> {
cond_symbol: *cond_symbol,
branches,
default_branch,
cond_layout: cond_layout.clone(),
ret_layout: ret_layout.clone(),
cond_layout: *cond_layout,
ret_layout: *ret_layout,
});
(switch, case_live_vars)

File diff suppressed because it is too large Load diff

View file

@ -23,7 +23,7 @@ pub enum LayoutProblem {
}
/// Types for code gen must be monomorphic. No type variables allowed!
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Layout<'a> {
Builtin(Builtin<'a>),
/// A layout that is empty (turns into the empty struct in LLVM IR
@ -39,7 +39,7 @@ pub enum Layout<'a> {
Pointer(&'a Layout<'a>),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum UnionLayout<'a> {
/// A non-recursive tag union
/// e.g. `Result a e : [ Ok a, Err e ]`
@ -93,7 +93,7 @@ impl<'a> UnionLayout<'a> {
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ClosureLayout<'a> {
/// the layout that this specific closure captures
/// uses a Vec instead of a MutMap because it's Hash
@ -258,7 +258,7 @@ impl<'a> ClosureLayout<'a> {
// define the function pointer
let function_ptr_layout = {
let mut temp = Vec::from_iter_in(argument_layouts.iter().cloned(), arena);
temp.push(closure_data_layout.clone());
temp.push(*closure_data_layout);
Layout::FunctionPointer(temp.into_bump_slice(), ret_layout)
};
@ -278,8 +278,8 @@ impl<'a> ClosureLayout<'a> {
pub fn as_named_layout(&self, symbol: Symbol) -> Layout<'a> {
let layouts = if self.captured.is_empty() {
match self.layout {
Layout::Struct(fields) if fields.len() == 1 => fields[0].clone(),
other => other.clone(),
Layout::Struct(fields) if fields.len() == 1 => fields[0],
other => *other,
}
} else if let Some((_, tag_args)) = self
.captured
@ -287,7 +287,7 @@ impl<'a> ClosureLayout<'a> {
.find(|(tn, _)| *tn == TagName::Closure(symbol))
{
if tag_args.len() == 1 {
tag_args[0].clone()
tag_args[0]
} else {
Layout::Struct(tag_args)
}
@ -303,13 +303,13 @@ impl<'a> ClosureLayout<'a> {
pub fn as_block_of_memory_layout(&self) -> Layout<'a> {
match self.layout {
Layout::Struct(fields) if fields.len() == 1 => fields[0].clone(),
other => other.clone(),
Layout::Struct(fields) if fields.len() == 1 => fields[0],
other => *other,
}
}
pub fn internal_layout(&self) -> Layout<'a> {
self.layout.clone()
*self.layout
}
pub fn build_closure_data(
@ -382,7 +382,7 @@ pub enum MemoryMode {
Refcounted,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Builtin<'a> {
Int128,
Int64,
@ -825,7 +825,7 @@ impl<'a> LayoutCache<'a> {
// of a problem
if false {
let cached_layout = match &result {
Ok(layout) => Cached(layout.clone()),
Ok(layout) => Cached(*layout),
Err(problem) => Problem(problem.clone()),
};
@ -2057,7 +2057,7 @@ impl<'a> LayoutIds<'a> {
// If we had to default to next_id, it must not have been found;
// store the ID we're going to return and increment next_id.
if answer == ids.next_id {
ids.by_id.insert(layout.clone(), ids.next_id);
ids.by_id.insert(*layout, ids.next_id);
ids.next_id += 1;
}

View file

@ -1,6 +1,6 @@
#![warn(clippy::all, clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
pub mod borrow;
pub mod expand_rc;
@ -13,7 +13,6 @@ pub mod tail_recursion;
// For now, following this warning's advice will lead to nasty type inference errors.
//#[allow(clippy::ptr_arg)]
//pub mod decision_tree;
#[allow(clippy::ptr_arg)]
pub mod decision_tree;
#[allow(clippy::ptr_arg)]
pub mod exhaustive;

View file

@ -42,7 +42,7 @@ pub fn make_tail_recursive<'a>(
let params = Vec::from_iter_in(
args.iter().map(|(layout, symbol)| Param {
symbol: *symbol,
layout: layout.clone(),
layout: *layout,
borrow: true,
}),
arena,
@ -117,7 +117,7 @@ fn insert_jumps<'a>(
if opt_cont.is_some() {
let cont = opt_cont.unwrap_or(cont);
Some(arena.alloc(Let(*symbol, expr.clone(), layout.clone(), cont)))
Some(arena.alloc(Let(*symbol, expr.clone(), *layout, cont)))
} else {
None
}
@ -140,7 +140,7 @@ fn insert_jumps<'a>(
let stmt = Invoke {
symbol: *symbol,
call: call.clone(),
layout: layout.clone(),
layout: *layout,
pass,
fail,
};
@ -222,10 +222,10 @@ fn insert_jumps<'a>(
Some(arena.alloc(Switch {
cond_symbol: *cond_symbol,
cond_layout: cond_layout.clone(),
cond_layout: *cond_layout,
default_branch,
branches,
ret_layout: ret_layout.clone(),
ret_layout: *ret_layout,
}))
} else {
None

View file

@ -69,7 +69,7 @@ mod test_mono {
let mut loaded = match loaded {
Ok(x) => x,
Err(roc_load::file::LoadingProblem::ParsingFailedReport(report)) => {
Err(roc_load::file::LoadingProblem::FormattedReport(report)) => {
println!("{}", report);
panic!();
}
@ -341,13 +341,13 @@ mod test_mono {
"#,
indoc!(
r#"
procedure Num.46 (#Attr.2):
procedure Num.47 (#Attr.2):
let Test.3 = lowlevel NumRound #Attr.2;
ret Test.3;
procedure Test.0 ():
let Test.2 = 3.6f64;
let Test.1 = CallByName Num.46 Test.2;
let Test.1 = CallByName Num.47 Test.2;
ret Test.1;
"#
),

View file

@ -12,20 +12,20 @@ pub enum Module<'a> {
Platform { header: PlatformHeader<'a> },
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WhenBranch<'a> {
pub patterns: &'a [Loc<Pattern<'a>>],
pub value: Loc<Expr<'a>>,
pub guard: Option<Loc<Expr<'a>>>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WhenPattern<'a> {
pub pattern: Loc<Pattern<'a>>,
pub guard: Option<Loc<Expr<'a>>>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StrSegment<'a> {
Plaintext(&'a str), // e.g. "foo"
Unicode(Loc<&'a str>), // e.g. "00A0" in "\u(00A0)"
@ -33,7 +33,7 @@ pub enum StrSegment<'a> {
Interpolated(Loc<&'a Expr<'a>>), // e.g. (name) in "Hi, \(name)!"
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum EscapedChar {
Newline, // \n
Tab, // \t
@ -57,7 +57,7 @@ impl EscapedChar {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StrLiteral<'a> {
/// The most common case: a plain string with no escapes or interpolations
PlainLine(&'a str),
@ -74,7 +74,7 @@ pub enum StrLiteral<'a> {
/// we move on to canonicalization, which often needs to allocate more because
/// it's doing things like turning local variables into fully qualified symbols.
/// Once canonicalization is done, the arena and the input string get dropped.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Expr<'a> {
// Number Literals
Float(&'a str),
@ -151,10 +151,6 @@ pub enum Expr<'a> {
SpaceAfter(&'a Expr<'a>, &'a [CommentOrNewline<'a>]),
ParensAround(&'a Expr<'a>),
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
/// It lets us take an (&Expr) and create a plain (Expr) from it.
Nested(&'a Expr<'a>),
// Problems
MalformedIdent(&'a str, crate::ident::BadIdent),
MalformedClosure,
@ -163,7 +159,7 @@ pub enum Expr<'a> {
PrecedenceConflict(&'a PrecedenceConflict<'a>),
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PrecedenceConflict<'a> {
pub whole_region: Region,
pub binop1_position: Position,
@ -173,7 +169,7 @@ pub struct PrecedenceConflict<'a> {
pub expr: &'a Loc<Expr<'a>>,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Def<'a> {
// TODO in canonicalization, validate the pattern; only certain patterns
// are allowed in annotations.
@ -208,14 +204,10 @@ pub enum Def<'a> {
SpaceBefore(&'a Def<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a Def<'a>, &'a [CommentOrNewline<'a>]),
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
/// It lets us take a (&Def) and create a plain (Def) from it.
Nested(&'a Def<'a>),
NotYetImplemented(&'static str),
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TypeAnnotation<'a> {
/// A function. The types of its arguments, then the type of its return value.
Function(&'a [Loc<TypeAnnotation<'a>>], &'a Loc<TypeAnnotation<'a>>),
@ -261,7 +253,7 @@ pub enum TypeAnnotation<'a> {
Malformed(&'a str),
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Tag<'a> {
Global {
name: Loc<&'a str>,
@ -281,7 +273,7 @@ pub enum Tag<'a> {
Malformed(&'a str),
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AssignedField<'a, Val> {
// A required field with a label, e.g. `{ name: "blah" }` or `{ name : Str }`
RequiredValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Val>),
@ -303,7 +295,7 @@ pub enum AssignedField<'a, Val> {
Malformed(&'a str),
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CommentOrNewline<'a> {
Newline,
LineComment(&'a str),
@ -330,7 +322,7 @@ impl<'a> CommentOrNewline<'a> {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Pattern<'a> {
// Identifier
Identifier(&'a str),
@ -351,10 +343,6 @@ pub enum Pattern<'a> {
/// Can only occur inside of a RecordDestructure
OptionalField(&'a str, &'a Loc<Expr<'a>>),
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
/// It lets us take an (&Expr) and create a plain (Expr) from it.
Nested(&'a Pattern<'a>),
// Literal
NumLiteral(&'a str),
NonBase10Literal {
@ -464,8 +452,6 @@ impl<'a> Pattern<'a> {
// { x, y ? False } = rec
x == y
}
(Nested(x), Nested(y)) => x.equivalent(y),
// Literal
(NumLiteral(x), NumLiteral(y)) => x == y,
(

View file

@ -125,7 +125,7 @@ where
E: 'a,
{
move |_, state: State<'a>| {
if state.column > min_indent {
if state.column >= min_indent {
Ok((NoProgress, (), state))
} else {
Err((NoProgress, indent_problem(state.line, state.column), state))

View file

@ -16,16 +16,33 @@ use roc_region::all::{Located, Position, Region};
use crate::parser::Progress::{self, *};
fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|_arena, state: State<'a>| {
if state.has_reached_end() {
Ok((NoProgress, (), state))
} else {
Err((
NoProgress,
EExpr::BadExprEnd(state.line, state.column),
state,
))
}
}
}
pub fn test_parse_expr<'a>(
min_indent: u16,
arena: &'a bumpalo::Bump,
state: State<'a>,
) -> Result<Located<Expr<'a>>, EExpr<'a>> {
let parser = space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s),
min_indent,
EExpr::Space,
EExpr::IndentStart,
let parser = skip_second!(
space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s),
min_indent,
EExpr::Space,
EExpr::IndentStart,
),
expr_end()
);
match parser.parse(arena, state) {
@ -35,9 +52,27 @@ pub fn test_parse_expr<'a>(
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MultiBackpassing {
Allow,
Disallow,
pub struct ExprParseOptions {
/// Check for and accept multi-backpassing syntax
/// This is usually true, but false within list/record literals
/// because the comma separating backpassing arguments conflicts
/// with the comma separating literal elements
accept_multi_backpassing: bool,
/// Check for the `->` token, and raise an error if found
/// This is usually true, but false in if-guards
///
/// > Just foo if foo == 2 -> ...
check_for_arrow: bool,
}
impl Default for ExprParseOptions {
fn default() -> Self {
ExprParseOptions {
accept_multi_backpassing: true,
check_for_arrow: true,
}
}
}
pub fn expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
@ -159,7 +194,7 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> {
fn parse_loc_term<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
@ -167,10 +202,7 @@ fn parse_loc_term<'a>(
loc_expr_in_parens_etc_help(min_indent),
loc!(specialize(EExpr::Str, string_literal_help())),
loc!(specialize(EExpr::Number, positive_number_literal_help())),
loc!(specialize(
EExpr::Lambda,
closure_help(min_indent, multi_backpassing)
)),
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
loc!(record_literal_help(min_indent)),
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
loc!(map_with_arena!(
@ -183,17 +215,14 @@ fn parse_loc_term<'a>(
fn loc_possibly_negative_or_negated_term<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
one_of![
|arena, state: State<'a>| {
let initial = state;
let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term(
min_indent,
multi_backpassing,
a,
s
min_indent, options, a, s
))
.parse(arena, state)?;
@ -205,7 +234,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
loc!(specialize(EExpr::Number, number_literal_help())),
loc!(map_with_arena!(
and!(loc!(word1(b'!', EExpr::Start)), |a, s| {
parse_loc_term(min_indent, multi_backpassing, a, s)
parse_loc_term(min_indent, options, a, s)
}),
|arena: &'a Bump, (loc_op, loc_expr): (Located<_>, _)| {
Expr::UnaryOp(
@ -216,7 +245,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
)),
|arena, state| {
// TODO use parse_loc_term_better
parse_loc_term(min_indent, multi_backpassing, arena, state)
parse_loc_term(min_indent, options, arena, state)
}
]
}
@ -257,25 +286,19 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
fn parse_expr_start<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
start: Position,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
one_of![
loc!(specialize(
EExpr::If,
if_expr_help(min_indent, multi_backpassing)
)),
loc!(specialize(EExpr::If, if_expr_help(min_indent, options))),
loc!(specialize(
EExpr::When,
when::expr_help(min_indent, multi_backpassing)
when::expr_help(min_indent, options)
)),
loc!(specialize(
EExpr::Lambda,
closure_help(min_indent, multi_backpassing)
)),
loc!(move |a, s| parse_expr_operator_chain(min_indent, multi_backpassing, start, a, s)),
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
loc!(move |a, s| parse_expr_operator_chain(min_indent, options, start, a, s)),
fail_expr_start_e()
]
.parse(arena, state)
@ -283,13 +306,13 @@ fn parse_expr_start<'a>(
fn parse_expr_operator_chain<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
start: Position,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let (_, expr, state) =
loc_possibly_negative_or_negated_term(min_indent, multi_backpassing).parse(arena, state)?;
loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state)?;
let initial = state;
let end = state.get_position();
@ -306,14 +329,7 @@ fn parse_expr_operator_chain<'a>(
end,
};
parse_expr_end(
min_indent,
multi_backpassing,
start,
expr_state,
arena,
state,
)
parse_expr_end(min_indent, options, start, expr_state, arena, state)
}
}
}
@ -685,7 +701,7 @@ struct DefState<'a> {
}
fn parse_defs_end<'a>(
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
start: Position,
mut def_state: DefState<'a>,
arena: &'a Bump,
@ -740,7 +756,7 @@ fn parse_defs_end<'a>(
loc_def_expr,
);
parse_defs_end(multi_backpassing, start, def_state, arena, state)
parse_defs_end(options, start, def_state, arena, state)
}
Ok((_, BinOp::HasType, state)) => {
let (_, ann_type, state) = specialize(
@ -762,7 +778,7 @@ fn parse_defs_end<'a>(
ann_type,
);
parse_defs_end(multi_backpassing, start, def_state, arena, state)
parse_defs_end(options, start, def_state, arena, state)
}
_ => Ok((MadeProgress, def_state, initial)),
@ -771,7 +787,7 @@ fn parse_defs_end<'a>(
}
fn parse_defs_expr<'a>(
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
start: Position,
def_state: DefState<'a>,
arena: &'a Bump,
@ -779,7 +795,7 @@ fn parse_defs_expr<'a>(
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let min_indent = start.col;
match parse_defs_end(multi_backpassing, start, def_state, arena, state) {
match parse_defs_end(options, start, def_state, arena, state) {
Err(bad) => Err(bad),
Ok((_, def_state, state)) => {
// this is no def, because there is no `=` or `:`; parse as an expr
@ -812,7 +828,7 @@ fn parse_defs_expr<'a>(
fn parse_expr_operator<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
start: Position,
mut expr_state: ExprState<'a>,
loc_op: Located<BinOp>,
@ -832,8 +848,7 @@ fn parse_expr_operator<'a>(
BinOp::Minus if expr_state.end != op_start && op_end == new_start => {
// negative terms
let (_, negated_expr, state) =
parse_loc_term(min_indent, multi_backpassing, arena, state)?;
let (_, negated_expr, state) = parse_loc_term(min_indent, options, arena, state)?;
let new_end = state.get_position();
let arg = numeric_negate_expression(
@ -856,14 +871,7 @@ fn parse_expr_operator<'a>(
expr_state.spaces_after = spaces;
expr_state.end = new_end;
parse_expr_end(
min_indent,
multi_backpassing,
start,
expr_state,
arena,
state,
)
parse_expr_end(min_indent, options, start, expr_state, arena, state)
}
BinOp::Assignment => {
let expr_region = expr_state.expr.region;
@ -912,7 +920,7 @@ fn parse_expr_operator<'a>(
spaces_after: &[],
};
parse_defs_expr(multi_backpassing, start, def_state, arena, state)
parse_defs_expr(options, start, def_state, arena, state)
}
BinOp::Backpassing => {
let expr_region = expr_state.expr.region;
@ -1066,11 +1074,9 @@ fn parse_expr_operator<'a>(
spaces_after: &[],
};
parse_defs_expr(multi_backpassing, start, def_state, arena, state)
parse_defs_expr(options, start, def_state, arena, state)
}
_ => match loc_possibly_negative_or_negated_term(min_indent, multi_backpassing)
.parse(arena, state)
{
_ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) {
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
Ok((_, mut new_expr, state)) => {
let new_end = state.get_position();
@ -1108,14 +1114,7 @@ fn parse_expr_operator<'a>(
expr_state.spaces_after = spaces;
// TODO new start?
parse_expr_end(
min_indent,
multi_backpassing,
start,
expr_state,
arena,
state,
)
parse_expr_end(min_indent, options, start, expr_state, arena, state)
}
}
}
@ -1128,7 +1127,7 @@ fn parse_expr_operator<'a>(
fn parse_expr_end<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
start: Position,
mut expr_state: ExprState<'a>,
arena: &'a Bump,
@ -1136,7 +1135,7 @@ fn parse_expr_end<'a>(
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let parser = skip_first!(
crate::blankspace::check_indent(min_indent, EExpr::IndentEnd),
move |a, s| parse_loc_term(min_indent, multi_backpassing, a, s)
move |a, s| parse_loc_term(min_indent, options, a, s)
);
match parser.parse(arena, state) {
@ -1167,14 +1166,7 @@ fn parse_expr_end<'a>(
expr_state.end = new_end;
expr_state.spaces_after = new_spaces;
parse_expr_end(
min_indent,
multi_backpassing,
start,
expr_state,
arena,
state,
)
parse_expr_end(min_indent, options, start, expr_state, arena, state)
}
}
}
@ -1187,19 +1179,12 @@ fn parse_expr_end<'a>(
expr_state.consume_spaces(arena);
expr_state.initial = before_op;
parse_expr_operator(
min_indent,
multi_backpassing,
start,
expr_state,
loc_op,
arena,
state,
min_indent, options, start, expr_state, loc_op, arena, state,
)
}
Err((NoProgress, _, mut state)) => {
// try multi-backpassing
if multi_backpassing == MultiBackpassing::Allow && state.bytes.starts_with(b",")
{
if options.accept_multi_backpassing && state.bytes.starts_with(b",") {
state.bytes = &state.bytes[1..];
state.column += 1;
@ -1260,6 +1245,12 @@ fn parse_expr_end<'a>(
Ok((MadeProgress, ret, state))
}
}
} else if options.check_for_arrow && state.bytes.starts_with(b"->") {
Err((
MadeProgress,
EExpr::BadOperator(&[b'-', b'>'], state.line, state.column),
state,
))
} else {
// roll back space parsing
let state = expr_state.initial;
@ -1277,7 +1268,15 @@ fn parse_loc_expr<'a>(
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
parse_loc_expr_with_options(min_indent, MultiBackpassing::Allow, arena, state)
parse_loc_expr_with_options(
min_indent,
ExprParseOptions {
accept_multi_backpassing: true,
..Default::default()
},
arena,
state,
)
}
pub fn parse_loc_expr_no_multi_backpassing<'a>(
@ -1285,17 +1284,25 @@ pub fn parse_loc_expr_no_multi_backpassing<'a>(
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
parse_loc_expr_with_options(min_indent, MultiBackpassing::Disallow, arena, state)
parse_loc_expr_with_options(
min_indent,
ExprParseOptions {
accept_multi_backpassing: false,
..Default::default()
},
arena,
state,
)
}
fn parse_loc_expr_with_options<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
let start = state.get_position();
parse_expr_start(min_indent, multi_backpassing, start, arena, state)
parse_expr_start(min_indent, options, start, arena, state)
}
/// If the given Expr would parse the same way as a valid Pattern, convert it.
@ -1339,9 +1346,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
spaces,
)),
Expr::ParensAround(sub_expr) | Expr::Nested(sub_expr) => {
expr_to_pattern_help(arena, sub_expr)
}
Expr::ParensAround(sub_expr) => expr_to_pattern_help(arena, sub_expr),
Expr::Record {
fields,
@ -1385,7 +1390,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::RecordUpdate { .. }
| Expr::UnaryOp(_, _) => Err(()),
Expr::Str(string) => Ok(Pattern::StrLiteral(string.clone())),
Expr::Str(string) => Ok(Pattern::StrLiteral(*string)),
Expr::MalformedIdent(string, _problem) => Ok(Pattern::Malformed(string)),
}
}
@ -1414,7 +1419,7 @@ fn assigned_expr_field_to_pattern_help<'a>(
AssignedField::OptionalValue(name, spaces, value) => {
let result = arena.alloc(Located {
region: value.region,
value: value.value.clone(),
value: value.value,
});
if spaces.is_empty() {
Pattern::OptionalField(name.value, result)
@ -1449,8 +1454,13 @@ pub fn defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, E
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
let start = state.get_position();
let (_, def_state, state) =
parse_defs_end(MultiBackpassing::Disallow, start, def_state, arena, state)?;
let options = ExprParseOptions {
accept_multi_backpassing: false,
check_for_arrow: true,
};
let (_, def_state, state) = parse_defs_end(options, start, def_state, arena, state)?;
let (_, final_space, state) =
space0_e(start.col, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
@ -1462,7 +1472,7 @@ pub fn defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, E
let last = def_state.defs.len() - 1;
for (i, ref_def) in def_state.defs.into_iter().enumerate() {
let mut def = ref_def.clone();
let mut def = *ref_def;
if i == first {
def = arena
@ -1488,7 +1498,7 @@ pub fn defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, E
fn closure_help<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
map_with_arena!(
skip_first!(
@ -1516,7 +1526,7 @@ fn closure_help<'a>(
// Parse the body
space0_before_e(
specialize_ref(ELambda::Body, move |arena, state| {
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
parse_loc_expr_with_options(min_indent, options, arena, state)
}),
min_indent,
ELambda::Space,
@ -1541,7 +1551,7 @@ mod when {
/// Parser for when expressions.
pub fn expr_help<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
) -> impl Parser<'a, Expr<'a>, When<'a>> {
then(
and!(
@ -1549,7 +1559,7 @@ mod when {
skip_second!(
space0_around_ee(
specialize_ref(When::Condition, move |arena, state| {
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
parse_loc_expr_with_options(min_indent, options, arena, state)
}),
min_indent,
When::Space,
@ -1572,7 +1582,7 @@ mod when {
// Everything in the branches must be indented at least as much as the case itself.
let min_indent = case_indent;
let (p1, branches, state) = branches(min_indent).parse(arena, state)?;
let (p1, branches, state) = branches(min_indent, options).parse(arena, state)?;
Ok((
progress.or(p1),
@ -1592,22 +1602,27 @@ mod when {
}
}
fn branches<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, When<'a>> {
move |arena, state| {
fn branches<'a>(
min_indent: u16,
options: ExprParseOptions,
) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, When<'a>> {
move |arena, state: State<'a>| {
let when_indent = state.indent_col;
let mut branches: Vec<'a, &'a WhenBranch<'a>> = Vec::with_capacity_in(2, arena);
// 1. Parse the first branch and get its indentation level. (It must be >= min_indent.)
// 2. Parse the other branches. Their indentation levels must be == the first branch's.
let (_, (loc_first_patterns, loc_first_guard), state) =
branch_alternatives(min_indent).parse(arena, state)?;
let loc_first_pattern = loc_first_patterns.first().unwrap();
let original_indent = loc_first_pattern.region.start_col;
let indented_more = original_indent + 1;
let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), mut state) =
branch_alternatives(min_indent, options, None).parse(arena, state)?;
let original_indent = pattern_indent_level;
state.indent_col = pattern_indent_level;
// Parse the first "->" and the expression after it.
let (_, loc_first_expr, mut state) =
branch_result(indented_more).parse(arena, state)?;
branch_result(original_indent + 1).parse(arena, state)?;
// Record this as the first branch, then optionally parse additional branches.
branches.push(arena.alloc(WhenBranch {
@ -1619,19 +1634,21 @@ mod when {
let branch_parser = map!(
and!(
then(
branch_alternatives(min_indent),
move |_arena, state, _, (loc_patterns, loc_guard)| {
match alternatives_indented_correctly(&loc_patterns, original_indent) {
Ok(()) => Ok((MadeProgress, (loc_patterns, loc_guard), state)),
Err(indent) => Err((
branch_alternatives(min_indent, options, Some(pattern_indent_level)),
move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| {
if pattern_indent_level == indent_col {
Ok((MadeProgress, (loc_patterns, loc_guard), state))
} else {
let indent = pattern_indent_level - indent_col;
Err((
MadeProgress,
When::PatternAlignment(indent, state.line, state.column),
state,
)),
))
}
},
),
branch_result(indented_more)
branch_result(original_indent + 1)
),
|((patterns, guard), expr)| {
let patterns: Vec<'a, _> = patterns;
@ -1661,40 +1678,36 @@ mod when {
}
}
Ok((MadeProgress, branches, state))
Ok((
MadeProgress,
branches,
State {
indent_col: when_indent,
..state
},
))
}
}
/// Parsing alternative patterns in when branches.
fn branch_alternatives<'a>(
min_indent: u16,
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>), When<'a>> {
options: ExprParseOptions,
pattern_indent_level: Option<u16>,
) -> impl Parser<
'a,
(
(Col, Vec<'a, Located<Pattern<'a>>>),
Option<Located<Expr<'a>>>,
),
When<'a>,
> {
let options = ExprParseOptions {
check_for_arrow: false,
..options
};
and!(
sep_by1(word1(b'|', When::Bar), |arena, state| {
let (_, spaces, state) =
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
.parse(arena, state)?;
let (_, loc_pattern, state) = space0_after_e(
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
min_indent,
When::Space,
When::IndentPattern,
)
.parse(arena, state)?;
Ok((
MadeProgress,
if spaces.is_empty() {
loc_pattern
} else {
arena
.alloc(loc_pattern.value)
.with_spaces_before(spaces, loc_pattern.region)
},
state,
))
}),
branch_alternatives_help(min_indent, pattern_indent_level),
one_of![
map!(
skip_first!(
@ -1702,7 +1715,7 @@ mod when {
// TODO we should require space before the expression but not after
space0_around_ee(
specialize_ref(When::IfGuard, move |arena, state| {
parse_loc_expr(min_indent, arena, state)
parse_loc_expr_with_options(min_indent + 1, options, arena, state)
}),
min_indent,
When::Space,
@ -1717,22 +1730,103 @@ mod when {
)
}
/// Check if alternatives of a when branch are indented correctly.
fn alternatives_indented_correctly<'a>(
loc_patterns: &'a Vec<'a, Located<Pattern<'a>>>,
original_indent: u16,
) -> Result<(), u16> {
let (first, rest) = loc_patterns.split_first().unwrap();
let first_indented_correctly = first.region.start_col == original_indent;
if first_indented_correctly {
for when_pattern in rest.iter() {
if when_pattern.region.start_col < original_indent {
return Err(original_indent - when_pattern.region.start_col);
fn branch_single_alternative<'a>(
min_indent: u16,
) -> impl Parser<'a, Located<Pattern<'a>>, When<'a>> {
move |arena, state| {
let (_, spaces, state) =
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
.parse(arena, state)?;
let (_, loc_pattern, state) = space0_after_e(
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
min_indent,
When::Space,
When::IndentPattern,
)
.parse(arena, state)?;
Ok((
MadeProgress,
if spaces.is_empty() {
loc_pattern
} else {
arena
.alloc(loc_pattern.value)
.with_spaces_before(spaces, loc_pattern.region)
},
state,
))
}
}
fn branch_alternatives_help<'a>(
min_indent: u16,
pattern_indent_level: Option<u16>,
) -> impl Parser<'a, (Col, Vec<'a, Located<Pattern<'a>>>), When<'a>> {
move |arena, state: State<'a>| {
let initial = state;
// put no restrictions on the indent after the spaces; we'll check it manually
match space0_e(0, When::Space, When::IndentPattern).parse(arena, state) {
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
Ok((_progress, spaces, state)) => {
match pattern_indent_level {
Some(wanted) if state.column > wanted => {
// this branch is indented too much
Err((
NoProgress,
When::IndentPattern(state.line, state.column),
initial,
))
}
Some(wanted) if state.column < wanted => {
let indent = wanted - state.column;
Err((
NoProgress,
When::PatternAlignment(indent, state.line, state.column),
initial,
))
}
_ => {
let pattern_indent =
min_indent.max(pattern_indent_level.unwrap_or(min_indent));
// the region is not reliable for the indent col in the case of
// parentheses around patterns
let pattern_indent_col = state.column;
let parser = sep_by1(
word1(b'|', When::Bar),
branch_single_alternative(pattern_indent + 1),
);
match parser.parse(arena, state) {
Err((MadeProgress, fail, state)) => {
Err((MadeProgress, fail, state))
}
Err((NoProgress, fail, _)) => {
// roll back space parsing if the pattern made no progress
Err((NoProgress, fail, initial))
}
Ok((_, mut loc_patterns, state)) => {
// tag spaces onto the first parsed pattern
if !spaces.is_empty() {
if let Some(first) = loc_patterns.get_mut(0) {
*first = arena
.alloc(first.value)
.with_spaces_before(spaces, first.region);
}
}
Ok((MadeProgress, (pattern_indent_col, loc_patterns), state))
}
}
}
}
}
}
Ok(())
} else {
Err(original_indent - first.region.start_col)
}
}
@ -1795,7 +1889,7 @@ fn if_branch<'a>(
fn if_expr_help<'a>(
min_indent: u16,
multi_backpassing: MultiBackpassing,
options: ExprParseOptions,
) -> impl Parser<'a, Expr<'a>, If<'a>> {
move |arena: &'a Bump, state| {
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, state)?;
@ -1827,7 +1921,7 @@ fn if_expr_help<'a>(
let (_, else_branch, state) = space0_before_e(
specialize_ref(If::ElseBranch, move |arena, state| {
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
parse_loc_expr_with_options(min_indent, options, arena, state)
}),
min_indent,
If::Space,

View file

@ -42,15 +42,15 @@ pub enum PackageOrPath<'a> {
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ModuleName<'a>(&'a str);
impl<'a> Into<&'a str> for ModuleName<'a> {
fn into(self) -> &'a str {
self.0
impl<'a> From<ModuleName<'a>> for &'a str {
fn from(name: ModuleName<'a>) -> Self {
name.0
}
}
impl<'a> Into<InlinableString> for ModuleName<'a> {
fn into(self) -> InlinableString {
self.0.into()
impl<'a> From<ModuleName<'a>> for InlinableString {
fn from(name: ModuleName<'a>) -> InlinableString {
name.0.into()
}
}

View file

@ -185,7 +185,7 @@ pub enum SyntaxError<'a> {
ReservedKeyword(Region),
ArgumentsBeforeEquals(Region),
NotYetImplemented(String),
TODO,
Todo,
Type(Type<'a>),
Pattern(EPattern<'a>),
Expr(EExpr<'a>),
@ -360,6 +360,7 @@ impl<'a> SyntaxError<'a> {
pub fn into_parse_problem(
self,
filename: std::path::PathBuf,
prefix: &'a str,
bytes: &'a [u8],
) -> ParseProblem<'a, SyntaxError<'a>> {
ParseProblem {
@ -368,6 +369,7 @@ impl<'a> SyntaxError<'a> {
problem: self,
filename,
bytes,
prefix,
}
}
}
@ -379,6 +381,7 @@ pub type Col = u16;
pub enum EExpr<'a> {
Start(Row, Col),
End(Row, Col),
BadExprEnd(Row, Col),
Space(BadInputError, Row, Col),
Dot(Row, Col),
@ -679,6 +682,8 @@ pub struct ParseProblem<'a, T> {
pub problem: T,
pub filename: std::path::PathBuf,
pub bytes: &'a [u8],
/// prefix is usually the header (for parse problems in the body), or empty
pub prefix: &'a str,
}
pub trait Parser<'a, Output, Error> {
@ -926,8 +931,8 @@ where
state = next_state;
buf.push(next_output);
}
Err((element_progress, fail, state)) => {
return Err((element_progress, fail, state));
Err((_, fail, state)) => {
return Err((MadeProgress, fail, state));
}
}
}

View file

@ -62,8 +62,8 @@ pub fn loc_pattern_help<'a>(
EPattern::Record,
crate::pattern::record_pattern_help(min_indent)
)),
loc!(number_pattern_help()),
loc!(string_pattern_help()),
loc!(number_pattern_help())
)
}

View file

@ -2550,6 +2550,247 @@ mod test_parse {
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_with_negative_numbers() {
let arena = Bump::new();
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
let expr1 = Num("2");
let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
let branch1 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
});
let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(NumLiteral("-3")), newlines);
let loc_pattern2 = Located::new(2, 2, 1, 3, pattern2);
let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 7, 8, expr2);
let branch2 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern2]),
value: loc_expr2,
guard: None,
});
let branches = &[branch1, branch2];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 5, 6, var);
let expected = Expr::When(arena.alloc(loc_cond), branches);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
when x is
1 -> 2
-3 -> 4
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_with_function_application() {
let arena = Bump::new();
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
let num_2 = Num("2");
let num_neg = Located::new(
1,
1,
9,
16,
Expr::Var {
module_name: "Num",
ident: "neg",
},
);
let expr0 = Located::new(2, 2, 5, 6, Expr::SpaceBefore(&num_2, &[Newline]));
let expr1 = Expr::Apply(
&num_neg,
&*arena.alloc([&*arena.alloc(expr0)]),
CalledVia::Space,
);
let loc_expr1 = Located::new(1, 2, 9, 6, expr1);
let branch1 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
});
let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
let loc_pattern2 = Located::new(3, 3, 4, 5, pattern2);
let expr2 = Num("4");
let loc_expr2 = Located::new(3, 3, 9, 10, expr2);
let branch2 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern2]),
value: loc_expr2,
guard: None,
});
let branches = &[branch1, branch2];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 5, 6, var);
let expected = Expr::When(arena.alloc(loc_cond), branches);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
when x is
1 -> Num.neg
2
_ -> 4
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_if_guard() {
let arena = Bump::new();
let branch1 = {
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
let num_1 = Num("1");
let expr1 = Located::new(
2,
2,
8,
9,
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branch2 = {
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), &[Newline, Newline]);
let loc_pattern1 = Located::new(4, 4, 4, 5, pattern1);
let num_1 = Num("2");
let expr1 = Located::new(
5,
5,
8,
9,
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branch3 = {
let pattern1 =
Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), &[Newline, Newline]);
let loc_pattern1 = Located::new(7, 7, 4, 6, pattern1);
let num_1 = Num("3");
let expr1 = Located::new(
8,
8,
8,
9,
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branches = &[branch1, branch2, branch3];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 5, 6, var);
let expected = Expr::When(arena.alloc(loc_cond), branches);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
when x is
_ ->
1
_ ->
2
Ok ->
3
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_in_parens() {
let arena = Bump::new();
let branch1 = {
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), newlines);
let loc_pattern1 = Located::new(1, 1, 4, 6, pattern1);
let num_1 = Num("3");
let expr1 = Located::new(
2,
2,
8,
9,
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branches = &[branch1];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 6, 7, var);
let when = Expr::When(arena.alloc(loc_cond), branches);
let expected = Expr::ParensAround(&when);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
(when x is
Ok ->
3)
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_with_records() {
let arena = Bump::new();
@ -2599,6 +2840,47 @@ mod test_parse {
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_in_parens_indented() {
let arena = Bump::new();
let branch1 = {
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), newlines);
let loc_pattern1 = Located::new(1, 1, 4, 6, pattern1);
let num_1 = Num("3");
let expr1 = Located::new(1, 1, 10, 11, num_1);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branches = &[branch1];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 6, 7, var);
let when = Expr::When(arena.alloc(loc_cond), branches);
let spaced = Expr::SpaceAfter(&when, &[Newline]);
let expected = Expr::ParensAround(&spaced);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
(when x is
Ok -> 3
)
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_with_alternative_patterns() {
let arena = Bump::new();
@ -2620,9 +2902,9 @@ mod test_parse {
let pattern2_alt =
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt);
let loc_pattern2_alt = Located::new(3, 3, 2, 7, pattern2_alt);
let expr2 = Num("2");
let loc_expr2 = Located::new(3, 3, 10, 11, expr2);
let loc_expr2 = Located::new(3, 3, 11, 12, expr2);
let branch2 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
value: loc_expr2,
@ -2642,7 +2924,7 @@ mod test_parse {
when x is
"blah" | "blop" -> 1
"foo" |
"bar" -> 2
"bar" -> 2
"#
),
);

View file

@ -134,6 +134,7 @@ pub enum RuntimeError {
},
InvalidPrecedence(PrecedenceProblem, Region),
MalformedIdentifier(Box<str>, roc_parse::ident::BadIdent, Region),
MalformedTypeName(Box<str>, Region),
MalformedClosure(Region),
InvalidRecordUpdate {
region: Region,

View file

@ -776,8 +776,20 @@ fn pretty_runtime_error<'b>(
}
RuntimeError::MalformedIdentifier(_box_str, bad_ident, surroundings) => {
to_bad_ident_expr_report(alloc, bad_ident, surroundings)
}
RuntimeError::MalformedTypeName(_box_str, surroundings) => {
alloc.stack(vec![
alloc.reflow(r"I am confused by this type name:"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow("Type names start with an uppercase letter, "),
alloc.reflow("and can optionally be qualified by a module name, like "),
alloc.parser_suggestion("Bool"),
alloc.reflow(" or "),
alloc.parser_suggestion("Http.Request.Request"),
alloc.reflow("."),
]),
])
}
RuntimeError::MalformedClosure(_) => todo!(""),
RuntimeError::InvalidFloat(sign @ FloatErrorKind::PositiveInfinity, region, _raw_str)

View file

@ -272,22 +272,31 @@ fn to_expr_report<'a>(
])
.indent(4),
])],
b"->" => vec![alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("The arrow "),
alloc.parser_suggestion("->"),
alloc.reflow(" is only used to define cases in a "),
alloc.keyword("when"),
alloc.reflow("."),
]),
alloc
.vcat(vec![
alloc.text("when color is"),
alloc.text("Red -> \"stop!\"").indent(4),
alloc.text("Green -> \"go!\"").indent(4),
])
.indent(4),
])],
b"->" => match context {
Context::InNode(Node::WhenBranch, _row, _col, _) => {
return to_unexpected_arrow_report(
alloc, filename, *row, *col, start_row, start_col,
);
}
_ => {
vec![alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("The arrow "),
alloc.parser_suggestion("->"),
alloc.reflow(" is only used to define cases in a "),
alloc.keyword("when"),
alloc.reflow("."),
]),
alloc
.vcat(vec![
alloc.text("when color is"),
alloc.text("Red -> \"stop!\"").indent(4),
alloc.text("Green -> \"go!\"").indent(4),
])
.indent(4),
])]
}
},
b"!" => vec![
alloc.reflow("The boolean negation operator "),
alloc.parser_suggestion("!"),
@ -458,6 +467,27 @@ fn to_expr_report<'a>(
*col,
),
EExpr::BadExprEnd(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
let doc = alloc.stack(vec![
alloc.reflow(r"I got stuck here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("Whatever I am running into is confusing me a lot! "),
alloc.reflow("Normally I can give fairly specific hints, "),
alloc.reflow("but something is really tripping me up this time."),
]),
]);
Report {
filename,
doc,
title: "SYNTAX PROBLEM".to_string(),
}
}
EExpr::Colon(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
@ -982,7 +1012,7 @@ fn to_list_report<'a>(
),
List::Open(row, col) | List::End(row, col) => {
match dbg!(what_is_next(alloc.src_lines, row, col)) {
match what_is_next(alloc.src_lines, row, col) {
Next::Other(Some(',')) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
@ -1387,25 +1417,68 @@ fn to_unfinished_when_report<'a>(
start_row: Row,
start_col: Col,
message: RocDocBuilder<'a>,
) -> Report<'a> {
match what_is_next(alloc.src_lines, row, col) {
Next::Token("->") => {
to_unexpected_arrow_report(alloc, filename, row, col, start_row, start_col)
}
_ => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
alloc.concat(vec![
alloc.reflow(r"I was partway through parsing a "),
alloc.keyword("when"),
alloc.reflow(r" expression, but I got stuck here:"),
]),
alloc.region_with_subregion(surroundings, region),
message,
note_for_when_error(alloc),
]);
Report {
filename,
doc,
title: "UNFINISHED WHEN".to_string(),
}
}
}
}
fn to_unexpected_arrow_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
row: Row,
col: Col,
start_row: Row,
start_col: Col,
) -> Report<'a> {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
let region = Region::from_rows_cols(row, col, row, col + 2);
let doc = alloc.stack(vec![
alloc.concat(vec![
alloc.reflow(r"I was partway through parsing a "),
alloc.reflow(r"I am parsing a "),
alloc.keyword("when"),
alloc.reflow(r" expression, but I got stuck here:"),
alloc.reflow(r" expression right now, but this arrow is confusing me:"),
]),
alloc.region_with_subregion(surroundings, region),
message,
alloc.concat(vec![
alloc.reflow(r"It makes sense to see arrows around here, "),
alloc.reflow(r"so I suspect it is something earlier."),
alloc.reflow(
r"Maybe this pattern is indented a bit farther from the previous patterns?",
),
]),
note_for_when_error(alloc),
]);
Report {
filename,
doc,
title: "UNFINISHED WHEN".to_string(),
title: "UNEXPECTED ARROW".to_string(),
}
}

View file

@ -129,7 +129,7 @@ mod test_reporting {
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
let problem = fail.into_parse_problem(filename.clone(), src.as_bytes());
let problem = fail.into_parse_problem(filename.clone(), "", src.as_bytes());
let doc = parse_problem(&alloc, filename, 0, problem);
callback(doc.pretty(&alloc).append(alloc.line()), buf)
@ -190,8 +190,11 @@ mod test_reporting {
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
use roc_parse::parser::SyntaxError;
let problem =
SyntaxError::Header(fail).into_parse_problem(filename.clone(), src.as_bytes());
let problem = SyntaxError::Header(fail).into_parse_problem(
filename.clone(),
"",
src.as_bytes(),
);
let doc = parse_problem(&alloc, filename, 0, problem);
callback(doc.pretty(&alloc).append(alloc.line()), buf)
@ -4483,7 +4486,7 @@ mod test_reporting {
^
Tab characters are not allowed.
"
"
),
)
}
@ -4547,7 +4550,19 @@ mod test_reporting {
f
"#
),
indoc!(r#""#),
indoc!(
r#"
SYNTAX PROBLEM
I am confused by this type name:
1 f : Foo..Bar
^^^^^^^^
Type names start with an uppercase letter, and can optionally be
qualified by a module name, like Bool or Http.Request.Request.
"#
),
)
// ── DOUBLE DOT ──────────────────────────────────────────────────────────────────
@ -4570,7 +4585,19 @@ mod test_reporting {
f
"#
),
indoc!(r#""#),
indoc!(
r#"
SYNTAX PROBLEM
I am confused by this type name:
1 f : Foo.Bar.
^^^^^^^^
Type names start with an uppercase letter, and can optionally be
qualified by a module name, like Bool or Http.Request.Request.
"#
),
)
// ── TRAILING DOT ────────────────────────────────────────────────────────────────
@ -4618,7 +4645,19 @@ mod test_reporting {
f
"#
),
indoc!(r#""#),
indoc!(
r#"
SYNTAX PROBLEM
I am confused by this type name:
1 f : Foo.1
^^^^^
Type names start with an uppercase letter, and can optionally be
qualified by a module name, like Bool or Http.Request.Request.
"#
),
)
// ── WEIRD QUALIFIED NAME ────────────────────────────────────────────────────────
@ -4642,7 +4681,19 @@ mod test_reporting {
f
"#
),
indoc!(r#""#),
indoc!(
r#"
SYNTAX PROBLEM
I am confused by this type name:
1 f : Foo.foo
^^^^^^^
Type names start with an uppercase letter, and can optionally be
qualified by a module name, like Bool or Http.Request.Request.
"#
),
)
}
@ -4915,7 +4966,6 @@ mod test_reporting {
#[test]
fn empty_or_pattern() {
// this should get better with time
report_problem_as(
indoc!(
r#"
@ -4929,29 +4979,16 @@ mod test_reporting {
),
indoc!(
r#"
MISSING EXPRESSION
I am partway through parsing a definition, but I got stuck here:
1 when Just 4 is
UNFINISHED PATTERN
I just started parsing a pattern, but I got stuck here:
2 Just 4 | ->
^
I was expecting to see an expression like 42 or "hello".
^
Note: I may be confused by indentation
"#
),
// indoc!(
// r#"
// ── UNFINISHED PATTERN ──────────────────────────────────────────────────────────
//
// I just started parsing a pattern, but I got stuck here:
//
// 2│ Just 4 | ->
// ^
//
// Note: I may be confused by indentation
// "#
// ),
)
}
@ -5083,29 +5120,111 @@ mod test_reporting {
r#"
when 4 is
5 -> 2
_ -> 2
2 -> 2
"#
),
indoc!(
r#"
UNFINISHED WHEN
SYNTAX PROBLEM
I got stuck here:
1 when 4 is
2 5 -> 2
^
Whatever I am running into is confusing me a lot! Normally I can give
fairly specific hints, but something is really tripping me up this
time.
"#
),
// TODO this formerly gave
//
// ── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
//
// I was partway through parsing a `when` expression, but I got stuck here:
//
// 3│ _ -> 2
// ^
//
// I suspect this is a pattern that is not indented enough? (by 2 spaces)
//
// but that requires parsing the next pattern blindly, irrespective of indentation. Can
// we find an efficient solution that doesn't require parsing an extra pattern for
// every `when`, i.e. we want a good error message for the test case above, but for
// a valid `when`, we don't want to do extra work, e.g. here
//
// x
// when x is
// n -> n
//
// 4
//
// We don't want to parse the `4` and say it's an outdented pattern!
)
}
I was partway through parsing a `when` expression, but I got stuck here:
3 _ -> 2
^
I suspect this is a pattern that is not indented enough? (by 2 spaces)
#[test]
fn when_over_indented_underscore() {
report_problem_as(
indoc!(
r#"
when 4 is
5 -> 2
_ -> 2
"#
),
indoc!(
r#"
SYNTAX PROBLEM
I got stuck here:
1 when 4 is
2 5 -> 2
^
Whatever I am running into is confusing me a lot! Normally I can give
fairly specific hints, but something is really tripping me up this
time.
"#
),
)
}
#[test]
fn when_over_indented_int() {
report_problem_as(
indoc!(
r#"
when 4 is
5 -> Num.neg
2 -> 2
"#
),
indoc!(
r#"
UNEXPECTED ARROW
I am parsing a `when` expression right now, but this arrow is confusing
me:
3 2 -> 2
^^
It makes sense to see arrows around here, so I suspect it is something
earlier.Maybe this pattern is indented a bit farther from the previous
patterns?
Note: Here is an example of a valid `when` expression for reference.
when List.first plants is
Ok n ->
n
Err _ ->
200
Notice the indentation. All patterns are aligned, and each branch is
indented a bit more than the corresponding pattern. That is important!
"#
@ -5757,21 +5876,18 @@ mod test_reporting {
),
indoc!(
r#"
MISSING FINAL EXPRESSION
I am partway through parsing a definition's final expression, but I
got stuck here:
UNKNOWN OPERATOR
This looks like an operator, but it's not one I recognize!
1 main = 5 -> 3
^
This definition is missing a final expression. A nested definition
must be followed by either another definition, or an expression
x = 4
y = 2
x + y
^^
The arrow -> is only used to define cases in a `when`.
when color is
Red -> "stop!"
Green -> "go!"
"#
),
)

View file

@ -880,7 +880,7 @@ fn check_for_infinite_type(
let var = loc_var.value;
let is_uniq_infer = matches!(
subs.get(var).content,
subs.get_ref(var).content,
Content::Alias(Symbol::ATTR_ATTR, _, _)
);
@ -1088,7 +1088,7 @@ fn generalize(
for vars in all_but_last_pool {
for &var in vars {
if !subs.redundant(var) {
let rank = subs.get(var).rank;
let rank = subs.get_rank(var);
pools.get_mut(rank).push(var);
}
@ -1099,13 +1099,12 @@ fn generalize(
// otherwise generalize
for &var in last_pool {
if !subs.redundant(var) {
let mut desc = subs.get(var);
let desc_rank = subs.get_rank(var);
if desc.rank < young_rank {
pools.get_mut(desc.rank).push(var);
if desc_rank < young_rank {
pools.get_mut(desc_rank).push(var);
} else {
desc.rank = Rank::NONE;
subs.set(var, desc);
subs.set_rank(var, Rank::NONE);
}
}
}
@ -1121,18 +1120,8 @@ fn pool_to_rank_table(
// Sort the variables into buckets by rank.
for &var in young_vars.iter() {
let desc = subs.get(var);
let rank = desc.rank;
subs.set(
var,
Descriptor {
rank,
mark: young_mark,
content: desc.content,
copy: desc.copy,
},
);
let rank = subs.get_rank(var);
subs.set_mark(var, young_mark);
debug_assert!(rank.into_usize() < young_rank.into_usize() + 1);
pools.get_mut(rank).push(var);
@ -1155,24 +1144,15 @@ fn adjust_rank(
if desc.mark == young_mark {
let Descriptor {
content,
rank,
rank: _,
mark: _,
copy,
} = desc;
// Mark the variable as visited before adjusting content, as it may be cyclic.
subs.set(
var,
Descriptor {
content: content.clone(),
rank,
mark: visit_mark,
copy,
},
);
subs.set_mark(var, visit_mark);
let max_rank =
adjust_rank_content(subs, young_mark, visit_mark, group_rank, content.clone());
let max_rank = adjust_rank_content(subs, young_mark, visit_mark, group_rank, &content);
subs.set(
var,
@ -1208,7 +1188,7 @@ fn adjust_rank_content(
young_mark: Mark,
visit_mark: Mark,
group_rank: Rank,
content: Content,
content: &Content,
) -> Rank {
use roc_types::subs::Content::*;
use roc_types::subs::FlatType::*;
@ -1224,14 +1204,15 @@ fn adjust_rank_content(
let mut rank = Rank::toplevel();
for var in args {
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
rank =
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
}
rank
}
Func(arg_vars, closure_var, ret_var) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ret_var);
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ret_var);
// TODO investigate further.
//
@ -1244,12 +1225,13 @@ fn adjust_rank_content(
young_mark,
visit_mark,
group_rank,
closure_var,
*closure_var,
));
}
for var in arg_vars {
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
rank =
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
}
rank
@ -1263,9 +1245,9 @@ fn adjust_rank_content(
EmptyTagUnion => Rank::toplevel(),
Record(fields, ext_var) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ext_var);
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
for (_, var) in fields {
for var in fields.values() {
rank = rank.max(adjust_rank(
subs,
young_mark,
@ -1279,7 +1261,7 @@ fn adjust_rank_content(
}
TagUnion(tags, ext_var) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ext_var);
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
for var in tags.values().flatten() {
rank =
@ -1290,9 +1272,9 @@ fn adjust_rank_content(
}
RecursiveTagUnion(rec_var, tags, ext_var) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, rec_var);
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *rec_var);
rank = rank.max(adjust_rank(
subs, young_mark, visit_mark, group_rank, ext_var,
subs, young_mark, visit_mark, group_rank, *ext_var,
));
for var in tags.values().flatten() {
@ -1305,10 +1287,11 @@ fn adjust_rank_content(
Boolean(Bool::Shared) => Rank::toplevel(),
Boolean(Bool::Container(cvar, mvars)) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, cvar);
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *cvar);
for var in mvars {
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
rank =
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
}
rank
@ -1322,13 +1305,13 @@ fn adjust_rank_content(
let mut rank = Rank::toplevel();
for (_, var) in args {
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
}
// from elm-compiler: THEORY: anything in the real_var would be Rank::toplevel()
// this theory is not true in Roc! aliases of function types capture the closure var
rank = rank.max(adjust_rank(
subs, young_mark, visit_mark, group_rank, real_var,
subs, young_mark, visit_mark, group_rank, *real_var,
));
rank

View file

@ -1760,6 +1760,13 @@ fn list_sum() {
assert_evals_to!("List.sum [ 1.1, 2.2, 3.3 ]", 6.6, f64);
}
#[test]
fn list_product() {
assert_evals_to!("List.product []", 1, i64);
assert_evals_to!("List.product [ 1, 2, 3 ]", 6, i64);
assert_evals_to!("List.product [ 1.1, 2.2, 3.3 ]", 1.1 * 2.2 * 3.3, f64);
}
#[test]
fn list_keep_oks() {
assert_evals_to!("List.keepOks [] (\\x -> x)", 0, i64);

View file

@ -361,7 +361,6 @@ mod gen_num {
#[test]
fn f64_sqrt() {
// FIXME this works with normal types, but fails when checking uniqueness types
assert_evals_to!(
indoc!(
r#"
@ -375,6 +374,96 @@ mod gen_num {
);
}
#[test]
fn f64_log() {
assert_evals_to!(
indoc!(
r#"
when Num.log 7.38905609893 is
Ok val -> val
Err _ -> -1
"#
),
1.999999999999912,
f64
);
}
#[test]
fn f64_log_one() {
assert_evals_to!(
indoc!(
r#"
when Num.log 1 is
Ok val -> val
Err _ -> -1
"#
),
0.0,
f64
);
}
#[test]
fn f64_sqrt_zero() {
assert_evals_to!(
indoc!(
r#"
when Num.sqrt 0 is
Ok val -> val
Err _ -> -1
"#
),
0.0,
f64
);
}
#[test]
fn f64_sqrt_negative() {
assert_evals_to!(
indoc!(
r#"
when Num.sqrt -1 is
Err _ -> 42
Ok val -> val
"#
),
42.0,
f64
);
}
#[test]
fn f64_log_zero() {
assert_evals_to!(
indoc!(
r#"
when Num.log 0 is
Err _ -> 42
Ok val -> val
"#
),
42.0,
f64
);
}
#[test]
fn f64_log_negative() {
assert_evals_to!(
indoc!(
r#"
when Num.log -1 is
Err _ -> 42
Ok val -> val
"#
),
42.0,
f64
);
}
#[test]
fn f64_round_old() {
assert_evals_to!("Num.round 3.6", 4, i64);

View file

@ -2258,3 +2258,41 @@ fn backpassing_result() {
i64
);
}
#[test]
#[should_panic(
expected = "Shadowing { original_region: |L 3-3, C 4-5|, shadow: |L 5-5, C 6-7| Ident(\\\"x\\\") }"
)]
fn function_malformed_pattern() {
assert_evals_to!(
indoc!(
r#"
x = 3
(\x -> x) 42
"#
),
3,
i64
);
}
#[test]
#[should_panic(expected = "Hit an erroneous type when creating a layout for")]
fn call_invalid_layout() {
assert_llvm_evals_to!(
indoc!(
r#"
f : I64 -> I64
f = \x -> x
f {}
"#
),
3,
i64,
|x| x,
false,
true
);
}

View file

@ -37,6 +37,7 @@ pub fn helper<'a>(
src: &str,
stdlib: &'a roc_builtins::std::StdLib,
leak: bool,
ignore_problems: bool,
context: &'a inkwell::context::Context,
) -> (&'static str, String, Library) {
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
@ -74,7 +75,7 @@ pub fn helper<'a>(
let mut loaded = match loaded {
Ok(x) => x,
Err(roc_load::file::LoadingProblem::ParsingFailedReport(report)) => {
Err(roc_load::file::LoadingProblem::FormattedReport(report)) => {
println!("{}", report);
panic!();
}
@ -170,13 +171,13 @@ pub fn helper<'a>(
println!("{}", lines.join("\n"));
// only crash at this point if there were no delayed_errors
if delayed_errors.is_empty() {
if delayed_errors.is_empty() && !ignore_problems {
assert_eq!(0, 1, "Mistakes were made");
}
}
let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
Some(found) => found.clone(),
Some(found) => *found,
None => panic!(
"The main function symbol {:?} does not have a procedure in {:?}",
main_fn_symbol,
@ -331,7 +332,7 @@ pub fn helper<'a>(
#[macro_export]
macro_rules! assert_llvm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $ignore_problems:expr) => {
use bumpalo::Bump;
use inkwell::context::Context;
use roc_gen::run_jit_function;
@ -343,7 +344,7 @@ macro_rules! assert_llvm_evals_to {
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
let (main_fn_name, errors, lib) =
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, $ignore_problems, &context);
let transform = |success| {
let expected = $expected;
@ -354,7 +355,7 @@ macro_rules! assert_llvm_evals_to {
};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
assert_llvm_evals_to!($src, $expected, $ty, $transform, true);
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
};
}
@ -375,7 +376,7 @@ macro_rules! assert_evals_to {
// parsing the source, so that there's no chance their passing
// or failing depends on leftover state from the previous one.
{
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak, false);
}
{
// NOTE at the moment, the optimized tests do the same thing
@ -392,7 +393,7 @@ macro_rules! assert_non_opt_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
// Same as above, except with an additional transformation argument.
{
assert_llvm_evals_to!($src, $expected, $ty, $transform, true);
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
}
};
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {{

View file

@ -93,9 +93,9 @@ impl VarStore {
}
}
impl Into<Variable> for VarStore {
fn into(self) -> Variable {
Variable(self.next)
impl From<VarStore> for Variable {
fn from(store: VarStore) -> Self {
Variable(store.next)
}
}
@ -139,9 +139,9 @@ impl fmt::Debug for OptVariable {
}
}
impl Into<Option<Variable>> for OptVariable {
fn into(self) -> Option<Variable> {
self.into_variable()
impl From<OptVariable> for Option<Variable> {
fn from(opt_var: OptVariable) -> Self {
opt_var.into_variable()
}
}
@ -180,9 +180,9 @@ impl Variable {
}
}
impl Into<OptVariable> for Variable {
fn into(self) -> OptVariable {
OptVariable(self.0)
impl From<Variable> for OptVariable {
fn from(var: Variable) -> Self {
OptVariable(var.0)
}
}
@ -306,6 +306,18 @@ impl Subs {
self.utable.probe_value(key)
}
pub fn get_ref(&self, key: Variable) -> &Descriptor {
&self.utable.probe_value_ref(key).value
}
pub fn get_rank(&mut self, key: Variable) -> Rank {
self.utable.probe_value_ref(key).value.rank
}
pub fn get_mark(&mut self, key: Variable) -> Mark {
self.utable.probe_value_ref(key).value.mark
}
pub fn get_without_compacting(&self, key: Variable) -> Descriptor {
self.utable.probe_value_without_compacting(key)
}
@ -471,9 +483,9 @@ impl fmt::Debug for Rank {
}
}
impl Into<usize> for Rank {
fn into(self) -> usize {
self.0
impl From<Rank> for usize {
fn from(rank: Rank) -> Self {
rank.0
}
}