first draft about invalid optional fields in records

This commit is contained in:
Sébastien Besnier 2020-11-03 21:28:50 +01:00
parent 2fb803cdd0
commit aaff5a5dce
3 changed files with 70 additions and 46 deletions

View file

@ -207,19 +207,24 @@ pub fn canonicalize_expr<'a>(
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(CanonicalizeFieldProblem::InvalidOptionalValue) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalRecord),
Output::default(),
),
}
} else {
// only (optionally qualified) variables can be updated, not arbitrary expressions
@ -241,16 +246,19 @@ pub fn canonicalize_expr<'a>(
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(CanonicalizeFieldProblem::InvalidOptionalValue) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalRecord),
Output::default(),
),
}
}
}
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
@ -977,35 +985,44 @@ fn canonicalize_fields<'a>(
scope: &mut Scope,
region: Region,
fields: &'a [Located<ast::AssignedField<'a, ast::Expr<'a>>>],
) -> (SendMap<Lowercase, Field>, Output) {
) -> Result<(SendMap<Lowercase, Field>, Output), CanonicalizeFieldProblem> {
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(invalid_optional_value @ CanonicalizeFieldProblem::InvalidOptionalValue) => {
env.problems.push(Problem::InvalidOptionalRecord);
return Err(invalid_optional_value);
}
}
output.references = output.references.union(field_out.references);
}
(can_fields, output)
Ok((can_fields, output))
}
enum CanonicalizeFieldProblem {
InvalidOptionalValue,
}
fn canonicalize_field<'a>(
@ -1014,7 +1031,7 @@ fn canonicalize_field<'a>(
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 {
@ -1024,17 +1041,15 @@ 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(_, _, _) => Err(CanonicalizeFieldProblem::InvalidOptionalValue),
// A label with no value, e.g. `{ name }` (this is sugar for { name: name })
LabelOnly(_) => {