mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge remote-tracking branch 'origin/trunk' into editor-ir
This commit is contained in:
commit
66748d3a50
250 changed files with 37730 additions and 10808 deletions
|
@ -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,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue