Merge branch 'trunk' of ssh://github.com/rtfeldman/roc into text_highlighting

This commit is contained in:
Anton-4 2020-12-30 13:40:42 +01:00
commit 7f69e57d30
20 changed files with 944 additions and 287 deletions

View file

@ -93,7 +93,10 @@ fn jit_to_ast_help<'a>(
),
Layout::Builtin(Builtin::EmptyList) => {
Ok(run_jit_function!(lib, main_fn_name, &'static str, |_| {
Expr::List(&[])
Expr::List {
items: &[],
final_comments: &[],
}
}))
}
Layout::Builtin(Builtin::List(_, elem_layout)) => Ok(run_jit_function!(
@ -251,7 +254,10 @@ fn ptr_to_ast<'a>(
num_to_ast(env, f64_to_ast(env.arena, num), content)
}
Layout::Builtin(Builtin::EmptyList) => Expr::List(&[]),
Layout::Builtin(Builtin::EmptyList) => Expr::List {
items: &[],
final_comments: &[],
},
Layout::Builtin(Builtin::List(_, elem_layout)) => {
// Turn the (ptr, len) wrapper struct into actual ptr and len values.
let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) };
@ -331,7 +337,10 @@ fn list_to_ast<'a>(
let output = output.into_bump_slice();
Expr::List(output)
Expr::List {
items: output,
final_comments: &[],
}
}
fn single_tag_union_to_ast<'a>(

View file

