mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
first draft about invalid optional fields in records
This commit is contained in:
parent
2fb803cdd0
commit
aaff5a5dce
3 changed files with 70 additions and 46 deletions
|
@ -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(_) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue