Merge branch 'trunk' of github.com:rtfeldman/roc into Change--arch-flag-to--A-when-calling-ld

This commit is contained in:
Luiz de Oliveira 2021-10-31 11:49:58 -03:00
commit 4b8a816aed
7 changed files with 248 additions and 41 deletions

View file

@ -42,4 +42,4 @@ Viktor Fröberg <vikfroberg@gmail.com>
Locria Cyber <locriacyber@noreply.users.github.com> Locria Cyber <locriacyber@noreply.users.github.com>
Matthias Beyer <mail@beyermatthias.de> Matthias Beyer <mail@beyermatthias.de>
Tim Whiting <tim@whitings.org> Tim Whiting <tim@whitings.org>
Logan Lowder <logan.lowder@logikcull.com>

View file

@ -50,8 +50,12 @@ If you want to install it manually, you can also download Zig directly [here](ht
**version: 12.0.x** **version: 12.0.x**
For macOS, you can install LLVM 12 using `brew install llvm@12` and then adding For macOS, you can install LLVM 12 using `brew install llvm@12` and then adding
`/usr/local/opt/llvm/bin` to your `PATH`. You can confirm this worked by `/usr/local/opt/llvm@12/bin` to your `PATH`. You can confirm this worked by
running `llc --version` - it should mention "LLVM version 12.0.0" at the top. running `llc --version` - it should mention "LLVM version 12.0.0" at the top.
You may also need to manually specify a prefix env var like so:
```
export LLVM_SYS_120_PREFIX=/usr/local/opt/llvm@12
```
For Ubuntu and Debian: For Ubuntu and Debian:
``` ```
@ -62,7 +66,7 @@ chmod +x llvm.sh
``` ```
If you use this script, you'll need to add `clang` and `llvm-as` to your `PATH`. If you use this script, you'll need to add `clang` and `llvm-as` to your `PATH`.
By default, the script installs them as `llvm-as-12` and `clang-12`, By default, the script installs them as `clang-12` and `llvm-as-12`,
respectively. You can address this with symlinks like so: respectively. You can address this with symlinks like so:
``` ```

View file

@ -753,6 +753,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
add_top_level_function_type!( add_top_level_function_type!(
Symbol::LIST_MIN, Symbol::LIST_MIN,
vec![list_type(num_type(flex(TVAR1)))], vec![list_type(num_type(flex(TVAR1)))],
Box::new(result_type(num_type(flex(TVAR1)), list_was_empty.clone())),
);
// max : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
add_top_level_function_type!(
Symbol::LIST_MAX,
vec![list_type(num_type(flex(TVAR1)))],
Box::new(result_type(num_type(flex(TVAR1)), list_was_empty)), Box::new(result_type(num_type(flex(TVAR1)), list_was_empty)),
); );

View file

@ -81,6 +81,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_CONCAT => list_concat, LIST_CONCAT => list_concat,
LIST_CONTAINS => list_contains, LIST_CONTAINS => list_contains,
LIST_MIN => list_min, LIST_MIN => list_min,
LIST_MAX => list_max,
LIST_SUM => list_sum, LIST_SUM => list_sum,
LIST_PRODUCT => list_product, LIST_PRODUCT => list_product,
LIST_PREPEND => list_prepend, LIST_PREPEND => list_prepend,
@ -2268,6 +2269,136 @@ fn list_min_lt(list_elem_var: Variable, var_store: &mut VarStore) -> Expr {
) )
} }
// max : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
fn list_max(symbol: Symbol, var_store: &mut VarStore) -> Def {
let arg_var = var_store.fresh();
let bool_var = var_store.fresh();
let list_var = var_store.fresh();
let len_var = Variable::NAT;
let num_var = len_var;
let num_precision_var = Variable::NATURAL;
let list_elem_var = var_store.fresh();
let ret_var = var_store.fresh();
let closure_var = var_store.fresh();
// Perform a bounds check. If it passes, delegate to List.getUnsafe.
let body = If {
cond_var: bool_var,
branch_var: var_store.fresh(),
branches: vec![(
// if-condition
no_region(
// List.len list != 0
RunLowLevel {
op: LowLevel::NotEq,
args: vec![
(len_var, int(num_var, num_precision_var, 0)),
(
len_var,
RunLowLevel {
op: LowLevel::ListLen,
args: vec![(list_var, Var(Symbol::ARG_1))],
ret_var: len_var,
},
),
],
ret_var: bool_var,
},
),
// list was not empty
no_region(
// Ok ( List.walk list (List.getUnsafe list 0) \acc,elem -> if elem < acc then elem else acc )
tag(
"Ok",
vec![
// List.walk list (List.getUnsafe list 0) \acc,elem -> if elem < acc then elem else acc
RunLowLevel {
op: LowLevel::ListWalk,
args: vec![
(list_var, Var(Symbol::ARG_1)),
// (List.getUnsafe list 0)
(
list_elem_var,
RunLowLevel {
op: LowLevel::ListGetUnsafe,
args: vec![
(list_var, Var(Symbol::ARG_1)),
(arg_var, int(num_var, num_precision_var, 0)),
],
ret_var: list_elem_var,
},
),
// \acc,elem -> if elem < acc then elem else acc
(closure_var, list_max_gt(list_elem_var, var_store)),
],
ret_var: list_elem_var,
},
],
var_store,
),
),
)],
final_else: Box::new(
// list was empty
no_region(
// Err ListWasEmpty
tag(
"Err",
vec![tag("ListWasEmpty", Vec::new(), var_store)],
var_store,
),
),
),
};
defn(
symbol,
vec![(list_var, Symbol::ARG_1)],
var_store,
body,
ret_var,
)
}
// \acc,elem -> if elem > acc then elem else acc
fn list_max_gt(list_elem_var: Variable, var_store: &mut VarStore) -> Expr {
let bool_var = var_store.fresh();
// if elem > acc then elem else acc
let body = If {
cond_var: bool_var,
branch_var: list_elem_var,
branches: vec![(
// if-condition
no_region(
// elem > acc
RunLowLevel {
op: LowLevel::NumGt,
args: vec![
(list_elem_var, Var(Symbol::ARG_4)),
(list_elem_var, Var(Symbol::ARG_3)),
],
ret_var: bool_var,
},
),
// return elem
no_region(Var(Symbol::ARG_4)),
)],
// return acc
final_else: Box::new(no_region(Var(Symbol::ARG_3))),
};
defn_help(
Symbol::LIST_MAX_GT,
vec![
(list_elem_var, Symbol::ARG_3),
(list_elem_var, Symbol::ARG_4),
],
var_store,
body,
list_elem_var,
)
}
/// List.sum : List (Num a) -> Num a /// List.sum : List (Num a) -> Num a
fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def {
let num_var = var_store.fresh(); let num_var = var_store.fresh();

View file

@ -1058,6 +1058,8 @@ define_builtins! {
35 LIST_DROP_LAST: "dropLast" 35 LIST_DROP_LAST: "dropLast"
36 LIST_MIN: "min" 36 LIST_MIN: "min"
37 LIST_MIN_LT: "#minlt" 37 LIST_MIN_LT: "#minlt"
38 LIST_MAX: "max"
39 LIST_MAX_GT: "#maxGt"
} }
5 RESULT: "Result" => { 5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias 0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -8,6 +8,7 @@ use roc_types::subs::{
Content, FlatType, RecordFields, Subs, UnionTags, Variable, VariableSubsSlice, Content, FlatType, RecordFields, Subs, UnionTags, Variable, VariableSubsSlice,
}; };
use roc_types::types::{gather_fields_unsorted_iter, RecordField}; use roc_types::types::{gather_fields_unsorted_iter, RecordField};
use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
use ven_pretty::{DocAllocator, DocBuilder}; use ven_pretty::{DocAllocator, DocBuilder};
@ -2534,6 +2535,64 @@ struct IdsByLayout<'a> {
next_id: u32, next_id: u32,
} }
impl<'a> IdsByLayout<'a> {
#[inline(always)]
fn insert_layout(&mut self, layout: Layout<'a>) -> LayoutId {
match self.by_id.entry(layout) {
Entry::Vacant(vacant) => {
let answer = self.next_id;
vacant.insert(answer);
self.next_id += 1;
LayoutId(answer)
}
Entry::Occupied(occupied) => LayoutId(*occupied.get()),
}
}
#[inline(always)]
fn singleton_layout(layout: Layout<'a>) -> (Self, LayoutId) {
let mut by_id = HashMap::with_capacity_and_hasher(1, default_hasher());
by_id.insert(layout, 1);
let ids_by_layout = IdsByLayout {
by_id,
toplevels_by_id: Default::default(),
next_id: 2,
};
(ids_by_layout, LayoutId(1))
}
#[inline(always)]
fn insert_toplevel(&mut self, layout: crate::ir::ProcLayout<'a>) -> LayoutId {
match self.toplevels_by_id.entry(layout) {
Entry::Vacant(vacant) => {
let answer = self.next_id;
vacant.insert(answer);
self.next_id += 1;
LayoutId(answer)
}
Entry::Occupied(occupied) => LayoutId(*occupied.get()),
}
}
#[inline(always)]
fn singleton_toplevel(layout: crate::ir::ProcLayout<'a>) -> (Self, LayoutId) {
let mut toplevels_by_id = HashMap::with_capacity_and_hasher(1, default_hasher());
toplevels_by_id.insert(layout, 1);
let ids_by_layout = IdsByLayout {
by_id: Default::default(),
toplevels_by_id,
next_id: 2,
};
(ids_by_layout, LayoutId(1))
}
}
#[derive(Default)] #[derive(Default)]
pub struct LayoutIds<'a> { pub struct LayoutIds<'a> {
by_symbol: MutMap<Symbol, IdsByLayout<'a>>, by_symbol: MutMap<Symbol, IdsByLayout<'a>>,
@ -2542,60 +2601,38 @@ pub struct LayoutIds<'a> {
impl<'a> LayoutIds<'a> { impl<'a> LayoutIds<'a> {
/// Returns a LayoutId which is unique for the given symbol and layout. /// Returns a LayoutId which is unique for the given symbol and layout.
/// If given the same symbol and same layout, returns the same LayoutId. /// If given the same symbol and same layout, returns the same LayoutId.
#[inline(always)]
pub fn get<'b>(&mut self, symbol: Symbol, layout: &'b Layout<'a>) -> LayoutId { pub fn get<'b>(&mut self, symbol: Symbol, layout: &'b Layout<'a>) -> LayoutId {
// Note: this function does some weird stuff to satisfy the borrow checker. match self.by_symbol.entry(symbol) {
// There's probably a nicer way to write it that still works. Entry::Vacant(vacant) => {
let ids = self.by_symbol.entry(symbol).or_insert_with(|| IdsByLayout { let (ids_by_layout, layout_id) = IdsByLayout::singleton_layout(*layout);
by_id: HashMap::with_capacity_and_hasher(1, default_hasher()),
toplevels_by_id: Default::default(),
next_id: 1,
});
// Get the id associated with this layout, or default to next_id. vacant.insert(ids_by_layout);
let answer = ids.by_id.get(layout).copied().unwrap_or(ids.next_id);
// If we had to default to next_id, it must not have been found; layout_id
// store the ID we're going to return and increment next_id. }
if answer == ids.next_id { Entry::Occupied(mut occupied_ids) => occupied_ids.get_mut().insert_layout(*layout),
ids.by_id.insert(*layout, ids.next_id);
ids.next_id += 1;
} }
LayoutId(answer)
} }
/// Returns a LayoutId which is unique for the given symbol and layout. /// Returns a LayoutId which is unique for the given symbol and layout.
/// If given the same symbol and same layout, returns the same LayoutId. /// If given the same symbol and same layout, returns the same LayoutId.
#[inline(always)]
pub fn get_toplevel<'b>( pub fn get_toplevel<'b>(
&mut self, &mut self,
symbol: Symbol, symbol: Symbol,
layout: &'b crate::ir::ProcLayout<'a>, layout: &'b crate::ir::ProcLayout<'a>,
) -> LayoutId { ) -> LayoutId {
// Note: this function does some weird stuff to satisfy the borrow checker. match self.by_symbol.entry(symbol) {
// There's probably a nicer way to write it that still works. Entry::Vacant(vacant) => {
let ids = self.by_symbol.entry(symbol).or_insert_with(|| IdsByLayout { let (ids_by_layout, layout_id) = IdsByLayout::singleton_toplevel(*layout);
by_id: Default::default(),
toplevels_by_id: HashMap::with_capacity_and_hasher(1, default_hasher()),
next_id: 1,
});
// Get the id associated with this layout, or default to next_id. vacant.insert(ids_by_layout);
let answer = ids
.toplevels_by_id
.get(layout)
.copied()
.unwrap_or(ids.next_id);
// If we had to default to next_id, it must not have been found; layout_id
// store the ID we're going to return and increment next_id. }
if answer == ids.next_id { Entry::Occupied(mut occupied_ids) => occupied_ids.get_mut().insert_toplevel(*layout),
ids.toplevels_by_id.insert(*layout, ids.next_id);
ids.next_id += 1;
} }
LayoutId(answer)
} }
} }

View file

@ -1975,6 +1975,32 @@ fn list_min() {
); );
} }
#[test]
fn list_max() {
assert_evals_to!(
indoc!(
r#"
when List.max [] is
Ok val -> val
Err _ -> -1
"#
),
-1,
i64
);
assert_evals_to!(
indoc!(
r#"
when List.max [3, 1, 2] is
Ok val -> val
Err _ -> -1
"#
),
3,
i64
);
}
#[test] #[test]
fn list_sum() { fn list_sum() {
assert_evals_to!("List.sum []", 0, i64); assert_evals_to!("List.sum []", 0, i64);