@ -285,7 +285,9 @@ pub fn canonicalize_expr<'a>(
}
}
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
ast::Expr::List(loc_elems) => {
ast::Expr::List {
items: loc_elems, ..
} => {
if loc_elems.is_empty() {
(
List {

View file

@ -109,14 +109,24 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
arena.alloc(Located { region, value })
}
List(elems) | Nested(List(elems)) => {
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
for elem in elems.iter() {
new_elems.push(desugar_expr(arena, elem));
List {
items,
final_comments,
}
let new_elems = new_elems.into_bump_slice();
let value: Expr<'a> = List(new_elems);
| Nested(List {
items,
final_comments,
}) => {
let mut new_items = Vec::with_capacity_in(items.len(), arena);
for item in items.iter() {
new_items.push(desugar_expr(arena, item));
}
let new_items = new_items.into_bump_slice();
let value: Expr<'a> = List {
items: new_items,
final_comments,
};
arena.alloc(Located {
region: loc_expr.region,

View file

@ -122,7 +122,7 @@ pub fn fmt_body<'a>(
Expr::SpaceBefore(_, _) => {
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
}
Expr::Record { .. } | Expr::List(_) => {
Expr::Record { .. } | Expr::List { .. } => {
newline(buf, indent + INDENT);
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
}

View file

@ -38,7 +38,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
// These expressions always have newlines
Defs(_, _) | When(_, _) => true,
List(elems) => elems.iter().any(|loc_expr| loc_expr.is_multiline()),
List { items, .. } => items.iter().any(|loc_expr| loc_expr.is_multiline()),
Str(literal) => {
use roc_parse::ast::StrLiteral::*;
@ -261,8 +261,11 @@ impl<'a> Formattable<'a> for Expr<'a> {
fmt_if(buf, loc_condition, loc_then, loc_else, indent);
}
When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent),
List(loc_items) => {
fmt_list(buf, &loc_items, indent);
List {
items,
final_comments,
} => {
fmt_list(buf, &items, final_comments, indent);
}
BinOp((loc_left_side, bin_op, loc_right_side)) => fmt_bin_op(
buf,
@ -396,22 +399,23 @@ fn fmt_bin_op<'a>(
}
}
pub fn fmt_list<'a>(buf: &mut String<'a>, loc_items: &[&Located<Expr<'a>>], indent: u16) {
buf.push('[');
let mut iter = loc_items.iter().peekable();
let is_multiline = loc_items.iter().any(|item| (&item.value).is_multiline());
let item_indent = if is_multiline {
indent + INDENT
pub fn fmt_list<'a>(
buf: &mut String<'a>,
loc_items: &[&Located<Expr<'a>>],
final_comments: &'a [CommentOrNewline<'a>],
indent: u16,
) {
if loc_items.is_empty() && final_comments.iter().all(|c| c.is_newline()) {
buf.push_str("[]");
} else {
indent
};
while let Some(item) = iter.next() {
buf.push('[');
let is_multiline = loc_items.iter().any(|item| (&item.value).is_multiline());
if is_multiline {
let item_indent = indent + INDENT;
for item in loc_items.iter() {
match &item.value {
// TODO?? These SpaceAfter/SpaceBefore litany seems overcomplicated
// Can we simplify this? Or at least move this in a separate function.
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
newline(buf, item_indent);
fmt_comments_only(
@ -424,10 +428,7 @@ pub fn fmt_list<'a>(buf: &mut String<'a>, loc_items: &[&Located<Expr<'a>>], inde
match &expr_below {
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
expr_above.format(buf, item_indent);
if iter.peek().is_some() {
buf.push(',');
}
fmt_comments_only(
buf,
@ -438,50 +439,48 @@ pub fn fmt_list<'a>(buf: &mut String<'a>, loc_items: &[&Located<Expr<'a>>], inde
}
_ => {
expr_below.format(buf, item_indent);
if iter.peek().is_some() {
buf.push(',');
}
}
}
}
Expr::SpaceAfter(sub_expr, spaces) => {
newline(buf, item_indent);
sub_expr.format(buf, item_indent);
if iter.peek().is_some() {
buf.push(',');
}
fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, item_indent);
}
_ => {
newline(buf, item_indent);
item.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, item_indent);
if iter.peek().is_some() {
item.format_with_options(
buf,
Parens::NotNeeded,
Newlines::Yes,
item_indent,
);
buf.push(',');
}
}
}
} else {
buf.push(' ');
item.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, item_indent);
if iter.peek().is_some() {
buf.push(',');
}
}
}
if is_multiline {
fmt_comments_only(buf, final_comments.iter(), NewlineAt::Top, item_indent);
newline(buf, indent);
}
if !loc_items.is_empty() && !is_multiline {
buf.push(' ');
}
buf.push(']');
} else {
// is_multiline == false
let mut iter = loc_items.iter().peekable();
while let Some(item) = iter.next() {
buf.push(' ');
item.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
if iter.peek().is_some() {
buf.push(',');
}
}
buf.push_str(" ]");
}
}
}
pub fn empty_line_before_expr<'a>(expr: &'a Expr<'a>) -> bool {
@ -809,7 +808,7 @@ pub fn fmt_record<'a>(
final_comments: &'a [CommentOrNewline<'a>],
indent: u16,
) {
if loc_fields.is_empty() {
if loc_fields.is_empty() && final_comments.iter().all(|c| c.is_newline()) {
buf.push_str("{}");
} else {
buf.push('{');
@ -846,7 +845,7 @@ pub fn fmt_record<'a>(
newline(buf, indent);
} else {
// is_multiline == false */
// is_multiline == false
buf.push(' ');
let field_indent = indent;
let mut iter = loc_fields.iter().peekable();

View file

@ -1022,7 +1022,7 @@ mod test_fmt {
[
7,
8,
9
9,
]
"#
));
@ -1041,7 +1041,7 @@ mod test_fmt {
[
17,
18,
19
19,
]
"#
),
@ -1064,7 +1064,7 @@ mod test_fmt {
[
27,
28,
29
29,
]
"#
),
@ -1084,7 +1084,7 @@ mod test_fmt {
[
157,
158,
159
159,
]
"#
),
@ -1105,7 +1105,7 @@ mod test_fmt {
557,
648,
759,
837
837,
]
"#
),
@ -1127,7 +1127,7 @@ mod test_fmt {
257,
358,
# Hey!
459
459,
]
"#
),
@ -1155,7 +1155,7 @@ mod test_fmt {
37,
# Thirty Eight
38,
39
39,
]
"#
),
@ -1189,7 +1189,32 @@ mod test_fmt {
48,
# Bottom 48
# Top 49
49
49,
# Bottom 49
# 49!
]
"#
),
);
}
#[test]
fn ending_comments_in_list() {
expr_formats_to(
indoc!(
r#"
[ # Top 49
49
# Bottom 49
,
# 49!
]
"#
),
indoc!(
r#"
[
# Top 49
49,
# Bottom 49
# 49!
]
@ -1197,7 +1222,6 @@ mod test_fmt {
),
);
}
#[test]
fn multi_line_list_def() {
// expr_formats_same(indoc!(
@ -1228,7 +1252,7 @@ mod test_fmt {
results =
[
Ok 4,
Ok 5
Ok 5,
]
allOks results
@ -1271,6 +1295,27 @@ mod test_fmt {
expr_formats_same("{}");
}
#[test]
fn empty_record_with_comment() {
expr_formats_same(indoc!(
r#"
{
# comment
}"#
));
}
#[test]
fn empty_record_with_newline() {
expr_formats_to(
indoc!(
r#"
{
}"#
),
"{}",
);
}
#[test]
fn one_field() {
expr_formats_same("{ x: 4 }");
@ -2407,22 +2452,54 @@ mod test_fmt {
));
}
// TODO This raises a parse error:
// NotYetImplemented("TODO the : in this declaration seems outdented")
// #[test]
// fn multiline_tag_union_annotation() {
// expr_formats_same(indoc!(
// r#"
// b :
// [
// True,
// False,
// ]
#[test]
fn multiline_tag_union_annotation() {
expr_formats_same(indoc!(
r#"
b :
[
True,
False,
]
// b
// "#
// ));
// }
b
"#
));
}
#[test]
fn multiline_tag_union_annotation_with_final_comment() {
expr_formats_to(
indoc!(
r#"
b :
[
True,
# comment 1
False # comment 2
,
# comment 3
]
b
"#
),
indoc!(
r#"
b :
[
True,
# comment 1
False,
# comment 2
# comment 3
]
b
"#
),
);
}
#[test]
fn tag_union() {
@ -2436,6 +2513,28 @@ mod test_fmt {
));
}
// TODO: the current formatting seems a bit odd for multiline function annotations
// (beside weird indentation, note the trailing space after the "->")
// #[test]
// fn multiline_tag_union_function_annotation() {
// expr_formats_same(indoc!(
// r#"
// f :
// [
// True,
// False,
// ] ->
// [
// True,
// False,
// ]
// f = \x -> x
// a
// "#
// ));
// }
#[test]
fn recursive_tag_union() {
expr_formats_same(indoc!(

View file

@ -29,7 +29,7 @@ impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
#[macro_export]
macro_rules! run_jit_function {
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
let v: String = String::new();
run_jit_function!($lib, $main_fn_name, $ty, $transform, v)
}};
@ -52,12 +52,7 @@ macro_rules! run_jit_function {
match result.assume_init().into() {
Ok(success) => {
// only if there are no exceptions thrown, check for errors
assert_eq!(
$errors,
std::vec::Vec::new(),
"Encountered errors: {:?}",
$errors
);
assert!($errors.is_empty(), "Encountered errors:\n{}", $errors);
$transform(success)
}
@ -73,7 +68,7 @@ macro_rules! run_jit_function {
#[macro_export]
macro_rules! run_jit_function_dynamic_type {
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
let v: String = String::new();
run_jit_function_dynamic_type!($lib, $main_fn_name, $bytes, $transform, v)
}};

View file

@ -1748,4 +1748,59 @@ mod gen_primitives {
u8
);
}
#[test]
#[should_panic(
expected = "Roc failed with message: \"Shadowing { original_region: |L 3-3, C 4-5|, shadow: |L 6-6, C 8-9| Ident(\\\"x\\\") }\""
)]
fn pattern_shadowing() {
assert_evals_to!(
indoc!(
r#"
x = 4
when 4 is
x -> x
"#
),
0,
i64
);
}
#[test]
#[should_panic(expected = "TODO non-exhaustive pattern")]
fn non_exhaustive_pattern_let() {
assert_evals_to!(
indoc!(
r#"
x : Result I64 F64
x = Ok 4
(Ok y) = x
y
"#
),
0,
i64
);
}
#[test]
#[ignore]
#[should_panic(expected = "")]
fn unsupported_pattern_str_interp() {
assert_evals_to!(
indoc!(
r#"
{ x: 4 } = { x : 4 }
x
"#
),
0,
i64
);
}
}

View file

@ -561,7 +561,9 @@ mod gen_records {
}
#[test]
#[ignore]
fn optional_field_function_no_use_default() {
// blocked on https://github.com/rtfeldman/roc/issues/786
assert_evals_to!(
indoc!(
r#"
@ -579,7 +581,9 @@ mod gen_records {
}
#[test]
#[ignore]
fn optional_field_function_no_use_default_nested() {
// blocked on https://github.com/rtfeldman/roc/issues/786
assert_evals_to!(
indoc!(
r#"

View file

@ -22,7 +22,7 @@ pub fn helper<'a>(
stdlib: roc_builtins::std::StdLib,
leak: bool,
context: &'a inkwell::context::Context,
) -> (&'static str, Vec<roc_problem::can::Problem>, Library) {
) -> (&'static str, String, Library) {
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
use std::path::{Path, PathBuf};
@ -64,11 +64,14 @@ pub fn helper<'a>(
debug_assert_eq!(exposed_to_host.len(), 1);
let main_fn_symbol = exposed_to_host.keys().copied().nth(0).unwrap();
let (_, main_fn_layout) = procedures
.keys()
.find(|(s, _)| *s == main_fn_symbol)
.unwrap()
.clone();
let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
Some(found) => found.clone(),
None => panic!(
"The main function symbol {:?} does not have a procedure in {:?}",
main_fn_symbol,
&procedures.keys()
),
};
let target = target_lexicon::Triple::host();
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
@ -106,14 +109,14 @@ pub fn helper<'a>(
| UnusedArgument(_, _, _)
| UnusedImport(_, _)
| RuntimeError(_)
| UnsupportedPattern(_, _)
| ExposedButNotDefined(_) => {
delayed_errors.push(problem.clone());
let report = can_problem(&alloc, module_path.clone(), problem);
let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette);
delayed_errors.push(buf.clone());
lines.push(buf);
}
_ => {
@ -142,6 +145,7 @@ pub fn helper<'a>(
report.render_color_terminal(&mut buf, &alloc, &palette);
delayed_errors.push(buf.clone());
lines.push(buf);
}
}
@ -283,7 +287,7 @@ pub fn helper<'a>(
let lib = module_to_dylib(&env.module, &target, opt_level)
.expect("Error loading compiled dylib for test");
(main_fn_name, delayed_errors, lib)
(main_fn_name, delayed_errors.join("\n"), lib)
}
// TODO this is almost all code duplication with assert_llvm_evals_to

View file

@ -379,7 +379,7 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
match pattern {
// TODO use guard!
Identifier(_) | Underscore | Shadowed(_, _) | UnsupportedPattern(_) => {
Identifier(_) | Underscore => {
if let Guard::Guard { symbol, id, stmt } = guard {
all_tests.push(Guarded {
opt_test: None,
@ -408,12 +408,9 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
DestructType::Guard(guard) => {
arguments.push((guard.clone(), destruct.layout.clone()));
}
DestructType::Required => {
DestructType::Required(_) => {
arguments.push((Pattern::Underscore, destruct.layout.clone()));
}
DestructType::Optional(_expr) => {
// do nothing
}
}
}
@ -531,7 +528,7 @@ fn to_relevant_branch_help<'a>(
use Test::*;
match pattern {
Identifier(_) | Underscore | Shadowed(_, _) | UnsupportedPattern(_) => Some(branch.clone()),
Identifier(_) | Underscore => Some(branch.clone()),
RecordDestructure(destructs, _) => match test {
IsCtor {
@ -540,15 +537,10 @@ fn to_relevant_branch_help<'a>(
..
} => {
debug_assert!(test_name == &TagName::Global(RECORD_TAG_NAME.into()));
let sub_positions = destructs
.into_iter()
.filter(|destruct| !matches!(destruct.typ, DestructType::Optional(_)))
.enumerate()
.map(|(index, destruct)| {
let sub_positions = destructs.into_iter().enumerate().map(|(index, destruct)| {
let pattern = match destruct.typ {
DestructType::Guard(guard) => guard.clone(),
DestructType::Required => Pattern::Underscore,
DestructType::Optional(_expr) => unreachable!("because of the filter"),
DestructType::Required(_) => Pattern::Underscore,
};
(
@ -742,7 +734,7 @@ fn needs_tests<'a>(pattern: &Pattern<'a>) -> bool {
use Pattern::*;
match pattern {
Identifier(_) | Underscore | Shadowed(_, _) | UnsupportedPattern(_) => false,
Identifier(_) | Underscore => false,
RecordDestructure(_, _)
| AppliedTag { .. }

View file

@ -67,7 +67,7 @@ fn simplify<'a>(pattern: &crate::ir::Pattern<'a>) -> Pattern {
field_names.push(destruct.label.clone());
match &destruct.typ {
DestructType::Required | DestructType::Optional(_) => patterns.push(Anything),
DestructType::Required(_) => patterns.push(Anything),
DestructType::Guard(guard) => patterns.push(simplify(guard)),
}
}
@ -84,17 +84,6 @@ fn simplify<'a>(pattern: &crate::ir::Pattern<'a>) -> Pattern {
Ctor(union, tag_id, patterns)
}
Shadowed(_region, _ident) => {
// Treat as an Anything
// code-gen will make a runtime error out of the branch
Anything
}
UnsupportedPattern(_region) => {
// Treat as an Anything
// code-gen will make a runtime error out of the branch
Anything
}
AppliedTag {
tag_id,
arguments,

View file

@ -4,7 +4,7 @@ use crate::layout::{Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::all::{default_hasher, MutMap, MutSet};
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_problem::can::RuntimeError;
@ -1264,7 +1264,50 @@ fn patterns_to_when<'a>(
// this must be fixed when moving exhaustiveness checking to the new canonical AST
for (pattern_var, pattern) in patterns.into_iter() {
let context = crate::exhaustive::Context::BadArg;
let mono_pattern = from_can_pattern(env, layout_cache, &pattern.value);
let mono_pattern = match from_can_pattern(env, layout_cache, &pattern.value) {
Ok((pat, assignments)) => {
for (symbol, variable, expr) in assignments.into_iter().rev() {
if let Ok(old_body) = body {
let def = roc_can::def::Def {
annotation: None,
expr_var: variable,
loc_expr: Located::at(pattern.region, expr),
loc_pattern: Located::at(
pattern.region,
roc_can::pattern::Pattern::Identifier(symbol),
),
pattern_vars: std::iter::once((symbol, variable)).collect(),
};
let new_expr = roc_can::expr::Expr::LetNonRec(
Box::new(def),
Box::new(old_body),
variable,
);
let new_body = Located {
region: pattern.region,
value: new_expr,
};
body = Ok(new_body);
}
}
pat
}
Err(runtime_error) => {
// Even if the body was Ok, replace it with this Err.
// If it was already an Err, leave it at that Err, so the first
// RuntimeError we encountered remains the first.
body = body.and({
Err(Located {
region: pattern.region,
value: runtime_error,
})
});
continue;
}
};
match crate::exhaustive::check(
pattern.region,
@ -2392,7 +2435,14 @@ pub fn with_hole<'a>(
}
} else {
// this may be a destructure pattern
let mono_pattern = from_can_pattern(env, layout_cache, &def.loc_pattern.value);
let (mono_pattern, assignments) =
match from_can_pattern(env, layout_cache, &def.loc_pattern.value) {
Ok(v) => v,
Err(_runtime_error) => {
// todo
panic!();
}
};
let context = crate::exhaustive::Context::BadDestruct;
match crate::exhaustive::check(
@ -2412,6 +2462,14 @@ pub fn with_hole<'a>(
// return Stmt::RuntimeError("TODO non-exhaustive pattern");
}
let mut hole = hole;
for (symbol, variable, expr) in assignments {
let stmt = with_hole(env, expr, variable, procs, layout_cache, symbol, hole);
hole = env.arena.alloc(stmt);
}
// convert the continuation
let mut stmt = with_hole(
env,
@ -3923,13 +3981,23 @@ pub fn from_can<'a>(
}
// this may be a destructure pattern
let mono_pattern = from_can_pattern(env, layout_cache, &def.loc_pattern.value);
let (mono_pattern, assignments) =
match from_can_pattern(env, layout_cache, &def.loc_pattern.value) {
Ok(v) => v,
Err(_) => todo!(),
};
if let Pattern::Identifier(symbol) = mono_pattern {
let hole =
let mut hole =
env.arena
.alloc(from_can(env, variable, cont.value, procs, layout_cache));
for (symbol, variable, expr) in assignments {
let stmt = with_hole(env, expr, variable, procs, layout_cache, symbol, hole);
hole = env.arena.alloc(stmt);
}
with_hole(
env,
def.loc_expr.value,
@ -3954,13 +4022,20 @@ pub fn from_can<'a>(
for error in errors {
env.problems.push(MonoProblem::PatternProblem(error))
}
} // TODO make all variables bound in the pattern evaluate to a runtime error
// return Stmt::RuntimeError("TODO non-exhaustive pattern");
return Stmt::RuntimeError("TODO non-exhaustive pattern");
}
}
// convert the continuation
let mut stmt = from_can(env, variable, cont.value, procs, layout_cache);
// layer on any default record fields
for (symbol, variable, expr) in assignments {
let hole = env.arena.alloc(stmt);
stmt = with_hole(env, expr, variable, procs, layout_cache, symbol, hole);
}
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
.unwrap()
@ -4015,21 +4090,53 @@ fn to_opt_branches<'a>(
};
for loc_pattern in when_branch.patterns {
let mono_pattern = from_can_pattern(env, layout_cache, &loc_pattern.value);
match from_can_pattern(env, layout_cache, &loc_pattern.value) {
Ok((mono_pattern, assignments)) => {
loc_branches.push((
Located::at(loc_pattern.region, mono_pattern.clone()),
exhaustive_guard.clone(),
));
let mut loc_expr = when_branch.value.clone();
let region = loc_pattern.region;
for (symbol, variable, expr) in assignments.into_iter().rev() {
let def = roc_can::def::Def {
annotation: None,
expr_var: variable,
loc_expr: Located::at(region, expr),
loc_pattern: Located::at(
region,
roc_can::pattern::Pattern::Identifier(symbol),
),
pattern_vars: std::iter::once((symbol, variable)).collect(),
};
let new_expr = roc_can::expr::Expr::LetNonRec(
Box::new(def),
Box::new(loc_expr),
variable,
);
loc_expr = Located::at(region, new_expr);
}
// TODO remove clone?
opt_branches.push((mono_pattern, when_branch.guard.clone(), loc_expr.value));
}
Err(runtime_error) => {
loc_branches.push((
Located::at(loc_pattern.region, Pattern::Underscore),
exhaustive_guard.clone(),
));
// TODO remove clone?
opt_branches.push((
mono_pattern,
Pattern::Underscore,
when_branch.guard.clone(),
when_branch.value.value.clone(),
roc_can::expr::Expr::RuntimeError(runtime_error),
));
}
}
}
}
// NOTE exhaustiveness is checked after the construction of all the branches
// In contrast to elm (currently), we still do codegen even if a pattern is non-exhaustive.
@ -4658,14 +4765,6 @@ fn store_pattern<'a>(
RecordDestructure(_, _) => {
unreachable!("a record destructure must always occur on a struct layout");
}
Shadowed(_region, _ident) => {
return Err(&"shadowed");
}
UnsupportedPattern(_region) => {
return Err(&"unsupported pattern");
}
}
Ok(stmt)
@ -4695,25 +4794,14 @@ fn store_record_destruct<'a>(
};
match &destruct.typ {
DestructType::Required => {
DestructType::Required(symbol) => {
stmt = Stmt::Let(
destruct.symbol,
*symbol,
load,
destruct.layout.clone(),
env.arena.alloc(stmt),
);
}
DestructType::Optional(expr) => {
stmt = with_hole(
env,
expr.clone(),
destruct.variable,
procs,
layout_cache,
destruct.symbol,
env.arena.alloc(stmt),
);
}
DestructType::Guard(guard_pattern) => match &guard_pattern {
Identifier(symbol) => {
stmt = Stmt::Let(
@ -5429,11 +5517,6 @@ pub enum Pattern<'a> {
layout: Layout<'a>,
union: crate::exhaustive::Union,
},
// Runtime Exceptions
Shadowed(Region, Located<Ident>),
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
UnsupportedPattern(Region),
}
#[derive(Clone, Debug, PartialEq)]
@ -5441,14 +5524,12 @@ pub struct RecordDestruct<'a> {
pub label: Lowercase,
pub variable: Variable,
pub layout: Layout<'a>,
pub symbol: Symbol,
pub typ: DestructType<'a>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum DestructType<'a> {
Required,
Optional(roc_can::expr::Expr),
Required(Symbol),
Guard(Pattern<'a>),
}
@ -5459,27 +5540,51 @@ pub struct WhenBranch<'a> {
pub guard: Option<Stmt<'a>>,
}
pub fn from_can_pattern<'a>(
#[allow(clippy::type_complexity)]
fn from_can_pattern<'a>(
env: &mut Env<'a, '_>,
layout_cache: &mut LayoutCache<'a>,
can_pattern: &roc_can::pattern::Pattern,
) -> Pattern<'a> {
) -> Result<
(
Pattern<'a>,
Vec<'a, (Symbol, Variable, roc_can::expr::Expr)>,
),
RuntimeError,
> {
let mut assignments = Vec::new_in(env.arena);
let pattern = from_can_pattern_help(env, layout_cache, can_pattern, &mut assignments)?;
Ok((pattern, assignments))
}
fn from_can_pattern_help<'a>(
env: &mut Env<'a, '_>,
layout_cache: &mut LayoutCache<'a>,
can_pattern: &roc_can::pattern::Pattern,
assignments: &mut Vec<'a, (Symbol, Variable, roc_can::expr::Expr)>,
) -> Result<Pattern<'a>, RuntimeError> {
use roc_can::pattern::Pattern::*;
match can_pattern {
Underscore => Pattern::Underscore,
Identifier(symbol) => Pattern::Identifier(*symbol),
IntLiteral(v) => Pattern::IntLiteral(*v),
FloatLiteral(v) => Pattern::FloatLiteral(f64::to_bits(*v)),
StrLiteral(v) => Pattern::StrLiteral(v.clone()),
Shadowed(region, ident) => Pattern::Shadowed(*region, ident.clone()),
UnsupportedPattern(region) => Pattern::UnsupportedPattern(*region),
Underscore => Ok(Pattern::Underscore),
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
IntLiteral(v) => Ok(Pattern::IntLiteral(*v)),
FloatLiteral(v) => Ok(Pattern::FloatLiteral(f64::to_bits(*v))),
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
Shadowed(region, ident) => Err(RuntimeError::Shadowing {
original_region: *region,
shadow: ident.clone(),
}),
UnsupportedPattern(region) => Err(RuntimeError::UnsupportedPattern(*region)),
MalformedPattern(_problem, region) => {
// TODO preserve malformed problem information here?
Pattern::UnsupportedPattern(*region)
Err(RuntimeError::UnsupportedPattern(*region))
}
NumLiteral(var, num) => match num_argument_to_int_or_float(env.subs, *var) {
IntOrFloat::IntType => Pattern::IntLiteral(*num),
IntOrFloat::FloatType => Pattern::FloatLiteral(*num as u64),
IntOrFloat::IntType => Ok(Pattern::IntLiteral(*num)),
IntOrFloat::FloatType => Ok(Pattern::FloatLiteral(*num as u64)),
},
AppliedTag {
@ -5493,7 +5598,7 @@ pub fn from_can_pattern<'a>(
let variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs);
match variant {
let result = match variant {
Never => unreachable!(
"there is no pattern of type `[]`, union var {:?}",
*whole_var
@ -5582,7 +5687,7 @@ pub fn from_can_pattern<'a>(
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
for ((_, loc_pat), layout) in arguments.iter().zip(field_layouts.iter()) {
mono_args.push((
from_can_pattern(env, layout_cache, &loc_pat.value),
from_can_pattern_help(env, layout_cache, &loc_pat.value, assignments)?,
layout.clone(),
));
}
@ -5642,7 +5747,7 @@ pub fn from_can_pattern<'a>(
let it = argument_layouts[1..].iter();
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
mono_args.push((
from_can_pattern(env, layout_cache, &loc_pat.value),
from_can_pattern_help(env, layout_cache, &loc_pat.value, assignments)?,
layout.clone(),
));
}
@ -5664,7 +5769,9 @@ pub fn from_can_pattern<'a>(
layout,
}
}
}
};
Ok(result)
}
RecordDestructure {
@ -5704,14 +5811,14 @@ pub fn from_can_pattern<'a>(
layout_cache,
&destruct.value,
field_layout.clone(),
));
assignments,
)?);
}
None => {
// this field is not destructured by the pattern
// put in an underscore
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
variable,
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
@ -5727,29 +5834,26 @@ pub fn from_can_pattern<'a>(
match destructs_by_label.remove(&label) {
Some(destruct) => {
// this field is destructured by the pattern
mono_destructs.push(RecordDestruct {
label: destruct.value.label.clone(),
symbol: destruct.value.symbol,
layout: field_layout,
variable,
typ: match &destruct.value.typ {
match &destruct.value.typ {
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
// if we reach this stage, the optional field is not present
// so use the default
DestructType::Optional(loc_expr.value.clone())
// so we push the default assignment into the branch
assignments.push((
destruct.value.symbol,
variable,
loc_expr.value.clone(),
));
}
_ => unreachable!(
"only optional destructs can be optional fields"
),
},
});
};
}
None => {
// this field is not destructured by the pattern
// put in an underscore
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
variable,
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
@ -5765,28 +5869,30 @@ pub fn from_can_pattern<'a>(
// it must be an optional field, and we will use the default
match &destruct.value.typ {
roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
let field_layout = layout_cache
.from_var(env.arena, *field_var, env.subs)
.unwrap_or_else(|err| {
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
});
mono_destructs.push(RecordDestruct {
label: destruct.value.label.clone(),
symbol: destruct.value.symbol,
variable: destruct.value.var,
layout: field_layout,
typ: DestructType::Optional(loc_expr.value.clone()),
})
// TODO these don't match up in the uniqueness inference; when we remove
// that, reinstate this assert!
//
// dbg!(&env.subs.get_without_compacting(*field_var).content);
// dbg!(&env.subs.get_without_compacting(destruct.value.var).content);
// debug_assert_eq!(
// env.subs.get_root_key_without_compacting(*field_var),
// env.subs.get_root_key_without_compacting(destruct.value.var)
// );
assignments.push((
destruct.value.symbol,
// destruct.value.var,
*field_var,
loc_expr.value.clone(),
));
}
_ => unreachable!("only optional destructs can be optional fields"),
}
}
Pattern::RecordDestructure(
Ok(Pattern::RecordDestructure(
mono_destructs,
Layout::Struct(field_layouts.into_bump_slice()),
)
))
}
}
}
@ -5796,24 +5902,23 @@ fn from_can_record_destruct<'a>(
layout_cache: &mut LayoutCache<'a>,
can_rd: &roc_can::pattern::RecordDestruct,
field_layout: Layout<'a>,
) -> RecordDestruct<'a> {
RecordDestruct {
assignments: &mut Vec<'a, (Symbol, Variable, roc_can::expr::Expr)>,
) -> Result<RecordDestruct<'a>, RuntimeError> {
Ok(RecordDestruct {
label: can_rd.label.clone(),
symbol: can_rd.symbol,
variable: can_rd.var,
layout: field_layout,
typ: match &can_rd.typ {
roc_can::pattern::DestructType::Required => DestructType::Required,
roc_can::pattern::DestructType::Required => DestructType::Required(can_rd.symbol),
roc_can::pattern::DestructType::Optional(_, _) => {
// if we reach this stage, the optional field is present
// DestructType::Optional(loc_expr.value.clone())
DestructType::Required
}
roc_can::pattern::DestructType::Guard(_, loc_pattern) => {
DestructType::Guard(from_can_pattern(env, layout_cache, &loc_pattern.value))
DestructType::Required(can_rd.symbol)
}
roc_can::pattern::DestructType::Guard(_, loc_pattern) => DestructType::Guard(
from_can_pattern_help(env, layout_cache, &loc_pattern.value, assignments)?,
),
},
}
})
}
/// Potentially translate LowLevel operations into more efficient ones based on

View file

@ -93,7 +93,10 @@ pub enum Expr<'a> {
AccessorFunction(&'a str),
// Collection Literals
List(&'a [&'a Loc<Expr<'a>>]),
List {
items: &'a [&'a Loc<Expr<'a>>],
final_comments: &'a [CommentOrNewline<'a>],
},
Record {
update: Option<&'a Loc<Expr<'a>>>,
@ -300,6 +303,15 @@ impl<'a> CommentOrNewline<'a> {
DocComment(_) => true,
}
}
pub fn is_newline(&self) -> bool {
use CommentOrNewline::*;
match self {
Newline => true,
LineComment(_) => false,
DocComment(_) => false,
}
}
}
#[derive(Clone, Debug, PartialEq)]

View file

@ -306,7 +306,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
// These would not have parsed as patterns
Expr::AccessorFunction(_)
| Expr::Access(_, _)
| Expr::List(_)
| Expr::List { .. }
| Expr::Closure(_, _)
| Expr::BinOp(_)
| Expr::Defs(_, _)
@ -1848,7 +1848,7 @@ fn binop<'a>() -> impl Parser<'a, BinOp> {
}
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
let elems = collection!(
let elems = collection_trailing_sep!(
ascii_char(b'['),
loc!(expr(min_indent)),
ascii_char(b','),
@ -1858,14 +1858,21 @@ pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
parser::attempt(
Attempting::List,
map_with_arena!(elems, |arena, parsed_elems: Vec<'a, Located<Expr<'a>>>| {
map_with_arena!(elems, |arena,
(parsed_elems, final_comments): (
Vec<'a, Located<Expr<'a>>>,
&'a [CommentOrNewline<'a>]
)| {
let mut allocated = Vec::with_capacity_in(parsed_elems.len(), arena);
for parsed_elem in parsed_elems {
allocated.push(&*arena.alloc(parsed_elem));
}
Expr::List(allocated.into_bump_slice())
Expr::List {
items: allocated.into_bump_slice(),
final_comments,
}
}),
)
}

View file

@ -1017,8 +1017,10 @@ mod test_parse {
#[test]
fn empty_list() {
let arena = Bump::new();
let elems = &[];
let expected = List(elems);
let expected = List {
items: &[],
final_comments: &[],
};
let actual = parse_expr_with(&arena, "[]");
assert_eq!(Ok(expected), actual);
@ -1028,8 +1030,10 @@ mod test_parse {
fn spaces_inside_empty_list() {
// This is a regression test!
let arena = Bump::new();
let elems = &[];
let expected = List(elems);
let expected = List {
items: &[],
final_comments: &[],
};
let actual = parse_expr_with(&arena, "[ ]");
assert_eq!(Ok(expected), actual);
@ -1038,8 +1042,11 @@ mod test_parse {
#[test]
fn packed_singleton_list() {
let arena = Bump::new();
let elems = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
let expected = List(elems);
let items = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
let expected = List {
items,
final_comments: &[],
};
let actual = parse_expr_with(&arena, "[1]");
assert_eq!(Ok(expected), actual);
@ -1048,8 +1055,11 @@ mod test_parse {
#[test]
fn spaced_singleton_list() {
let arena = Bump::new();
let elems = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
let expected = List(elems);
let items = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
let expected = List {
items,
final_comments: &[],
};
let actual = parse_expr_with(&arena, "[ 1 ]");
assert_eq!(Ok(expected), actual);

View file

@ -43,6 +43,28 @@ pub enum Def {
Function(FunctionDef),
}
impl Def {
pub fn symbols(&self, pool: &Pool) -> MutSet<Symbol> {
let mut output = MutSet::default();
match self {
Def::AnnotationOnly { .. } => todo!("lost pattern information here ... "),
Def::Value(ValueDef { pattern, .. }) => {
let pattern2 = &pool[*pattern];
output.extend(symbols_from_pattern(pool, pattern2));
}
Def::Function(function_def) => match function_def {
FunctionDef::NoAnnotation { name, .. }
| FunctionDef::WithAnnotation { name, .. } => {
output.insert(*name);
}
},
}
output
}
}
impl ShallowClone for Def {
fn shallow_clone(&self) -> Self {
match self {
@ -787,7 +809,6 @@ pub struct CanDefs {
pub fn canonicalize_defs<'a>(
env: &mut Env<'a>,
mut output: Output,
var_store: &mut VarStore,
original_scope: &Scope,
loc_defs: &'a [&'a Located<ast::Def<'a>>],
pattern_type: PatternType,

View file

@ -43,7 +43,7 @@ impl Output {
pub struct Env<'a> {
pub home: ModuleId,
pub var_store: VarStore,
pub var_store: &'a mut VarStore,
pub pool: &'a mut Pool,
pub arena: &'a Bump,
@ -63,6 +63,32 @@ pub struct Env<'a> {
}
impl<'a> Env<'a> {
pub fn new(
home: ModuleId,
arena: &'a Bump,
pool: &'a mut Pool,
var_store: &'a mut VarStore,
dep_idents: MutMap<ModuleId, IdentIds>,
module_ids: &'a ModuleIds,
exposed_ident_ids: IdentIds,
) -> Env<'a> {
Env {
home,
arena,
pool,
var_store,
dep_idents,
module_ids,
ident_ids: exposed_ident_ids.clone(), // we start with these, but will add more later
exposed_ident_ids,
closures: MutMap::default(),
qualified_lookups: MutSet::default(),
tailcallable_symbol: None,
closure_name_symbol: None,
top_level_symbols: MutSet::default(),
}
}
pub fn add<T>(&mut self, item: T, region: Region) -> NodeId<T> {
let id = self.pool.add(item);
self.set_region(id, region);
@ -285,14 +311,14 @@ pub fn to_expr2<'a>(
Str(literal) => flatten_str_literal(env, scope, &literal),
List(elements) => {
List { items, .. } => {
let mut output = Output::default();
let output_ref = &mut output;
let elems = PoolVec::with_capacity(elements.len() as u32, env.pool);
let elems = PoolVec::with_capacity(items.len() as u32, env.pool);
for (node_id, element) in elems.iter_node_ids().zip(elements.iter()) {
let (expr, sub_output) = to_expr2(env, scope, &element.value, element.region);
for (node_id, item) in elems.iter_node_ids().zip(items.iter()) {
let (expr, sub_output) = to_expr2(env, scope, &item.value, item.region);
output_ref.union(sub_output);

View file

@ -34,6 +34,7 @@ pub mod error;
pub mod expr;
pub mod file;
mod keyboard_input;
mod module;
mod ortho;
mod pattern;
pub mod pool;

317
editor/src/module.rs Normal file
View file

@ -0,0 +1,317 @@
#![allow(clippy::all)]
#![allow(dead_code)]
#![allow(unused_imports)]
#![allow(unused_variables)]
use crate::ast::{FunctionDef, ValueDef};
use crate::def::{canonicalize_defs, sort_can_defs, Declaration, Def};
use crate::expr::Env;
use crate::expr::Output;
use crate::pattern::Pattern2;
use crate::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone};
use crate::scope::Scope;
use crate::types::Alias;
use bumpalo::Bump;
use roc_can::operator::desugar_def;
use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap};
use roc_module::ident::Ident;
use roc_module::ident::Lowercase;
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
use roc_parse::ast;
use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Located, Region};
use roc_types::subs::{VarStore, Variable};
pub struct ModuleOutput {
pub aliases: MutMap<Symbol, NodeId<Alias>>,
pub rigid_variables: MutMap<Variable, Lowercase>,
pub declarations: Vec<Declaration>,
pub exposed_imports: MutMap<Symbol, Variable>,
pub lookups: Vec<(Symbol, Variable, Region)>,
pub problems: Vec<Problem>,
pub ident_ids: IdentIds,
pub references: MutSet<Symbol>,
}
// TODO trim these down
#[allow(clippy::too_many_arguments)]
pub fn canonicalize_module_defs<'a>(
arena: &Bump,
loc_defs: &'a [Located<ast::Def<'a>>],
home: ModuleId,
module_ids: &ModuleIds,
exposed_ident_ids: IdentIds,
dep_idents: MutMap<ModuleId, IdentIds>,
aliases: MutMap<Symbol, Alias>,
exposed_imports: MutMap<Ident, (Symbol, Region)>,
mut exposed_symbols: MutSet<Symbol>,
var_store: &mut VarStore,
) -> Result<ModuleOutput, RuntimeError> {
let mut pool = Pool::with_capacity(1 << 10);
let mut can_exposed_imports = MutMap::default();
let mut scope = Scope::new(home, &mut pool, var_store);
let num_deps = dep_idents.len();
for (name, alias) in aliases.into_iter() {
let vars = PoolVec::with_capacity(alias.targs.len() as u32, &mut pool);
for (node_id, targ_id) in vars.iter_node_ids().zip(alias.targs.iter_node_ids()) {
let (poolstr, var) = &pool[targ_id];
pool[node_id] = (poolstr.shallow_clone(), *var);
}
scope.add_alias(&mut pool, name, vars, alias.actual);
}
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
// If we did this *during* canonicalization, then each time we
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let mut desugared =
bumpalo::collections::Vec::with_capacity_in(loc_defs.len() + num_deps, arena);
for loc_def in loc_defs.iter() {
desugared.push(&*arena.alloc(Located {
value: desugar_def(arena, &loc_def.value),
region: loc_def.region,
}));
}
let mut env = Env::new(
home,
arena,
&mut pool,
var_store,
dep_idents,
module_ids,
exposed_ident_ids,
);
let mut lookups = Vec::with_capacity(num_deps);
let rigid_variables = MutMap::default();
// Exposed values are treated like defs that appear before any others, e.g.
//
// imports [ Foo.{ bar, baz } ]
//
// ...is basically the same as if we'd added these extra defs at the start of the module:
//
// bar = Foo.bar
// baz = Foo.baz
//
// Here we essentially add those "defs" to "the beginning of the module"
// by canonicalizing them right before we canonicalize the actual ast::Def nodes.
for (ident, (symbol, region)) in exposed_imports {
let first_char = ident.as_inline_str().chars().next().unwrap();
if first_char.is_lowercase() {
// this is a value definition
let expr_var = env.var_store.fresh();
match scope.import(ident, symbol, region) {
Ok(()) => {
// Add an entry to exposed_imports using the current module's name
// as the key; e.g. if this is the Foo module and we have
// exposes [ Bar.{ baz } ] then insert Foo.baz as the key, so when
// anything references `baz` in this Foo module, it will resolve to Bar.baz.
can_exposed_imports.insert(symbol, expr_var);
// This will be used during constraint generation,
// to add the usual Lookup constraint as if this were a normal def.
lookups.push((symbol, expr_var, region));
}
Err((_shadowed_symbol, _region)) => {
panic!("TODO gracefully handle shadowing in imports.")
}
}
} else {
// This is a type alias
// the should already be added to the scope when this module is canonicalized
debug_assert!(scope.contains_alias(symbol));
}
}
let (defs, _scope, output, symbols_introduced) = canonicalize_defs(
&mut env,
Output::default(),
&scope,
&desugared,
PatternType::TopLevelDef,
);
// See if any of the new idents we defined went unused.
// If any were unused and also not exposed, report it.
for (symbol, region) in symbols_introduced {
if !output.references.has_lookup(symbol) && !exposed_symbols.contains(&symbol) {
env.problem(Problem::UnusedDef(symbol, region));
}
}
// TODO register rigids
// for (var, lowercase) in output.introduced_variables.name_by_var.clone() {
// rigid_variables.insert(var, lowercase);
// }
let mut references = MutSet::default();
// Gather up all the symbols that were referenced across all the defs' lookups.
for symbol in output.references.lookups.iter() {
references.insert(*symbol);
}
// Gather up all the symbols that were referenced across all the defs' calls.
for symbol in output.references.calls.iter() {
references.insert(*symbol);
}
// Gather up all the symbols that were referenced from other modules.
for symbol in env.qualified_lookups.iter() {
references.insert(*symbol);
}
// NOTE previously we inserted builtin defs into the list of defs here
// this is now done later, in file.rs.
match sort_can_defs(&mut env, defs, Output::default()) {
(Ok(mut declarations), output) => {
use crate::def::Declaration::*;
for decl in declarations.iter() {
match decl {
Declare(def) => {
for symbol in def.symbols(env.pool) {
if exposed_symbols.contains(&symbol) {
// Remove this from exposed_symbols,
// so that at the end of the process,
// we can see if there were any
// exposed symbols which did not have
// corresponding defs.
exposed_symbols.remove(&symbol);
}
}
}
DeclareRec(defs) => {
for def in defs {
for symbol in def.symbols(env.pool) {
if exposed_symbols.contains(&symbol) {
// Remove this from exposed_symbols,
// so that at the end of the process,
// we can see if there were any
// exposed symbols which did not have
// corresponding defs.
exposed_symbols.remove(&symbol);
}
}
}
}
InvalidCycle(identifiers, _) => {
panic!("TODO gracefully handle potentially attempting to expose invalid cyclic defs {:?}" , identifiers);
}
Builtin(def) => {
// Builtins cannot be exposed in module declarations.
// This should never happen!
debug_assert!(def
.symbols(env.pool)
.iter()
.all(|symbol| !exposed_symbols.contains(symbol)));
}
}
}
let mut aliases = MutMap::default();
for (symbol, alias) in output.aliases {
// Remove this from exposed_symbols,
// so that at the end of the process,
// we can see if there were any
// exposed symbols which did not have
// corresponding defs.
exposed_symbols.remove(&symbol);
aliases.insert(symbol, alias);
}
// By this point, all exposed symbols should have been removed from
// exposed_symbols and added to exposed_vars_by_symbol. If any were
// not, that means they were declared as exposed but there was
// no actual declaration with that name!
for symbol in exposed_symbols {
env.problem(Problem::ExposedButNotDefined(symbol));
// In case this exposed value is referenced by other modules,
// create a decl for it whose implementation is a runtime error.
let mut pattern_vars = SendMap::default();
pattern_vars.insert(symbol, env.var_store.fresh());
let runtime_error = RuntimeError::ExposedButNotDefined(symbol);
let value_def = {
let pattern = env.pool.add(Pattern2::Identifier(symbol));
ValueDef {
pattern,
expr_type: None,
expr_var: env.var_store.fresh(),
}
};
let def = Def::Value(value_def);
declarations.push(Declaration::Declare(def));
}
// Incorporate any remaining output.lookups entries into references.
for symbol in output.references.lookups {
references.insert(symbol);
}
// Incorporate any remaining output.calls entries into references.
for symbol in output.references.calls {
references.insert(symbol);
}
// Gather up all the symbols that were referenced from other modules.
for symbol in env.qualified_lookups.iter() {
references.insert(*symbol);
}
// TODO find captured variables
// for declaration in declarations.iter_mut() {
// match declaration {
// Declare(def) => fix_values_captured_in_closure_def(def, &mut MutSet::default()),
// DeclareRec(defs) => {
// fix_values_captured_in_closure_defs(defs, &mut MutSet::default())
// }
// InvalidCycle(_, _) | Builtin(_) => {}
// }
// }
// TODO this loops over all symbols in the module, we can speed it up by having an
// iterator over all builtin symbols
// TODO move over the builtins
// for symbol in references.iter() {
// if symbol.is_builtin() {
// // this can fail when the symbol is for builtin types, or has no implementation yet
// if let Some(def) = builtins::builtin_defs_map(*symbol, var_store) {
// declarations.push(Declaration::Builtin(def));
// }
// }
// }
Ok(ModuleOutput {
aliases,
rigid_variables,
declarations,
references,
exposed_imports: can_exposed_imports,
problems: vec![], // TODO env.problems,
lookups,
ident_ids: env.ident_ids,
})
}
(Err(runtime_error), _) => Err(runtime_error),
}
}