Fix two canonicalization crashes: try() and overflowed tuple indexes

This commit is contained in:
Joshua Warner 2025-01-11 13:51:30 -08:00
parent 7d464a2989
commit e0ef01fa82
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
11 changed files with 114 additions and 16 deletions

View file

@ -1003,7 +1003,16 @@ pub fn desugar_expr<'a>(
},
loc_args,
) => {
let result_expr = if loc_args.len() == 1 {
let result_expr = if loc_args.is_empty() {
env.problem(Problem::UnderAppliedTry {
region: loc_expr.region,
});
// Replace with a dummy expression to avoid cascading errors
env.arena.alloc(Loc {
value: Record(Collection::empty()),
region: loc_expr.region,
})
} else if loc_args.len() == 1 {
desugar_expr(env, scope, loc_args.items[0])
} else {
let function = desugar_expr(env, scope, loc_args.items.first().unwrap());

View file

@ -1268,21 +1268,9 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::AccessorFunction(field) => (
RecordAccessor(StructAccessorData {
name: scope.gen_unique_symbol(),
function_var: var_store.fresh(),
record_var: var_store.fresh(),
ext_var: var_store.fresh(),
closure_var: var_store.fresh(),
field_var: var_store.fresh(),
field: match field {
Accessor::RecordField(field) => IndexOrField::Field((*field).into()),
Accessor::TupleIndex(index) => IndexOrField::Index(index.parse().unwrap()),
},
}),
Output::default(),
),
ast::Expr::AccessorFunction(field) => {
canonicalize_accessor_function(env, var_store, scope, field, region)
}
ast::Expr::TupleAccess(tuple_expr, field) => {
let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, tuple_expr);
@ -1656,6 +1644,37 @@ pub fn canonicalize_expr<'a>(
)
}
fn canonicalize_accessor_function(
env: &mut Env<'_>,
var_store: &mut VarStore,
scope: &mut Scope,
field: &Accessor<'_>,
region: Region,
) -> (Expr, Output) {
(
Expr::RecordAccessor(StructAccessorData {
name: scope.gen_unique_symbol(),
function_var: var_store.fresh(),
record_var: var_store.fresh(),
ext_var: var_store.fresh(),
closure_var: var_store.fresh(),
field_var: var_store.fresh(),
field: match field {
Accessor::RecordField(field) => IndexOrField::Field((*field).into()),
Accessor::TupleIndex(index) => match index.parse() {
Ok(index) => IndexOrField::Index(index),
Err(_) => {
let error = roc_problem::can::RuntimeError::InvalidTupleIndex(region);
env.problem(Problem::RuntimeError(error.clone()));
return (Expr::RuntimeError(error), Output::default());
}
},
},
}),
Output::default(),
)
}
pub fn canonicalize_record<'a>(
env: &mut Env<'a>,
var_store: &mut VarStore,

View file

@ -225,6 +225,9 @@ pub enum Problem {
OverAppliedDbg {
region: Region,
},
UnderAppliedTry {
region: Region,
},
FileProblem {
filename: PathBuf,
error: io::ErrorKind,
@ -339,6 +342,7 @@ impl Problem {
Problem::OverAppliedCrash { .. } => RuntimeError,
Problem::UnappliedDbg { .. } => RuntimeError,
Problem::OverAppliedDbg { .. } => RuntimeError,
Problem::UnderAppliedTry { .. } => Warning,
Problem::DefsOnlyUsedInRecursion(_, _) => Warning,
Problem::FileProblem { .. } => Fatal,
Problem::ReturnOutsideOfFunction { .. } => Warning,
@ -467,6 +471,7 @@ impl Problem {
| Problem::UnappliedCrash { region }
| Problem::OverAppliedDbg { region }
| Problem::UnappliedDbg { region }
| Problem::UnderAppliedTry { region }
| Problem::DefsOnlyUsedInRecursion(_, region)
| Problem::ReturnOutsideOfFunction { region, .. }
| Problem::StatementsAfterReturn { region }
@ -681,6 +686,7 @@ pub enum RuntimeError {
},
NonFunctionHostedAnnotation(Region),
InvalidTupleIndex(Region),
}
impl RuntimeError {
@ -718,6 +724,7 @@ impl RuntimeError {
| RuntimeError::InvalidFloat(_, region, _)
| RuntimeError::InvalidInt(_, _, region, _)
| RuntimeError::EmptySingleQuote(region)
| RuntimeError::InvalidTupleIndex(region)
| RuntimeError::MultipleCharsInSingleQuote(region)
| RuntimeError::DegenerateBranch(region)
| RuntimeError::InvalidInterpolation(region)

View file

@ -0,0 +1,18 @@
@0-6 SpaceAfter(
Apply(
@0-5 PncApply(
@0-3 Try,
[],
),
[
@5-6 Var {
module_name: "",
ident: "t",
},
],
Space,
),
[
Newline,
],
)

View file

@ -0,0 +1 @@
.18888888888888888888

View file

@ -0,0 +1,10 @@
@0-21 SpaceAfter(
AccessorFunction(
TupleIndex(
"18888888888888888888",
),
),
[
Newline,
],
)

View file

@ -0,0 +1 @@
.18888888888888888888

View file

@ -436,6 +436,7 @@ mod test_snapshots {
pass/empty_record_newline_assign.expr,
pass/empty_record_update.expr,
pass/empty_string.expr,
pass/empty_try_pnc.expr,
pass/equals.expr,
pass/equals_with_spaces.expr,
pass/expect.expr,
@ -485,6 +486,7 @@ mod test_snapshots {
pass/int_with_underscore.expr,
pass/lambda_in_chain.expr,
pass/lambda_indent.expr,
pass/large_tuple_index.expr,
pass/list_closing_indent_not_enough.expr,
pass/list_closing_same_indent_no_trailing_comma.expr,
pass/list_closing_same_indent_with_trailing_comma.expr,

View file

@ -1338,6 +1338,21 @@ pub fn can_problem<'b>(
]);
title = "OVERAPPLIED DBG".to_string();
}
Problem::UnderAppliedTry { region } => {
doc = alloc.stack([
alloc.concat([
alloc.reflow("This "),
alloc.keyword("try"),
alloc.reflow(" has too few values given to it:"),
]),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.keyword("try"),
alloc.reflow(" must be given exactly one value to try."),
]),
]);
title = "UNDERAPPLIED TRY".to_string();
}
Problem::FileProblem { filename, error } => {
let report = to_file_problem_report(alloc, filename, error);
doc = report.doc;
@ -2480,6 +2495,20 @@ fn pretty_runtime_error<'b>(
title = NUMBER_UNDERFLOWS_SUFFIX;
}
RuntimeError::InvalidTupleIndex(region) => {
doc = alloc.stack([
alloc.concat([alloc
.reflow("This tuple accessor index is invalid:")]),
alloc.region(lines.convert_region(region), severity),
alloc.tip().append(alloc.concat([
alloc.reflow("Note that .2 in Roc is a function that extracts the tuple element at index 2 from its argument."),
alloc.reflow("If you mean to create a floating point number, make sure it starts with a digit like 0.2"),
alloc.reflow("."),
])),
]);
title = NUMBER_UNDERFLOWS_SUFFIX;
}
RuntimeError::InvalidOptionalValue {
field_name,
field_region,