mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into int-abs
This commit is contained in:
commit
09ef6b2734
5 changed files with 90 additions and 81 deletions
|
@ -38,7 +38,7 @@ Every Roc application is built on top of exactly one Roc platform. There is no s
|
|||
The core Roc language and standard library include no I/O operations, which gives platform authors complete control over which effects they want to support. Some of the implications of this include:
|
||||
|
||||
* A high-performance build tool (or text editor) written in Rust can be a Roc platform with a strong plugin security model. For example, it could expose only operations allowing plugin authors to modify the contents of certain files, rather than allowing plugins arbitrary read/write access to the entire filesystem.
|
||||
* A VR or [Arduino](https://www.arduino.cc/) platform can expose uncommon I/O operations supported by that hardware, while omitting common I/O operations that are unsupported (such as reading keyboard input from a terminal that doesn't exist).
|
||||
* A VR or [Arduino](https://www.arduino.cc/) platform can expose uncommon I/O operations supported by that hardware, while omitting common I/O operations that are unsupported (such as reading keyboard input from a terminal that doesn't exist).
|
||||
* A high-performance Web server written in Rust can be a Roc platform where all I/O operations are implemented in terms of Streams or Observables rather than a more traditional asynchronous abstraction like Futures or Promises. This would mean all code in that platform's ecosystem would be necessarily built on a common streaming abstraction.
|
||||
|
||||
Each Roc platform gets its own separate package repository, with packages built on top of the API that platform exposes. This means each platform has its own ecosystem where everything is built on top of the same shared set of platform-specific primitives.
|
||||
|
|
|
@ -408,9 +408,9 @@ fn gen(
|
|||
Closure(annotation, _, _, loc_args, boxed_body) => {
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
|
||||
procs.insert_closure(
|
||||
procs.insert_named(
|
||||
&mut mono_env,
|
||||
Some(symbol),
|
||||
symbol,
|
||||
annotation,
|
||||
loc_args,
|
||||
loc_body,
|
||||
|
|
|
@ -144,6 +144,31 @@ type MsgReceiver = mpsc::Receiver<Msg>;
|
|||
/// The loaded_modules argument specifies which modules have already been loaded.
|
||||
/// It typically contains *at least* the standard modules, but is empty when loading
|
||||
/// the standard modules themselves.
|
||||
///
|
||||
/// If we're just type-checking everything (e.g. running `roc check` at the command line),
|
||||
/// we can stop there. However, if we're generating code, then there are additional steps.
|
||||
///
|
||||
/// 10. After reporting the completed type annotation, we have all the information necessary
|
||||
/// to monomorphize. However, since we want to monomorphize in parallel without
|
||||
/// duplicating work, we do monomorphization in two steps. First, we go through and
|
||||
/// determine all the specializations this module *wants*. We compute the hashes
|
||||
/// and report them to the coordinator thread, along with the mono::expr::Expr values of
|
||||
/// the current function's body. At this point, we have not yet begun to assemble Procs;
|
||||
/// all we've done is send a list of requetsted specializations to the coordinator.
|
||||
/// 11. The coordinator works through the specialization requests in parallel, adding them
|
||||
/// to a global map once they're finished. Performing one specialization may result
|
||||
/// in requests for others; these are added to the queue and worked through as normal.
|
||||
/// This process continues until *both* all modules have reported that they've finished
|
||||
/// adding specialization requests to the queue, *and* the queue is empty (including
|
||||
/// of any requestss that were added in the course of completing other requests). Now
|
||||
/// we have a map of specializations, and everything was assembled in parallel with
|
||||
/// no unique specialization ever getting assembled twice (meanaing no wasted effort).
|
||||
/// 12. Now that we have our final map of specializations, we can proceed to code gen!
|
||||
/// As long as the specializations are stored in a per-ModuleId map, we can also
|
||||
/// parallelize this code gen. (e.g. in dev builds, building separate LLVM modules
|
||||
/// and then linking them together, and possibly caching them by the hash of their
|
||||
/// specializations, so if none of their specializations changed, we don't even need
|
||||
/// to rebuild the module and can link in the cached one directly.)
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub async fn load<'a>(
|
||||
stdlib: &StdLib,
|
||||
|
|
|
@ -35,72 +35,56 @@ pub struct Procs<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Procs<'a> {
|
||||
fn insert_user_defined(&mut self, symbol: Symbol, partial_proc: PartialProc<'a>) {
|
||||
self.user_defined.insert(symbol, partial_proc);
|
||||
}
|
||||
|
||||
fn insert_anonymous(&mut self, symbol: Symbol, proc: Option<Proc<'a>>) {
|
||||
self.anonymous.insert(symbol, proc);
|
||||
}
|
||||
|
||||
pub fn insert_closure(
|
||||
pub fn insert_named(
|
||||
&mut self,
|
||||
env: &mut Env<'a, '_>,
|
||||
name: Option<Symbol>,
|
||||
name: Symbol,
|
||||
annotation: Variable,
|
||||
loc_args: std::vec::Vec<(Variable, Located<roc_can::pattern::Pattern>)>,
|
||||
loc_body: Located<roc_can::expr::Expr>,
|
||||
ret_var: Variable,
|
||||
) -> Symbol {
|
||||
// turn record/tag patterns into a when expression, e.g.
|
||||
//
|
||||
// foo = \{ x } -> body
|
||||
//
|
||||
// becomes
|
||||
//
|
||||
// foo = \r -> when r is { x } -> body
|
||||
//
|
||||
// conversion of one-pattern when expressions will do the most optimal thing
|
||||
) {
|
||||
let (_, arg_symbols, body) = patterns_to_when(env, loc_args, ret_var, loc_body);
|
||||
|
||||
// a named closure
|
||||
self.user_defined.insert(
|
||||
name,
|
||||
PartialProc {
|
||||
annotation,
|
||||
patterns: arg_symbols,
|
||||
body: body.value,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn insert_anonymous(
|
||||
&mut self,
|
||||
env: &mut Env<'a, '_>,
|
||||
symbol: Symbol,
|
||||
annotation: Variable,
|
||||
loc_args: std::vec::Vec<(Variable, Located<roc_can::pattern::Pattern>)>,
|
||||
loc_body: Located<roc_can::expr::Expr>,
|
||||
ret_var: Variable,
|
||||
) {
|
||||
let (arg_vars, arg_symbols, body) = patterns_to_when(env, loc_args, ret_var, loc_body);
|
||||
|
||||
match name {
|
||||
Some(symbol) => {
|
||||
// a named closure
|
||||
self.insert_user_defined(
|
||||
symbol,
|
||||
PartialProc {
|
||||
annotation,
|
||||
patterns: arg_symbols,
|
||||
body: body.value,
|
||||
},
|
||||
);
|
||||
// an anonymous closure. These will always be specialized already
|
||||
// by the surrounding context
|
||||
|
||||
symbol
|
||||
}
|
||||
None => {
|
||||
// an anonymous closure. These will always be specialized already
|
||||
// by the surrounding context
|
||||
let symbol = env.unique_symbol();
|
||||
let opt_proc = specialize_proc_body(
|
||||
env,
|
||||
self,
|
||||
annotation,
|
||||
ret_var,
|
||||
symbol,
|
||||
&arg_vars,
|
||||
&arg_symbols,
|
||||
annotation,
|
||||
body.value,
|
||||
)
|
||||
.ok();
|
||||
|
||||
let opt_proc = specialize_proc_body(
|
||||
env,
|
||||
self,
|
||||
annotation,
|
||||
ret_var,
|
||||
symbol,
|
||||
&arg_vars,
|
||||
&arg_symbols,
|
||||
annotation,
|
||||
body.value,
|
||||
)
|
||||
.ok();
|
||||
|
||||
self.insert_anonymous(symbol, opt_proc);
|
||||
|
||||
symbol
|
||||
}
|
||||
}
|
||||
self.anonymous.insert(symbol, opt_proc);
|
||||
}
|
||||
|
||||
fn insert_specialization(
|
||||
|
@ -331,6 +315,15 @@ fn num_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
|||
}
|
||||
}
|
||||
|
||||
/// turn record/tag patterns into a when expression, e.g.
|
||||
///
|
||||
/// foo = \{ x } -> body
|
||||
///
|
||||
/// becomes
|
||||
///
|
||||
/// foo = \r -> when r is { x } -> body
|
||||
///
|
||||
/// conversion of one-pattern when expressions will do the most optimal thing
|
||||
fn patterns_to_when<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
patterns: std::vec::Vec<(Variable, Located<roc_can::pattern::Pattern>)>,
|
||||
|
@ -482,9 +475,20 @@ fn from_can<'a>(
|
|||
LetRec(defs, ret_expr, _, _) => from_can_defs(env, defs, *ret_expr, procs),
|
||||
LetNonRec(def, ret_expr, _, _) => from_can_defs(env, vec![*def], *ret_expr, procs),
|
||||
|
||||
Closure(annotation, _, _, loc_args, boxed_body) => {
|
||||
Closure(ann, original_name, _, loc_args, boxed_body) => {
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
let symbol = procs.insert_closure(env, name, annotation, loc_args, loc_body, ret_var);
|
||||
let symbol = match name {
|
||||
Some(symbol) => {
|
||||
procs.insert_named(env, symbol, ann, loc_args, loc_body, ret_var);
|
||||
|
||||
symbol
|
||||
}
|
||||
None => {
|
||||
procs.insert_anonymous(env, original_name, ann, loc_args, loc_body, ret_var);
|
||||
|
||||
original_name
|
||||
}
|
||||
};
|
||||
|
||||
Expr::FunctionPointer(symbol)
|
||||
}
|
||||
|
@ -1350,7 +1354,7 @@ fn call_by_name<'a>(
|
|||
Some(specialization) => {
|
||||
opt_specialize_body = None;
|
||||
|
||||
// a specialization with this type hash already exists, use its symbol
|
||||
// a specialization with this type hash already exists, so use its symbol
|
||||
specialization.0
|
||||
}
|
||||
None => {
|
||||
|
|
|
@ -51,26 +51,6 @@ pub fn infer_expr(
|
|||
#[allow(dead_code)]
|
||||
const EXPANDED_STACK_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// Without this, some tests pass in `cargo test --release` but fail without
|
||||
/// the --release flag because they run out of stack space. This increases
|
||||
/// stack size for debug builds only, while leaving the stack space at the default
|
||||
/// amount for release builds.
|
||||
#[allow(dead_code)]
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn with_larger_debug_stack<F>(run_test: F)
|
||||
where
|
||||
F: FnOnce() -> (),
|
||||
F: Send,
|
||||
F: 'static,
|
||||
{
|
||||
std::thread::Builder::new()
|
||||
.stack_size(EXPANDED_STACK_SIZE)
|
||||
.spawn(run_test)
|
||||
.expect("Error while spawning expanded dev stack size thread")
|
||||
.join()
|
||||
.expect("Error while joining expanded dev stack size thread")
|
||||
}
|
||||
|
||||
/// In --release builds, don't increase the stack size. Run the test normally.
|
||||
/// This way, we find out if any of our tests are blowing the stack even after
|
||||
/// optimizations in release builds.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue