merge upstream/main

This commit is contained in:
Luke Boswell 2022-11-06 09:27:46 +11:00
commit cec67721e6
No known key found for this signature in database
GPG key ID: 0E908525B2C7BD68
59 changed files with 2542 additions and 990 deletions

View file

@ -13,7 +13,7 @@ use roc_mono::layout::{
self, union_sorted_tags_pub, Builtin, Layout, LayoutCache, LayoutInterner, UnionLayout,
UnionVariant, WrappedVariant,
};
use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral};
use roc_parse::ast::{AssignedField, Collection, Expr, Pattern, StrLiteral};
use roc_region::all::{Loc, Region};
use roc_std::RocDec;
use roc_target::TargetInfo;
@ -29,11 +29,6 @@ struct Env<'a, 'env> {
layout_cache: LayoutCache<'a>,
}
#[derive(Debug)]
pub enum ToAstProblem {
FunctionLayout,
}
/// JIT execute the given main function, and then wrap its results in an Expr
/// so we can display them to the user using the formatter.
///
@ -53,7 +48,7 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>(
interns: &'a Interns,
layout_interner: LayoutInterner<'a>,
target_info: TargetInfo,
) -> Result<Expr<'a>, ToAstProblem> {
) -> Expr<'a> {
let mut env = Env {
arena,
subs,
@ -68,10 +63,24 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>(
result,
captures_niche: _,
} => {
// this is a thunk
// This is a thunk, which cannot be defined in userspace, so we know
// it's `main` and can be executed.
jit_to_ast_help(&mut env, app, main_fn_name, &result, var)
}
_ => Err(ToAstProblem::FunctionLayout),
ProcLayout { arguments, .. } => {
// This is a user-supplied function; create a fake Expr for it.
let mut arg_patterns =
bumpalo::collections::Vec::with_capacity_in(arguments.len(), arena);
// Put in an underscore for each of the args, just to get the arity right.
for _ in 0..arguments.len() {
arg_patterns.push(Loc::at_zero(Pattern::Underscore("_")));
}
let body_expr = Loc::at_zero(Expr::Record(Collection::empty()));
Expr::Closure(arg_patterns.into_bump_slice(), arena.alloc(body_expr))
}
}
}
@ -331,7 +340,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
main_fn_name: &str,
layout: &Layout<'a>,
var: Variable,
) -> Result<Expr<'a>, ToAstProblem> {
) -> Expr<'a> {
let (newtype_containers, alias_content, raw_var) = unroll_newtypes_and_aliases(env, var);
macro_rules! num_helper {
@ -342,18 +351,17 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
};
}
let result = match layout {
Layout::Builtin(Builtin::Bool) => Ok(app.call_function(
main_fn_name,
|_mem: &A::Memory, num: bool| {
let expr = match layout {
Layout::Builtin(Builtin::Bool) => {
app.call_function(main_fn_name, |_mem: &A::Memory, num: bool| {
bool_to_ast(env, num, env.subs.get_content_without_compacting(raw_var))
},
)),
})
}
Layout::Builtin(Builtin::Int(int_width)) => {
use Content::*;
use IntWidth::*;
let result = match (alias_content, int_width) {
match (alias_content, int_width) {
(Some(Alias(Symbol::NUM_UNSIGNED8, ..)), U8) => num_helper!(u8),
(_, U8) => {
// This is not a number, it's a tag union or something else
@ -371,22 +379,18 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
(_, I32) => num_helper!(i32),
(_, I64) => num_helper!(i64),
(_, I128) => num_helper!(i128),
};
Ok(result)
}
}
Layout::Builtin(Builtin::Float(float_width)) => {
use FloatWidth::*;
let result = match float_width {
match float_width {
F32 => num_helper!(f32),
F64 => num_helper!(f64),
F128 => todo!("F128 not implemented"),
};
Ok(result)
}
}
Layout::Builtin(Builtin::Decimal) => Ok(num_helper!(RocDec)),
Layout::Builtin(Builtin::Decimal) => num_helper!(RocDec),
Layout::Builtin(Builtin::Str) => {
let body = |mem: &A::Memory, addr| {
let string = mem.deref_str(addr);
@ -394,24 +398,21 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
Expr::Str(StrLiteral::PlainLine(arena_str))
};
Ok(app.call_function_returns_roc_str(env.target_info, main_fn_name, body))
}
Layout::Builtin(Builtin::List(elem_layout)) => {
//
Ok(app.call_function_returns_roc_list(
main_fn_name,
|mem: &A::Memory, (addr, len, _cap)| {
list_to_ast(
env,
mem,
addr,
len,
elem_layout,
env.subs.get_content_without_compacting(raw_var),
)
},
))
app.call_function_returns_roc_str(env.target_info, main_fn_name, body)
}
Layout::Builtin(Builtin::List(elem_layout)) => app.call_function_returns_roc_list(
main_fn_name,
|mem: &A::Memory, (addr, len, _cap)| {
list_to_ast(
env,
mem,
addr,
len,
elem_layout,
env.subs.get_content_without_compacting(raw_var),
)
},
),
Layout::Struct { field_layouts, .. } => {
let fields = [Layout::u64(), *layout];
let layout = Layout::struct_no_name_order(env.arena.alloc(fields));
@ -423,38 +424,24 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
.get_content_without_compacting(raw_var)
{
Content::Structure(FlatType::Record(fields, _)) => {
Ok(struct_to_ast(env, mem, addr, *fields))
struct_to_ast(env, mem, addr, *fields)
}
Content::Structure(FlatType::EmptyRecord) => {
Ok(struct_to_ast(env, mem, addr, RecordFields::empty()))
struct_to_ast(env, mem, addr, RecordFields::empty())
}
Content::Structure(FlatType::TagUnion(tags, _)) => {
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
Ok(single_tag_union_to_ast(
env,
mem,
addr,
field_layouts,
tag_name,
payload_vars,
))
single_tag_union_to_ast(env, mem, addr, field_layouts, tag_name, payload_vars)
}
Content::Structure(FlatType::FunctionOrTagUnion(tag_names, _, _)) => {
let tag_name = &env.subs.get_subs_slice(*tag_names)[0];
Ok(single_tag_union_to_ast(
env,
mem,
addr,
field_layouts,
tag_name,
&[],
))
single_tag_union_to_ast(env, mem, addr, field_layouts, tag_name, &[])
}
Content::Structure(FlatType::Func(_, _, _)) => {
// a function with a struct as the closure environment
Ok(OPAQUE_FUNCTION)
OPAQUE_FUNCTION
}
other => {
unreachable!(
@ -472,7 +459,8 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
}
Layout::Union(UnionLayout::NonRecursive(_)) => {
let size = layout.stack_size(&env.layout_cache.interner, env.target_info);
Ok(app.call_function_dynamic_size(
app.call_function_dynamic_size(
main_fn_name,
size as usize,
|mem: &'a A::Memory, addr: usize| {
@ -485,14 +473,15 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
env.subs.get_root_key_without_compacting(raw_var),
)
},
))
)
}
Layout::Union(UnionLayout::Recursive(_))
| Layout::Union(UnionLayout::NonNullableUnwrapped(_))
| Layout::Union(UnionLayout::NullableUnwrapped { .. })
| Layout::Union(UnionLayout::NullableWrapped { .. }) => {
let size = layout.stack_size(&env.layout_cache.interner, env.target_info);
Ok(app.call_function_dynamic_size(
app.call_function_dynamic_size(
main_fn_name,
size as usize,
|mem: &'a A::Memory, addr: usize| {
@ -505,31 +494,29 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
env.subs.get_root_key_without_compacting(raw_var),
)
},
))
)
}
Layout::RecursivePointer => {
unreachable!("RecursivePointers can only be inside structures")
}
Layout::LambdaSet(_) => Ok(OPAQUE_FUNCTION),
Layout::LambdaSet(_) => OPAQUE_FUNCTION,
Layout::Boxed(_) => {
let size = layout.stack_size(&env.layout_cache.interner, env.target_info);
Ok(app.call_function_dynamic_size(
main_fn_name,
size as usize,
|mem: &A::Memory, addr| {
addr_to_ast(
env,
mem,
addr,
layout,
WhenRecursive::Unreachable,
env.subs.get_root_key_without_compacting(raw_var),
)
},
))
app.call_function_dynamic_size(main_fn_name, size as usize, |mem: &A::Memory, addr| {
addr_to_ast(
env,
mem,
addr,
layout,
WhenRecursive::Unreachable,
env.subs.get_root_key_without_compacting(raw_var),
)
})
}
};
result.map(|e| apply_newtypes(env, newtype_containers.into_bump_slice(), e))
apply_newtypes(env, newtype_containers.into_bump_slice(), expr)
}
fn tag_name_to_expr<'a>(env: &Env<'a, '_>, tag_name: &TagName) -> Expr<'a> {

View file

@ -1,6 +1,6 @@
use bumpalo::Bump;
use roc_load::{ExecutionMode, LoadConfig, Threading};
use roc_reporting::report::Palette;
use roc_reporting::report::{Palette, Severity};
use std::path::PathBuf;
use roc_fmt::annotation::Formattable;
@ -11,48 +11,47 @@ use roc_region::all::LineInfo;
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
use roc_target::TargetInfo;
use crate::eval::ToAstProblem;
pub enum ReplOutput {
Problems(Vec<String>),
NoProblems { expr: String, expr_type: String },
#[derive(Debug)]
pub struct ReplOutput {
pub expr: String,
pub expr_type: String,
}
pub fn format_answer(
arena: &Bump,
res_answer: Result<Expr, ToAstProblem>,
expr_type_str: String,
) -> ReplOutput {
let mut expr = roc_fmt::Buf::new_in(arena);
pub fn format_answer<'a>(arena: &'a Bump, answer: Expr<'_>) -> &'a str {
match answer {
Expr::Closure(_, _) | Expr::MalformedClosure => "<function>",
_ => {
let mut expr = roc_fmt::Buf::new_in(arena);
use ToAstProblem::*;
match res_answer {
Ok(answer) => {
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0);
}
Err(FunctionLayout) => {
expr.indent(0);
expr.push_str("<function>");
}
}
ReplOutput::NoProblems {
expr: expr.into_bump_str().to_string(),
expr_type: expr_type_str,
expr.into_bump_str()
}
}
}
pub fn compile_to_mono<'a>(
#[derive(Default, Debug)]
pub struct Problems {
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
impl Problems {
pub fn is_empty(&self) -> bool {
self.errors.is_empty() && self.warnings.is_empty()
}
}
pub fn compile_to_mono<'a, 'i, I: Iterator<Item = &'i str>>(
arena: &'a Bump,
src: &str,
defs: I,
expr: &str,
target_info: TargetInfo,
palette: Palette,
) -> Result<MonomorphizedModule<'a>, Vec<String>> {
) -> (Option<MonomorphizedModule<'a>>, Problems) {
let filename = PathBuf::from("");
let src_dir = PathBuf::from("fake/test/path");
let module_src = arena.alloc(promote_expr_to_module(src));
let (bytes_before_expr, module_src) = promote_expr_to_module(arena, defs, expr);
let exposed_types = Default::default();
let loaded = roc_load::load_and_monomorphize_from_str(
arena,
@ -71,10 +70,16 @@ pub fn compile_to_mono<'a>(
let mut loaded = match loaded {
Ok(v) => v,
Err(LoadingProblem::FormattedReport(report)) => {
return Err(vec![report]);
return (
None,
Problems {
errors: vec![report],
warnings: Vec::new(),
},
);
}
Err(e) => {
panic!("error while loading module: {:?}", e)
todo!("error while loading module: {:?}", e)
}
};
@ -86,7 +91,10 @@ pub fn compile_to_mono<'a>(
..
} = &mut loaded;
let mut lines = Vec::new();
let mut problems = Problems::default();
let errors = &mut problems.errors;
let warnings = &mut problems.warnings;
for (home, (module_path, src)) in sources.iter() {
let can_probs = can_problems.remove(home).unwrap_or_default();
@ -105,42 +113,77 @@ pub fn compile_to_mono<'a>(
let alloc = RocDocAllocator::new(&src_lines, *home, interns);
for problem in can_probs.into_iter() {
let report = can_problem(&alloc, &line_info, module_path.clone(), problem);
let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette);
lines.push(buf);
}
for problem in type_probs {
if let Some(report) = type_problem(&alloc, &line_info, module_path.clone(), problem) {
// Filter out all warnings and errors whose regions end before this,
// because they must be part of the defs (excluding the most renently added def,
// if that's the one being evaluated) and therefore not things we should show.
// This filters out things like shadowing warnings and unused def warnings.
if problem.region().unwrap_or_default().end().offset as usize >= bytes_before_expr {
let report = can_problem(&alloc, &line_info, module_path.clone(), problem);
let severity = report.severity;
let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette);
lines.push(buf);
match severity {
Severity::Warning => {
warnings.push(buf);
}
Severity::RuntimeError => {
errors.push(buf);
}
}
}
}
for problem in type_probs {
if let Some(report) = type_problem(&alloc, &line_info, module_path.clone(), problem) {
let severity = report.severity;
let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette);
match severity {
Severity::Warning => {
warnings.push(buf);
}
Severity::RuntimeError => {
errors.push(buf);
}
}
}
}
}
if !lines.is_empty() {
Err(lines)
} else {
Ok(loaded)
}
(Some(loaded), problems)
}
fn promote_expr_to_module(src: &str) -> String {
let mut buffer =
String::from("app \"app\" provides [replOutput] to \"./platform\"\n\nreplOutput =\n");
fn promote_expr_to_module<'a, 'i, I: Iterator<Item = &'i str>>(
arena: &'a Bump,
defs: I,
expr: &str,
) -> (usize, &'a str) {
const REPL_MODULE_HEADER: &str = "app \"app\" provides [replOutput] to \"./platform\"\n\n";
const REPL_MODULE_MAIN_DEF: &str = "replOutput =\n";
const INDENT: &str = " ";
for line in src.lines() {
// indent the body!
buffer.push_str(" ");
let mut buffer = bumpalo::collections::string::String::from_str_in(REPL_MODULE_HEADER, arena);
for line in defs {
// don't indent the defs
buffer.push_str(line);
buffer.push_str("\n\n");
}
buffer.push_str(REPL_MODULE_MAIN_DEF);
let bytes_before_expr = buffer.len();
for line in expr.lines() {
// indent the expr!
buffer.push_str(INDENT);
buffer.push_str(line);
buffer.push('\n');
}
buffer
(bytes_before_expr, buffer.into_bump_str())
}