Merge remote-tracking branch 'origin/trunk' into editor-ir

This commit is contained in:
Folkert 2020-12-04 20:45:30 +01:00
commit 66748d3a50
250 changed files with 37730 additions and 10808 deletions

View file

@ -11,7 +11,7 @@ use crate::procedure::References;
use crate::scope::Scope;
use inlinable_string::InlinableString;
use roc_collections::all::{ImSet, MutMap, MutSet, SendMap};
use roc_module::ident::{Lowercase, TagName};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::operator::CalledVia;
use roc_module::symbol::Symbol;
@ -83,8 +83,8 @@ pub enum Expr {
},
// Let
LetRec(Vec<Def>, Box<Located<Expr>>, Variable, Aliases),
LetNonRec(Box<Def>, Box<Located<Expr>>, Variable, Aliases),
LetRec(Vec<Def>, Box<Located<Expr>>, Variable),
LetNonRec(Box<Def>, Box<Located<Expr>>, Variable),
/// This is *only* for calling functions, not for tag application.
/// The Tag variant contains any applied values inside it.
@ -98,6 +98,11 @@ pub enum Expr {
args: Vec<(Variable, Expr)>,
ret_var: Variable,
},
ForeignCall {
foreign_symbol: ForeignSymbol,
args: Vec<(Variable, Expr)>,
ret_var: Variable,
},
Closure {
function_type: Variable,
@ -157,8 +162,6 @@ pub enum Expr {
RuntimeError(RuntimeError),
}
type Aliases = SendMap<Symbol, Alias>;
#[derive(Clone, Debug, PartialEq)]
pub struct Field {
pub var: Variable,
@ -205,23 +208,37 @@ pub fn canonicalize_expr<'a>(
ast::Expr::Record {
fields,
update: Some(loc_update),
final_comments: _,
} => {
let (can_update, update_out) =
canonicalize_expr(env, var_store, scope, loc_update.region, &loc_update.value);
if let Var(symbol) = &can_update.value {
let (can_fields, mut output) =
canonicalize_fields(env, var_store, scope, region, fields);
match canonicalize_fields(env, var_store, scope, region, fields) {
Ok((can_fields, mut output)) => {
output.references = output.references.union(update_out.references);
output.references = output.references.union(update_out.references);
let answer = Update {
record_var: var_store.fresh(),
ext_var: var_store.fresh(),
symbol: *symbol,
updates: can_fields,
};
let answer = Update {
record_var: var_store.fresh(),
ext_var: var_store.fresh(),
symbol: *symbol,
updates: can_fields,
};
(answer, output)
(answer, output)
}
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
field_name,
field_region,
record_region,
}) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
field_name,
field_region,
record_region,
}),
Output::default(),
),
}
} else {
// only (optionally qualified) variables can be updated, not arbitrary expressions
@ -239,20 +256,32 @@ pub fn canonicalize_expr<'a>(
ast::Expr::Record {
fields,
update: None,
final_comments: _,
} => {
if fields.is_empty() {
(EmptyRecord, Output::default())
} else {
let (can_fields, output) =
canonicalize_fields(env, var_store, scope, region, fields);
(
Record {
record_var: var_store.fresh(),
fields: can_fields,
},
output,
)
match canonicalize_fields(env, var_store, scope, region, fields) {
Ok((can_fields, output)) => (
Record {
record_var: var_store.fresh(),
fields: can_fields,
},
output,
),
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
field_name,
field_region,
record_region,
}) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
field_name,
field_region,
record_region,
}),
Output::default(),
),
}
}
}
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
@ -309,7 +338,7 @@ pub fn canonicalize_expr<'a>(
let mut args = Vec::new();
let mut outputs = Vec::new();
for loc_arg in loc_args {
for loc_arg in loc_args.iter() {
let (arg_expr, arg_out) =
canonicalize_expr(env, var_store, scope, loc_arg.region, &loc_arg.value);
@ -320,6 +349,10 @@ pub fn canonicalize_expr<'a>(
// Default: We're not tail-calling a symbol (by name), we're tail-calling a function value.
output.tail_call = None;
for arg_out in outputs {
output.references = output.references.union(arg_out.references);
}
let expr = match fn_expr.value {
Var(symbol) => {
output.references.calls.insert(symbol);
@ -371,10 +404,6 @@ pub fn canonicalize_expr<'a>(
}
};
for arg_out in outputs {
output.references = output.references.union(arg_out.references);
}
(expr, output)
}
ast::Expr::Var { module_name, ident } => {
@ -450,7 +479,7 @@ pub fn canonicalize_expr<'a>(
let mut bound_by_argument_patterns = MutSet::default();
for loc_pattern in loc_arg_patterns.into_iter() {
for loc_pattern in loc_arg_patterns.iter() {
let (new_output, can_arg) = canonicalize_pattern(
env,
var_store,
@ -562,7 +591,7 @@ pub fn canonicalize_expr<'a>(
let mut can_branches = Vec::with_capacity(branches.len());
for branch in branches {
for branch in branches.iter() {
let (can_when_branch, branch_references) =
canonicalize_when_branch(env, var_store, scope, region, *branch, &mut output);
@ -698,10 +727,11 @@ pub fn canonicalize_expr<'a>(
}
ast::Expr::MalformedIdent(name) => {
use roc_problem::can::RuntimeError::*;
(
RuntimeError(MalformedIdentifier((*name).into(), region)),
Output::default(),
)
let problem = MalformedIdentifier((*name).into(), region);
env.problem(Problem::RuntimeError(problem.clone()));
(RuntimeError(problem), Output::default())
}
ast::Expr::Nested(sub_expr) => {
let (answer, output) = canonicalize_expr(env, var_store, scope, region, sub_expr);
@ -721,34 +751,34 @@ pub fn canonicalize_expr<'a>(
}
// Below this point, we shouln't see any of these nodes anymore because
// operator desugaring should have removed them!
ast::Expr::ParensAround(sub_expr) => {
bad_expr @ ast::Expr::ParensAround(_) => {
panic!(
"A ParensAround did not get removed during operator desugaring somehow: {:?}",
sub_expr
"A ParensAround did not get removed during operator desugaring somehow: {:#?}",
bad_expr
);
}
ast::Expr::SpaceBefore(sub_expr, _spaces) => {
bad_expr @ ast::Expr::SpaceBefore(_, _) => {
panic!(
"A SpaceBefore did not get removed during operator desugaring somehow: {:?}",
sub_expr
"A SpaceBefore did not get removed during operator desugaring somehow: {:#?}",
bad_expr
);
}
ast::Expr::SpaceAfter(sub_expr, _spaces) => {
bad_expr @ ast::Expr::SpaceAfter(_, _) => {
panic!(
"A SpaceAfter did not get removed during operator desugaring somehow: {:?}",
sub_expr
"A SpaceAfter did not get removed during operator desugaring somehow: {:#?}",
bad_expr
);
}
ast::Expr::BinOp((_, loc_op, _)) => {
bad_expr @ ast::Expr::BinOp(_) => {
panic!(
"A binary operator did not get desugared somehow: {:?}",
loc_op
"A binary operator did not get desugared somehow: {:#?}",
bad_expr
);
}
ast::Expr::UnaryOp(_, loc_op) => {
bad_expr @ ast::Expr::UnaryOp(_, _) => {
panic!(
"A unary operator did not get desugared somehow: {:?}",
loc_op
"A unary operator did not get desugared somehow: {:#?}",
bad_expr
);
}
};
@ -788,7 +818,7 @@ fn canonicalize_when_branch<'a>(
let mut scope = original_scope.clone();
// TODO report symbols not bound in all patterns
for loc_pattern in &branch.patterns {
for loc_pattern in branch.patterns.iter() {
let (new_output, can_pattern) = canonicalize_pattern(
env,
var_store,
@ -973,50 +1003,79 @@ where
}
}
enum CanonicalizeRecordProblem {
InvalidOptionalValue {
field_name: Lowercase,
field_region: Region,
record_region: Region,
},
}
fn canonicalize_fields<'a>(
env: &mut Env<'a>,
var_store: &mut VarStore,
scope: &mut Scope,
region: Region,
fields: &'a [Located<ast::AssignedField<'a, ast::Expr<'a>>>],
) -> (SendMap<Lowercase, Field>, Output) {
) -> Result<(SendMap<Lowercase, Field>, Output), CanonicalizeRecordProblem> {
let mut can_fields = SendMap::default();
let mut output = Output::default();
for loc_field in fields.iter() {
let (label, field_expr, field_out, field_var) =
canonicalize_field(env, var_store, scope, &loc_field.value, loc_field.region);
match canonicalize_field(env, var_store, scope, &loc_field.value, loc_field.region) {
Ok((label, field_expr, field_out, field_var)) => {
let field = Field {
var: field_var,
region: loc_field.region,
loc_expr: Box::new(field_expr),
};
let field = Field {
var: field_var,
region: loc_field.region,
loc_expr: Box::new(field_expr),
};
let replaced = can_fields.insert(label.clone(), field);
let replaced = can_fields.insert(label.clone(), field);
if let Some(old) = replaced {
env.problems.push(Problem::DuplicateRecordFieldValue {
field_name: label,
field_region: loc_field.region,
record_region: region,
replaced_region: old.region,
});
}
if let Some(old) = replaced {
env.problems.push(Problem::DuplicateRecordFieldValue {
field_name: label,
field_region: loc_field.region,
record_region: region,
replaced_region: old.region,
});
output.references = output.references.union(field_out.references);
}
Err(CanonicalizeFieldProblem::InvalidOptionalValue {
field_name,
field_region,
}) => {
env.problems.push(Problem::InvalidOptionalValue {
field_name: field_name.clone(),
field_region,
record_region: region,
});
return Err(CanonicalizeRecordProblem::InvalidOptionalValue {
field_name,
field_region,
record_region: region,
});
}
}
output.references = output.references.union(field_out.references);
}
(can_fields, output)
Ok((can_fields, output))
}
enum CanonicalizeFieldProblem {
InvalidOptionalValue {
field_name: Lowercase,
field_region: Region,
},
}
fn canonicalize_field<'a>(
env: &mut Env<'a>,
var_store: &mut VarStore,
scope: &mut Scope,
field: &'a ast::AssignedField<'a, ast::Expr<'a>>,
region: Region,
) -> (Lowercase, Located<Expr>, Output, Variable) {
) -> Result<(Lowercase, Located<Expr>, Output, Variable), CanonicalizeFieldProblem> {
use roc_parse::ast::AssignedField::*;
match field {
@ -1026,17 +1085,18 @@ fn canonicalize_field<'a>(
let (loc_can_expr, output) =
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
(
Ok((
Lowercase::from(label.value),
loc_can_expr,
output,
field_var,
)
))
}
OptionalValue(_, _, _) => {
todo!("TODO gracefully handle an optional field being used in an Expr");
}
OptionalValue(label, _, loc_expr) => Err(CanonicalizeFieldProblem::InvalidOptionalValue {
field_name: Lowercase::from(label.value),
field_region: Region::span_across(&label.region, &loc_expr.region),
}),
// A label with no value, e.g. `{ name }` (this is sugar for { name: name })
LabelOnly(_) => {
@ -1118,7 +1178,8 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
| other @ Accessor { .. }
| other @ Update { .. }
| other @ Var(_)
| other @ RunLowLevel { .. } => other,
| other @ RunLowLevel { .. }
| other @ ForeignCall { .. } => other,
List {
list_var,
@ -1221,7 +1282,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
}
}
LetRec(defs, loc_expr, var, aliases) => {
LetRec(defs, loc_expr, var) => {
let mut new_defs = Vec::with_capacity(defs.len());
for def in defs {
@ -1242,10 +1303,10 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
value: inline_calls(var_store, scope, loc_expr.value),
};
LetRec(new_defs, Box::new(loc_expr), var, aliases)
LetRec(new_defs, Box::new(loc_expr), var)
}
LetNonRec(def, loc_expr, var, aliases) => {
LetNonRec(def, loc_expr, var) => {
let def = Def {
loc_pattern: def.loc_pattern,
loc_expr: Located {
@ -1262,7 +1323,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
value: inline_calls(var_store, scope, loc_expr.value),
};
LetNonRec(Box::new(def), Box::new(loc_expr), var, aliases)
LetNonRec(Box::new(def), Box::new(loc_expr), var)
}
Closure {
@ -1367,9 +1428,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
// Not sure if param_var should be involved.
let pattern_vars = SendMap::default();
// TODO get the actual correct aliases
let aliases = SendMap::default();
let def = Def {
loc_pattern,
loc_expr,
@ -1384,7 +1442,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
Box::new(def),
Box::new(loc_answer),
var_store.fresh(),
aliases,
),
};
}