From ef4eeb5c1af1411a6e59a277e00a0e1c8e0a014e Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 17 Sep 2024 20:41:14 -0300 Subject: [PATCH 01/73] Parse effectful arrow in function annotations --- crates/compiler/can/src/annotation.rs | 5 +- crates/compiler/fmt/src/annotation.rs | 5 +- crates/compiler/load_internal/src/docs.rs | 6 +- crates/compiler/parse/src/ast.rs | 18 +++- crates/compiler/parse/src/normalize.rs | 3 +- crates/compiler/parse/src/type_annotation.rs | 20 ++-- ...and_signature_is_multiline.expr.result-ast | 1 + .../pass/ability_multi_line.expr.result-ast | 2 + .../pass/ability_single_line.expr.result-ast | 1 + .../pass/ability_two_in_a_row.expr.result-ast | 2 + .../pass/ann_effectful_fn.expr.formatted.roc | 5 + .../pass/ann_effectful_fn.expr.result-ast | 94 +++++++++++++++++++ .../snapshots/pass/ann_effectful_fn.expr.roc | 5 + .../pass/fn_with_record_arg.expr.result-ast | 1 + ...nction_with_tuple_ext_type.expr.result-ast | 1 + .../function_with_tuple_type.expr.result-ast | 1 + ...ested_def_annotation.moduledefs.result-ast | 1 + .../record_func_type_decl.expr.result-ast | 1 + .../record_type_with_function.expr.result-ast | 3 + .../snapshots/pass/tuple_type.expr.result-ast | 1 + .../pass/tuple_type_ext.expr.result-ast | 1 + .../type_decl_with_underscore.expr.result-ast | 1 + ...ype_signature_function_def.expr.result-ast | 1 + .../where_clause_function.expr.result-ast | 2 + ...e_multiple_bound_abilities.expr.result-ast | 2 + .../where_clause_multiple_has.expr.result-ast | 2 + ...ltiple_has_across_newlines.expr.result-ast | 2 + .../where_clause_on_newline.expr.result-ast | 1 + .../test_syntax/tests/test_snapshots.rs | 1 + crates/language_server/src/analysis/tokens.rs | 8 +- 30 files changed, 178 insertions(+), 19 deletions(-) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.roc diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 5279f9f7a9d..5dbe152c99e 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -448,7 +448,7 @@ pub fn find_type_def_symbols( stack.push(&t.value); } } - Function(arguments, result) => { + Function(arguments, _arrow, result) => { for t in arguments.iter() { stack.push(&t.value); } @@ -554,7 +554,8 @@ fn can_annotation_help( use roc_parse::ast::TypeAnnotation::*; match annotation { - Function(argument_types, return_type) => { + Function(argument_types, _arrow, return_type) => { + // [purity-inference] TODO: arrow let mut args = Vec::new(); for arg in *argument_types { diff --git a/crates/compiler/fmt/src/annotation.rs b/crates/compiler/fmt/src/annotation.rs index 16efa21795e..d10fb2509e1 100644 --- a/crates/compiler/fmt/src/annotation.rs +++ b/crates/compiler/fmt/src/annotation.rs @@ -149,7 +149,7 @@ impl<'a> Formattable for TypeAnnotation<'a> { } Wildcard | Inferred | BoundVariable(_) | Malformed(_) => false, - Function(args, result) => { + Function(args, _arrow, result) => { result.value.is_multiline() || args.iter().any(|loc_arg| loc_arg.value.is_multiline()) } @@ -195,7 +195,8 @@ impl<'a> Formattable for TypeAnnotation<'a> { let self_is_multiline = self.is_multiline(); match self { - Function(args, ret) => { + Function(args, _arrow, ret) => { + // [purity-infrence] TODO: format arrow let needs_parens = parens != Parens::NotNeeded; buf.indent(indent); diff --git a/crates/compiler/load_internal/src/docs.rs b/crates/compiler/load_internal/src/docs.rs index 3b4e3c8362b..b7ebbe9da43 100644 --- a/crates/compiler/load_internal/src/docs.rs +++ b/crates/compiler/load_internal/src/docs.rs @@ -443,7 +443,8 @@ fn contains_unexposed_type( Malformed(_) | Inferred | Wildcard | BoundVariable(_) => false, - Function(loc_args, loc_ret) => { + Function(loc_args, _arrow, loc_ret) => { + // [purity-inference] TODO: arrow let loc_args_contains_unexposed_type = loc_args.iter().any(|loc_arg| { contains_unexposed_type(&loc_arg.value, exposed_module_ids, module_ids) }); @@ -611,7 +612,8 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> ast::TypeAnnotation::SpaceAfter(&sub_type_ann, _) => { type_to_docs(in_func_type_ann, sub_type_ann) } - ast::TypeAnnotation::Function(ast_arg_anns, output_ann) => { + ast::TypeAnnotation::Function(ast_arg_anns, _arrow, output_ann) => { + // [purity-inference] TODO: arrow let mut doc_arg_anns = Vec::new(); for ast_arg_ann in ast_arg_anns { diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 2b2ab7631d6..b2d89990439 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -1530,10 +1530,22 @@ impl ImplementsAbilities<'_> { } } +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum FunctionArrow { + /// -> + Pure, + /// => + Effectful, +} + #[derive(Debug, Copy, Clone, PartialEq)] pub enum TypeAnnotation<'a> { - /// A function. The types of its arguments, then the type of its return value. - Function(&'a [Loc>], &'a Loc>), + /// A function. The types of its arguments, the type of arrow used, then the type of its return value. + Function( + &'a [Loc>], + FunctionArrow, + &'a Loc>, + ), /// Applying a type to some arguments (e.g. Map.Map String Int) Apply(&'a str, &'a str, &'a [Loc>]), @@ -2755,7 +2767,7 @@ impl<'a> Malformed for ModuleImportParams<'a> { impl<'a> Malformed for TypeAnnotation<'a> { fn is_malformed(&self) -> bool { match self { - TypeAnnotation::Function(args, ret) => { + TypeAnnotation::Function(args, _arrow, ret) => { args.iter().any(|arg| arg.is_malformed()) || ret.is_malformed() } TypeAnnotation::Apply(_, _, args) => args.iter().any(|arg| arg.is_malformed()), diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index a52982d6266..f0b4e43387e 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -881,8 +881,9 @@ impl<'a> Normalize<'a> for Pattern<'a> { impl<'a> Normalize<'a> for TypeAnnotation<'a> { fn normalize(&self, arena: &'a Bump) -> Self { match *self { - TypeAnnotation::Function(a, b) => TypeAnnotation::Function( + TypeAnnotation::Function(a, arrow, b) => TypeAnnotation::Function( arena.alloc(a.normalize(arena)), + arrow, arena.alloc(b.normalize(arena)), ), TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.normalize(arena)), diff --git a/crates/compiler/parse/src/type_annotation.rs b/crates/compiler/parse/src/type_annotation.rs index 03714e8ea77..2a978442a45 100644 --- a/crates/compiler/parse/src/type_annotation.rs +++ b/crates/compiler/parse/src/type_annotation.rs @@ -1,6 +1,7 @@ use crate::ast::{ - AbilityImpls, AssignedField, CommentOrNewline, Expr, ImplementsAbilities, ImplementsAbility, - ImplementsClause, Pattern, Spaceable, Spaced, Tag, TypeAnnotation, TypeHeader, + AbilityImpls, AssignedField, CommentOrNewline, Expr, FunctionArrow, ImplementsAbilities, + ImplementsAbility, ImplementsClause, Pattern, Spaceable, Spaced, Tag, TypeAnnotation, + TypeHeader, }; use crate::blankspace::{ space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, @@ -594,16 +595,23 @@ fn expression<'a>( ], )) .trace("type_annotation:expression:rest_args"), - skip_second( + and( space0_e(EType::TIndentStart), - two_bytes(b'-', b'>', EType::TStart), + one_of![ + map(two_bytes(b'-', b'>', EType::TStart), |_| { + FunctionArrow::Pure + }), + map(two_bytes(b'=', b'>', EType::TStart), |_| { + FunctionArrow::Effectful + }), + ], ) .trace("type_annotation:expression:arrow"), ) .parse(arena, state.clone(), min_indent); let (progress, annot, state) = match result { - Ok((p2, (rest, space_before_arrow), state)) => { + Ok((p2, (rest, (space_before_arrow, arrow)), state)) => { let (p3, return_type, state) = space0_before_e(term(stop_at_surface_has), EType::TIndentStart) .parse(arena, state, min_indent)?; @@ -626,7 +634,7 @@ fn expression<'a>( let result = Loc { region, - value: TypeAnnotation::Function(output, arena.alloc(return_type)), + value: TypeAnnotation::Function(output, arrow, arena.alloc(return_type)), }; let progress = p1.or(p2).or(p3); (progress, result, state) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast index 1bfe9896642..ede6aa85bba 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast @@ -40,6 +40,7 @@ SpaceAfter( ], ), ], + Pure, @40-43 Apply( "", "U64", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast index b5b139003dc..1cb962fc217 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast @@ -35,6 +35,7 @@ SpaceAfter( "a", ), ], + Pure, @30-33 Apply( "", "U64", @@ -55,6 +56,7 @@ SpaceAfter( "a", ), ], + Pure, @49-52 Apply( "", "U64", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast index b89c485a73d..017412f9d84 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast @@ -31,6 +31,7 @@ SpaceAfter( "a", ), ], + Pure, @28-31 Apply( "", "U64", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast index 1d8404edd54..7e21cc2acb0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast @@ -38,6 +38,7 @@ SpaceAfter( "a", ), ], + Pure, @26-28 Record { fields: [], ext: None, @@ -75,6 +76,7 @@ SpaceAfter( "a", ), ], + Pure, @79-81 Record { fields: [], ext: None, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc new file mode 100644 index 00000000000..4f425f46c3f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc @@ -0,0 +1,5 @@ +launchTheNukes : {} -> Result Bool LaunchNukeErr +launchTheNukes = \{} -> + crash "todo" + +launchTheNukes \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast new file mode 100644 index 00000000000..54d323f80ec --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast @@ -0,0 +1,94 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-89, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-14 Identifier { + ident: "launchTheNukes", + }, + ann_type: @17-48 Function( + [ + @17-19 Record { + fields: [], + ext: None, + }, + ], + Effectful, + @23-48 Apply( + "", + "Result", + [ + @30-34 Apply( + "", + "Bool", + [], + ), + @35-48 Apply( + "", + "LaunchNukeErr", + [], + ), + ], + ), + ), + lines_between: [ + Newline, + ], + body_pattern: @49-63 Identifier { + ident: "launchTheNukes", + }, + body_expr: @66-89 Closure( + [ + @67-69 RecordDestructure( + [], + ), + ], + @77-89 SpaceBefore( + Apply( + @77-82 Crash, + [ + @83-89 Str( + PlainLine( + "todo", + ), + ), + ], + Space, + ), + [ + Newline, + ], + ), + ), + }, + ], + }, + @91-105 SpaceBefore( + Var { + module_name: "", + ident: "launchTheNukes", + }, + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.roc new file mode 100644 index 00000000000..38183db57c4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.roc @@ -0,0 +1,5 @@ +launchTheNukes : {} => Result Bool LaunchNukeErr +launchTheNukes = \{} -> + crash "todo" + +launchTheNukes diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast index b6ff5167d50..475051e8bc7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast @@ -46,6 +46,7 @@ Defs( ext: None, }, ], + Pure, @39-44 Apply( "", "Table", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast index 5c3f9583d0c..6f68ac97a24 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast @@ -37,6 +37,7 @@ SpaceAfter( ), }, ], + Pure, @14-20 Tuple { elems: [ @15-18 Apply( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast index dc9b86e1b1e..4ffd212055f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast @@ -28,6 +28,7 @@ SpaceAfter( [], ), ], + Pure, @11-21 Tuple { elems: [ @12-15 Apply( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast index 0ffa2dece14..5fc26b74584 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast @@ -51,6 +51,7 @@ Defs { "a", ), ], + Pure, @34-38 Apply( "", "Bool", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast index 0508950b866..12ba5102e74 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast @@ -55,6 +55,7 @@ SpaceAfter( [], ), ], + Pure, @65-75 Apply( "", "Effect", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast index 57571d05a87..7eaa142894c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast @@ -32,6 +32,7 @@ SpaceAfter( ext: None, }, ], + Pure, @19-24 Apply( "", "Model", @@ -55,6 +56,7 @@ SpaceAfter( [], ), ], + Pure, @49-54 Apply( "", "Model", @@ -73,6 +75,7 @@ SpaceAfter( [], ), ], + Pure, @72-75 Apply( "", "Str", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast index eb797fa52b4..07482202abb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast @@ -37,6 +37,7 @@ Defs( ext: None, }, ], + Pure, @17-27 Tuple { elems: [ @18-21 Apply( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast index b373869f67a..92c3f3e03fa 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast @@ -41,6 +41,7 @@ Defs( ), }, ], + Pure, @18-29 Tuple { elems: [ @19-22 Apply( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast index fd45c732273..f85036bbe24 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast @@ -27,6 +27,7 @@ Defs( [], ), ], + Pure, @20-30 Apply( "", "Task", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast index 977eea38919..36bcc2decbc 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast @@ -32,6 +32,7 @@ Defs( [], ), ], + Pure, @20-24 Apply( "", "Bool", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast index b23d86cd02e..9d0c6670001 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast @@ -27,12 +27,14 @@ SpaceAfter( "a", ), ], + Pure, @10-16 Function( [ @10-11 BoundVariable( "b", ), ], + Pure, @15-16 BoundVariable( "c", ), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast index 8f597fc3d38..0bc4dc2d013 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast @@ -34,6 +34,7 @@ SpaceAfter( "a", ), ], + Pure, @9-10 BoundVariable( "b", ), @@ -89,6 +90,7 @@ SpaceAfter( "a", ), ], + Pure, @84-85 BoundVariable( "b", ), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast index 4e20ee7f18c..66b7a5b3cf8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast @@ -27,12 +27,14 @@ SpaceAfter( "a", ), ], + Pure, @10-16 Function( [ @10-11 BoundVariable( "b", ), ], + Pure, @15-16 BoundVariable( "c", ), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast index 4c8ab5ea672..0a6edab08bc 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast @@ -28,12 +28,14 @@ SpaceAfter( "a", ), ], + Pure, @10-16 Function( [ @10-11 BoundVariable( "b", ), ], + Pure, @15-16 BoundVariable( "c", ), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast index 03a7c703a23..ff2dbc064e7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast @@ -28,6 +28,7 @@ SpaceAfter( "a", ), ], + Pure, @9-12 Apply( "", "U64", diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 46f8bd52e14..164fcb6d111 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -275,6 +275,7 @@ mod test_snapshots { pass/add_var_with_spaces.expr, pass/add_with_spaces.expr, pass/ann_closed_union.expr, + pass/ann_effectful_fn.expr, pass/ann_open_union.expr, pass/annotated_record_destructure.expr, pass/annotated_tag_destructure.expr, diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 600f1d1411d..3f182ebc479 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -386,9 +386,11 @@ impl IterTokens for PlatformRequires<'_> { impl IterTokens for Loc> { fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc> { match self.value { - TypeAnnotation::Function(params, ret) => (params.iter_tokens(arena).into_iter()) - .chain(ret.iter_tokens(arena)) - .collect_in(arena), + TypeAnnotation::Function(params, _arrow, ret) => { + (params.iter_tokens(arena).into_iter()) + .chain(ret.iter_tokens(arena)) + .collect_in(arena) + } TypeAnnotation::Apply(_mod, _type, args) => args.iter_tokens(arena), TypeAnnotation::BoundVariable(_) => onetoken(Token::Type, self.region, arena), TypeAnnotation::As(ty, _, as_ty) => (ty.iter_tokens(arena).into_iter()) From d692fc7c42a9b8b624182284b2018fbceb025c70 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 17 Sep 2024 20:44:44 -0300 Subject: [PATCH 02/73] Format effectful arrows in annotations --- crates/compiler/fmt/src/annotation.rs | 13 ++++++++----- .../pass/ann_effectful_fn.expr.formatted.roc | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/compiler/fmt/src/annotation.rs b/crates/compiler/fmt/src/annotation.rs index d10fb2509e1..e79e2463f95 100644 --- a/crates/compiler/fmt/src/annotation.rs +++ b/crates/compiler/fmt/src/annotation.rs @@ -4,8 +4,8 @@ use crate::{ Buf, }; use roc_parse::ast::{ - AbilityImpls, AssignedField, Collection, Expr, ExtractSpaces, ImplementsAbilities, - ImplementsAbility, ImplementsClause, Tag, TypeAnnotation, TypeHeader, + AbilityImpls, AssignedField, Collection, Expr, ExtractSpaces, FunctionArrow, + ImplementsAbilities, ImplementsAbility, ImplementsClause, Tag, TypeAnnotation, TypeHeader, }; use roc_parse::ident::UppercaseIdent; use roc_region::all::Loc; @@ -195,8 +195,7 @@ impl<'a> Formattable for TypeAnnotation<'a> { let self_is_multiline = self.is_multiline(); match self { - Function(args, _arrow, ret) => { - // [purity-infrence] TODO: format arrow + Function(args, arrow, ret) => { let needs_parens = parens != Parens::NotNeeded; buf.indent(indent); @@ -237,7 +236,11 @@ impl<'a> Formattable for TypeAnnotation<'a> { buf.spaces(1); } - buf.push_str("->"); + match arrow { + FunctionArrow::Pure => buf.push_str("->"), + FunctionArrow::Effectful => buf.push_str("=>"), + } + buf.spaces(1); ret.value diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc index 4f425f46c3f..b247885ff2f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.formatted.roc @@ -1,4 +1,4 @@ -launchTheNukes : {} -> Result Bool LaunchNukeErr +launchTheNukes : {} => Result Bool LaunchNukeErr launchTheNukes = \{} -> crash "todo" From 386a5055eed044d49fe36441b2c1f6d8a0ad5ee2 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Fri, 4 Oct 2024 20:30:56 -0300 Subject: [PATCH 03/73] Add effect_type to can ClosureData --- crates/compiler/can/src/builtins.rs | 1 + crates/compiler/can/src/copy.rs | 2 ++ crates/compiler/can/src/def.rs | 1 + crates/compiler/can/src/expr.rs | 7 +++++++ crates/compiler/can/src/task_module.rs | 3 +++ crates/compiler/derive/src/decoding.rs | 1 + crates/compiler/derive/src/decoding/record.rs | 3 +++ crates/compiler/derive/src/decoding/tuple.rs | 3 +++ crates/compiler/derive/src/encoding.rs | 6 ++++++ crates/compiler/derive/src/hash.rs | 1 + crates/compiler/derive/src/inspect.rs | 6 ++++++ crates/compiler/lower_params/src/lower.rs | 2 ++ crates/compiler/types/src/subs.rs | 4 ++++ 13 files changed, 40 insertions(+) diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 64e7d9d6697..463a9162dfc 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -446,6 +446,7 @@ fn defn_help( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: ret_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: Vec::new(), diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 0f791e82733..cd750711d88 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -456,6 +456,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr function_type, closure_type, return_type, + effect_type, early_returns, name, captured_symbols, @@ -466,6 +467,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr function_type: sub!(*function_type), closure_type: sub!(*closure_type), return_type: sub!(*return_type), + effect_type: sub!(*effect_type), early_returns: early_returns .iter() .map(|(var, region)| (sub!(*var), *region)) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 31e9c1330f8..32fed7276d0 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -2354,6 +2354,7 @@ fn canonicalize_pending_value_def<'a>( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), + effect_type: var_store.fresh(), early_returns: scope.early_returns.clone(), name: symbol, captured_symbols: Vec::new(), diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index fa4689f1b78..3375fe24898 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -407,6 +407,7 @@ pub struct ClosureData { pub function_type: Variable, pub closure_type: Variable, pub return_type: Variable, + pub effect_type: Variable, pub early_returns: Vec<(Variable, Region)>, pub name: Symbol, pub captured_symbols: Vec<(Symbol, Variable)>, @@ -484,6 +485,7 @@ impl StructAccessorData { function_type: function_var, closure_type: closure_var, return_type: field_var, + effect_type: Variable::PURE, early_returns: vec![], name, captured_symbols: vec![], @@ -558,6 +560,7 @@ impl OpaqueWrapFunctionData { function_type: function_var, closure_type: closure_var, return_type: opaque_var, + effect_type: Variable::PURE, early_returns: vec![], name: function_name, captured_symbols: vec![], @@ -1687,6 +1690,7 @@ fn canonicalize_closure_body<'a>( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: return_type_var, + effect_type: var_store.fresh(), early_returns: scope.early_returns.clone(), name: symbol, captured_symbols, @@ -2317,6 +2321,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { function_type, closure_type, return_type, + effect_type, early_returns, recursive, name, @@ -2334,6 +2339,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { function_type, closure_type, return_type, + effect_type, early_returns, recursive, name, @@ -3184,6 +3190,7 @@ impl Declarations { function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), + effect_type: var_store.fresh(), early_returns: vec![], name: self.symbols[index].value, captured_symbols: vec![], diff --git a/crates/compiler/can/src/task_module.rs b/crates/compiler/can/src/task_module.rs index f762527a478..7bd20cb0faa 100644 --- a/crates/compiler/can/src/task_module.rs +++ b/crates/compiler/can/src/task_module.rs @@ -72,6 +72,7 @@ pub fn build_host_exposed_def( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), + effect_type: var_store.fresh(), early_returns: vec![], name: task_closure_symbol, captured_symbols, @@ -99,6 +100,7 @@ pub fn build_host_exposed_def( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), + effect_type: var_store.fresh(), early_returns: vec![], name: symbol, captured_symbols: std::vec::Vec::new(), @@ -128,6 +130,7 @@ pub fn build_host_exposed_def( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), + effect_type: var_store.fresh(), early_returns: vec![], name: task_closure_symbol, captured_symbols, diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 16d0f8b638f..2fd81ff9956 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -147,6 +147,7 @@ fn wrap_in_decode_custom_decode_with( function_type: fn_var, closure_type: fn_clos_var, return_type: decode_with_result_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: sorted_inner_decoder_captures, diff --git a/crates/compiler/derive/src/decoding/record.rs b/crates/compiler/derive/src/decoding/record.rs index 12540abac3e..1a7fed2a687 100644 --- a/crates/compiler/derive/src/decoding/record.rs +++ b/crates/compiler/derive/src/decoding/record.rs @@ -350,6 +350,7 @@ pub(super) fn step_field( function_type, closure_type, return_type: keep_or_skip_var, + effect_type: Variable::PURE, early_returns: vec![], name: step_field_closure, captured_symbols: Vec::new(), @@ -587,6 +588,7 @@ fn custom_decoder_lambda(env: &mut Env<'_>, args: DecodingFieldArgs) -> (Variabl function_type: this_custom_callback_var, closure_type: custom_callback_lambda_set_var, return_type: custom_callback_ret_var, + effect_type: Variable::PURE, early_returns: vec![], name: custom_closure_symbol, captured_symbols: vec![(state_arg_symbol, state_record_var)], @@ -995,6 +997,7 @@ pub(super) fn finalizer( function_type: function_var, closure_type, return_type: return_type_var, + effect_type: Variable::PURE, early_returns: vec![], name: function_symbol, captured_symbols: Vec::new(), diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index e9a2ed2a780..7c9640979b8 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -556,6 +556,7 @@ fn step_elem( function_type: this_custom_callback_var, closure_type: custom_callback_lambda_set_var, return_type: custom_callback_ret_var, + effect_type: Variable::PURE, early_returns: vec![], name: custom_closure_symbol, captured_symbols: vec![(state_arg_symbol, state_record_var)], @@ -711,6 +712,7 @@ fn step_elem( function_type, closure_type, return_type: keep_or_skip_var, + effect_type: Variable::PURE, early_returns: vec![], name: step_elem_closure, captured_symbols: Vec::new(), @@ -898,6 +900,7 @@ fn finalizer( function_type: function_var, closure_type, return_type: return_type_var, + effect_type: Variable::PURE, early_returns: vec![], name: function_symbol, captured_symbols: Vec::new(), diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index 317539d482d..e6df456861b 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -188,6 +188,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: to_elem_encoder_fn_var, closure_type: to_elem_encoder_lset, return_type: elem_encoder_var, + effect_type: Variable::PURE, early_returns: vec![], name: to_elem_encoder_sym, captured_symbols: vec![], @@ -282,6 +283,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -492,6 +494,7 @@ fn to_encoder_record( function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -675,6 +678,7 @@ fn to_encoder_tuple( function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -918,6 +922,7 @@ fn to_encoder_tag_union( function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -1030,6 +1035,7 @@ fn wrap_in_encode_custom( function_type: fn_var, closure_type: fn_clos_var, return_type: Variable::LIST_U8, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![(captured_symbol, captured_var)], diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index 1cb06d17093..22111f981da 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -542,6 +542,7 @@ fn build_outer_derived_closure( function_type: fn_var, closure_type: fn_clos_var, return_type: body_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], diff --git a/crates/compiler/derive/src/inspect.rs b/crates/compiler/derive/src/inspect.rs index b0da2dac1e3..cdba7360d3f 100644 --- a/crates/compiler/derive/src/inspect.rs +++ b/crates/compiler/derive/src/inspect.rs @@ -194,6 +194,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: to_elem_inspector_fn_var, closure_type: to_elem_inspector_lset, return_type: elem_inspector_var, + effect_type: Variable::PURE, early_returns: vec![], name: to_elem_inspector_sym, captured_symbols: vec![], @@ -293,6 +294,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -504,6 +506,7 @@ fn to_inspector_record( function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -688,6 +691,7 @@ fn to_inspector_tuple( function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -935,6 +939,7 @@ fn to_inspector_tag_union( function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -1034,6 +1039,7 @@ fn wrap_in_inspect_custom( function_type: fn_var, closure_type: fn_clos_var, return_type: fmt_var, + effect_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![(captured_symbol, captured_var)], diff --git a/crates/compiler/lower_params/src/lower.rs b/crates/compiler/lower_params/src/lower.rs index 6ef675eadea..699b17017fe 100644 --- a/crates/compiler/lower_params/src/lower.rs +++ b/crates/compiler/lower_params/src/lower.rs @@ -218,6 +218,7 @@ impl<'a> LowerParams<'a> { captured_symbols: _, name: _, function_type: _, + effect_type: _, closure_type: _, return_type: _, early_returns: _, @@ -539,6 +540,7 @@ impl<'a> LowerParams<'a> { function_type: self.var_store.fresh(), closure_type: self.var_store.fresh(), return_type: self.var_store.fresh(), + effect_type: self.var_store.fresh(), early_returns: vec![], name: self.unique_symbol(), captured_symbols, diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index 9b60e37d4f4..e6a703e69f1 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -1171,6 +1171,10 @@ define_const_var! { /// The erased lambda type. :pub ERASED_LAMBDA, + + /// Kind of function + :pub PURE, + :pub EFFECTFUL, } impl Variable { From 75177c9c98e026a0d86ec5feaa5e4b577f205dda Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Fri, 4 Oct 2024 20:37:10 -0300 Subject: [PATCH 04/73] Rename effect_type to fx_type and add to FunctionDef --- crates/compiler/can/src/builtins.rs | 2 +- crates/compiler/can/src/copy.rs | 4 ++-- crates/compiler/can/src/def.rs | 2 +- crates/compiler/can/src/expr.rs | 19 ++++++++++++------- crates/compiler/can/src/task_module.rs | 6 +++--- crates/compiler/derive/src/decoding.rs | 2 +- crates/compiler/derive/src/decoding/record.rs | 6 +++--- crates/compiler/derive/src/decoding/tuple.rs | 6 +++--- crates/compiler/derive/src/encoding.rs | 12 ++++++------ crates/compiler/derive/src/hash.rs | 2 +- crates/compiler/derive/src/inspect.rs | 12 ++++++------ crates/compiler/lower_params/src/lower.rs | 4 ++-- 12 files changed, 41 insertions(+), 36 deletions(-) diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 463a9162dfc..ae6b41cfdbe 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -446,7 +446,7 @@ fn defn_help( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: ret_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: Vec::new(), diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index cd750711d88..5507acbd6e2 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -456,7 +456,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr function_type, closure_type, return_type, - effect_type, + fx_type: effect_type, early_returns, name, captured_symbols, @@ -467,7 +467,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr function_type: sub!(*function_type), closure_type: sub!(*closure_type), return_type: sub!(*return_type), - effect_type: sub!(*effect_type), + fx_type: sub!(*effect_type), early_returns: early_returns .iter() .map(|(var, region)| (sub!(*var), *region)) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 32fed7276d0..a69ccecb5ad 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -2354,7 +2354,7 @@ fn canonicalize_pending_value_def<'a>( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), - effect_type: var_store.fresh(), + fx_type: var_store.fresh(), early_returns: scope.early_returns.clone(), name: symbol, captured_symbols: Vec::new(), diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 3375fe24898..00ce483ed4e 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -407,7 +407,7 @@ pub struct ClosureData { pub function_type: Variable, pub closure_type: Variable, pub return_type: Variable, - pub effect_type: Variable, + pub fx_type: Variable, pub early_returns: Vec<(Variable, Region)>, pub name: Symbol, pub captured_symbols: Vec<(Symbol, Variable)>, @@ -485,7 +485,7 @@ impl StructAccessorData { function_type: function_var, closure_type: closure_var, return_type: field_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name, captured_symbols: vec![], @@ -560,7 +560,7 @@ impl OpaqueWrapFunctionData { function_type: function_var, closure_type: closure_var, return_type: opaque_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: function_name, captured_symbols: vec![], @@ -1690,7 +1690,7 @@ fn canonicalize_closure_body<'a>( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: return_type_var, - effect_type: var_store.fresh(), + fx_type: var_store.fresh(), early_returns: scope.early_returns.clone(), name: symbol, captured_symbols, @@ -2321,7 +2321,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { function_type, closure_type, return_type, - effect_type, + fx_type: effect_type, early_returns, recursive, name, @@ -2339,7 +2339,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { function_type, closure_type, return_type, - effect_type, + fx_type: effect_type, early_returns, recursive, name, @@ -2916,6 +2916,7 @@ impl Declarations { let function_def = FunctionDef { closure_type: loc_closure_data.value.closure_type, return_type: loc_closure_data.value.return_type, + fx_type: loc_closure_data.value.fx_type, early_returns: loc_closure_data.value.early_returns, captured_symbols: loc_closure_data.value.captured_symbols, arguments: loc_closure_data.value.arguments, @@ -2968,6 +2969,7 @@ impl Declarations { let function_def = FunctionDef { closure_type: loc_closure_data.value.closure_type, return_type: loc_closure_data.value.return_type, + fx_type: loc_closure_data.value.fx_type, early_returns: loc_closure_data.value.early_returns, captured_symbols: loc_closure_data.value.captured_symbols, arguments: loc_closure_data.value.arguments, @@ -3149,6 +3151,7 @@ impl Declarations { let function_def = FunctionDef { closure_type: closure_data.closure_type, return_type: closure_data.return_type, + fx_type: closure_data.fx_type, early_returns: closure_data.early_returns, captured_symbols: closure_data.captured_symbols, arguments: closure_data.arguments, @@ -3190,7 +3193,7 @@ impl Declarations { function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), - effect_type: var_store.fresh(), + fx_type: var_store.fresh(), early_returns: vec![], name: self.symbols[index].value, captured_symbols: vec![], @@ -3204,6 +3207,7 @@ impl Declarations { let function_def = FunctionDef { closure_type: loc_closure_data.value.closure_type, return_type: loc_closure_data.value.return_type, + fx_type: loc_closure_data.value.fx_type, early_returns: loc_closure_data.value.early_returns, captured_symbols: loc_closure_data.value.captured_symbols, arguments: loc_closure_data.value.arguments, @@ -3339,6 +3343,7 @@ impl DeclarationTag { pub struct FunctionDef { pub closure_type: Variable, pub return_type: Variable, + pub fx_type: Variable, pub early_returns: Vec<(Variable, Region)>, pub captured_symbols: Vec<(Symbol, Variable)>, pub arguments: Vec<(Variable, AnnotatedMark, Loc)>, diff --git a/crates/compiler/can/src/task_module.rs b/crates/compiler/can/src/task_module.rs index 7bd20cb0faa..d5b4cfa4eb3 100644 --- a/crates/compiler/can/src/task_module.rs +++ b/crates/compiler/can/src/task_module.rs @@ -72,7 +72,7 @@ pub fn build_host_exposed_def( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), - effect_type: var_store.fresh(), + fx_type: var_store.fresh(), early_returns: vec![], name: task_closure_symbol, captured_symbols, @@ -100,7 +100,7 @@ pub fn build_host_exposed_def( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), - effect_type: var_store.fresh(), + fx_type: var_store.fresh(), early_returns: vec![], name: symbol, captured_symbols: std::vec::Vec::new(), @@ -130,7 +130,7 @@ pub fn build_host_exposed_def( function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), - effect_type: var_store.fresh(), + fx_type: var_store.fresh(), early_returns: vec![], name: task_closure_symbol, captured_symbols, diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 2fd81ff9956..372539a1d60 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -147,7 +147,7 @@ fn wrap_in_decode_custom_decode_with( function_type: fn_var, closure_type: fn_clos_var, return_type: decode_with_result_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: sorted_inner_decoder_captures, diff --git a/crates/compiler/derive/src/decoding/record.rs b/crates/compiler/derive/src/decoding/record.rs index 1a7fed2a687..2a7773ece80 100644 --- a/crates/compiler/derive/src/decoding/record.rs +++ b/crates/compiler/derive/src/decoding/record.rs @@ -350,7 +350,7 @@ pub(super) fn step_field( function_type, closure_type, return_type: keep_or_skip_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: step_field_closure, captured_symbols: Vec::new(), @@ -588,7 +588,7 @@ fn custom_decoder_lambda(env: &mut Env<'_>, args: DecodingFieldArgs) -> (Variabl function_type: this_custom_callback_var, closure_type: custom_callback_lambda_set_var, return_type: custom_callback_ret_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: custom_closure_symbol, captured_symbols: vec![(state_arg_symbol, state_record_var)], @@ -997,7 +997,7 @@ pub(super) fn finalizer( function_type: function_var, closure_type, return_type: return_type_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: function_symbol, captured_symbols: Vec::new(), diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index 7c9640979b8..fbeb5a1f249 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -556,7 +556,7 @@ fn step_elem( function_type: this_custom_callback_var, closure_type: custom_callback_lambda_set_var, return_type: custom_callback_ret_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: custom_closure_symbol, captured_symbols: vec![(state_arg_symbol, state_record_var)], @@ -712,7 +712,7 @@ fn step_elem( function_type, closure_type, return_type: keep_or_skip_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: step_elem_closure, captured_symbols: Vec::new(), @@ -900,7 +900,7 @@ fn finalizer( function_type: function_var, closure_type, return_type: return_type_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: function_symbol, captured_symbols: Vec::new(), diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index e6df456861b..fca8b93a989 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -188,7 +188,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: to_elem_encoder_fn_var, closure_type: to_elem_encoder_lset, return_type: elem_encoder_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: to_elem_encoder_sym, captured_symbols: vec![], @@ -283,7 +283,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -494,7 +494,7 @@ fn to_encoder_record( function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -678,7 +678,7 @@ fn to_encoder_tuple( function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -922,7 +922,7 @@ fn to_encoder_tag_union( function_type: fn_var, closure_type: fn_clos_var, return_type: this_encoder_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -1035,7 +1035,7 @@ fn wrap_in_encode_custom( function_type: fn_var, closure_type: fn_clos_var, return_type: Variable::LIST_U8, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![(captured_symbol, captured_var)], diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index 22111f981da..7fcf34c93b9 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -542,7 +542,7 @@ fn build_outer_derived_closure( function_type: fn_var, closure_type: fn_clos_var, return_type: body_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], diff --git a/crates/compiler/derive/src/inspect.rs b/crates/compiler/derive/src/inspect.rs index cdba7360d3f..4c900e61e80 100644 --- a/crates/compiler/derive/src/inspect.rs +++ b/crates/compiler/derive/src/inspect.rs @@ -194,7 +194,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: to_elem_inspector_fn_var, closure_type: to_elem_inspector_lset, return_type: elem_inspector_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: to_elem_inspector_sym, captured_symbols: vec![], @@ -294,7 +294,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -506,7 +506,7 @@ fn to_inspector_record( function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -691,7 +691,7 @@ fn to_inspector_tuple( function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -939,7 +939,7 @@ fn to_inspector_tag_union( function_type: fn_var, closure_type: fn_clos_var, return_type: this_inspector_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![], @@ -1039,7 +1039,7 @@ fn wrap_in_inspect_custom( function_type: fn_var, closure_type: fn_clos_var, return_type: fmt_var, - effect_type: Variable::PURE, + fx_type: Variable::PURE, early_returns: vec![], name: fn_name, captured_symbols: vec![(captured_symbol, captured_var)], diff --git a/crates/compiler/lower_params/src/lower.rs b/crates/compiler/lower_params/src/lower.rs index 699b17017fe..069e48530f1 100644 --- a/crates/compiler/lower_params/src/lower.rs +++ b/crates/compiler/lower_params/src/lower.rs @@ -218,7 +218,7 @@ impl<'a> LowerParams<'a> { captured_symbols: _, name: _, function_type: _, - effect_type: _, + fx_type: _, closure_type: _, return_type: _, early_returns: _, @@ -540,7 +540,7 @@ impl<'a> LowerParams<'a> { function_type: self.var_store.fresh(), closure_type: self.var_store.fresh(), return_type: self.var_store.fresh(), - effect_type: self.var_store.fresh(), + fx_type: self.var_store.fresh(), early_returns: vec![], name: self.unique_symbol(), captured_symbols, From 3cef756559d0b8b900cc260930b20a2036254e75 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sat, 5 Oct 2024 20:12:29 -0300 Subject: [PATCH 05/73] Add fx var to Type::Function et al --- crates/compiler/can/src/annotation.rs | 15 ++-- crates/compiler/can/src/def.rs | 1 + crates/compiler/can/src/task_module.rs | 4 +- crates/compiler/constrain/src/expr.rs | 74 ++++++++++++++++---- crates/compiler/lower_params/src/lower.rs | 2 +- crates/compiler/solve/src/to_var.rs | 4 +- crates/compiler/types/src/types.rs | 84 +++++++++++++++-------- 7 files changed, 133 insertions(+), 51 deletions(-) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 5dbe152c99e..25ed1d73829 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -4,7 +4,9 @@ use crate::scope::{PendingAbilitiesInScope, Scope, SymbolLookup}; use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet}; use roc_module::ident::{Ident, Lowercase, TagName}; use roc_module::symbol::Symbol; -use roc_parse::ast::{AssignedField, ExtractSpaces, Pattern, Tag, TypeAnnotation, TypeHeader}; +use roc_parse::ast::{ + AssignedField, ExtractSpaces, FunctionArrow, Pattern, Tag, TypeAnnotation, TypeHeader, +}; use roc_problem::can::ShadowKind; use roc_region::all::{Loc, Region}; use roc_types::subs::{VarStore, Variable}; @@ -554,8 +556,7 @@ fn can_annotation_help( use roc_parse::ast::TypeAnnotation::*; match annotation { - Function(argument_types, _arrow, return_type) => { - // [purity-inference] TODO: arrow + Function(argument_types, arrow, return_type) => { let mut args = Vec::new(); for arg in *argument_types { @@ -590,7 +591,13 @@ fn can_annotation_help( introduced_variables.insert_lambda_set(lambda_set); let closure = Type::Variable(lambda_set); - Type::Function(args, Box::new(closure), Box::new(ret)) + let fx_var = match arrow { + FunctionArrow::Pure => Variable::PURE, + FunctionArrow::Effectful => Variable::EFFECTFUL, + }; + let fx_type = Type::Variable(fx_var); + + Type::Function(args, Box::new(closure), Box::new(ret), Box::new(fx_type)) } Apply(module_name, ident, type_arguments) => { let symbol = match make_apply_symbol(env, region, scope, module_name, ident, references) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index a69ccecb5ad..cf1d40b0b75 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -127,6 +127,7 @@ impl Annotation { arg_types, Box::new(Type::Variable(var_store.fresh())), Box::new(self.signature.clone()), + Box::new(Type::Variable(var_store.fresh())), ); } } diff --git a/crates/compiler/can/src/task_module.rs b/crates/compiler/can/src/task_module.rs index d5b4cfa4eb3..ca2efcbb06d 100644 --- a/crates/compiler/can/src/task_module.rs +++ b/crates/compiler/can/src/task_module.rs @@ -33,7 +33,7 @@ pub fn build_host_exposed_def( let def_body = { match typ.shallow_structural_dealias() { - Type::Function(args, _, _) => { + Type::Function(args, _, _, _) => { for i in 0..args.len() { let name = format!("closure_arg_{ident}_{i}"); @@ -181,11 +181,13 @@ fn build_fresh_opaque_variables( let ok_var = var_store.fresh(); let err_var = var_store.fresh(); let result_var = var_store.fresh(); + let fx_var = var_store.fresh(); let actual = Type::Function( vec![Type::EmptyRec], Box::new(Type::Variable(closure_var)), Box::new(Type::Variable(result_var)), + Box::new(Type::Variable(fx_var)), ); let type_arguments = vec![ diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 143b697002a..dc3ffd637e6 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -67,6 +67,7 @@ fn constrain_untyped_args( arguments: &[(Variable, AnnotatedMark, Loc)], closure_type: Type, return_type: Type, + fx_type: Type, ) -> (Vec, PatternState, Type) { let mut vars = Vec::with_capacity(arguments.len()); let mut pattern_types = Vec::with_capacity(arguments.len()); @@ -97,8 +98,12 @@ fn constrain_untyped_args( vars.push(*pattern_var); } - let function_type = - Type::Function(pattern_types, Box::new(closure_type), Box::new(return_type)); + let function_type = Type::Function( + pattern_types, + Box::new(closure_type), + Box::new(return_type), + Box::new(fx_type), + ); (vars, pattern_state, function_type) } @@ -109,10 +114,10 @@ fn constrain_untyped_closure( env: &mut Env, region: Region, expected: ExpectedTypeIndex, - fn_var: Variable, closure_var: Variable, ret_var: Variable, + fx_var: Variable, early_returns: &[(Variable, Region)], arguments: &[(Variable, AnnotatedMark, Loc)], loc_body_expr: &Loc, @@ -122,6 +127,7 @@ fn constrain_untyped_closure( let closure_type = Type::Variable(closure_var); let return_type = Type::Variable(ret_var); let return_type_index = constraints.push_variable(ret_var); + let fx_type = Type::Variable(fx_var); let (mut vars, pattern_state, function_type) = constrain_untyped_args( types, constraints, @@ -129,9 +135,11 @@ fn constrain_untyped_closure( arguments, closure_type, return_type, + fx_type, ); vars.push(ret_var); + vars.push(fx_var); vars.push(closure_var); vars.push(fn_var); @@ -141,6 +149,7 @@ fn constrain_untyped_closure( loc_body_expr.region, )); + // [purity-inference] TODO: constrain calls in body with fx_var let ret_constraint = constrain_expr( types, constraints, @@ -546,7 +555,9 @@ pub fn constrain_expr( let arguments = types.from_old_type_slice(arg_types.iter()); let lambda_set = types.from_old_type(&closure_type); let ret = types.from_old_type(&ret_type); - let typ = types.function(arguments, lambda_set, ret); + // [purity-inference] TODO: Add fx var to call + let fx = types.from_old_type(&Type::Variable(Variable::PURE)); + let typ = types.function(arguments, lambda_set, ret, fx); constraints.push_type(types, typ) }; let expected_fn_type = @@ -646,6 +657,7 @@ pub fn constrain_expr( function_type: fn_var, closure_type: closure_var, return_type: ret_var, + fx_type: fx_var, early_returns, arguments, loc_body: boxed, @@ -663,6 +675,7 @@ pub fn constrain_expr( *fn_var, *closure_var, *ret_var, + *fx_var, early_returns, arguments, boxed, @@ -1288,6 +1301,7 @@ pub fn constrain_expr( vec![record_type], Box::new(closure_type), Box::new(field_type), + Box::new(Type::Variable(Variable::PURE)), )); constraints.push_type(types, typ) }; @@ -1668,6 +1682,7 @@ pub fn constrain_expr( vec![argument_type], Box::new(closure_type), Box::new(opaque_type), + Box::new(Type::Variable(Variable::PURE)), )); constraints.push_type(types, typ) }; @@ -1853,11 +1868,12 @@ fn constrain_function_def( let signature_index = constraints.push_type(types, signature); - let (arg_types, _signature_closure_type, ret_type) = match types[signature] { - TypeTag::Function(signature_closure_type, ret_type) => ( + let (arg_types, _signature_closure_type, ret_type, fx_type) = match types[signature] { + TypeTag::Function(signature_closure_type, ret_type, fx_type) => ( types.get_type_arguments(signature), signature_closure_type, ret_type, + fx_type, ), _ => { // aliases, or just something weird @@ -1917,6 +1933,7 @@ fn constrain_function_def( expr_var, function_def.closure_type, function_def.return_type, + function_def.fx_type, &function_def.early_returns, &function_def.arguments, loc_body_expr, @@ -1963,9 +1980,11 @@ fn constrain_function_def( let closure_var = function_def.closure_type; let ret_type_index = constraints.push_type(types, ret_type); + let fx_type_index = constraints.push_type(types, fx_type); vars.push(function_def.return_type); vars.push(function_def.closure_type); + vars.push(function_def.fx_type); let mut def_pattern_state = PatternState::default(); @@ -2043,8 +2062,9 @@ fn constrain_function_def( ); let lambda_set = types.from_old_type(&Type::Variable(function_def.closure_type)); let ret_var = types.from_old_type(&Type::Variable(function_def.return_type)); + let fx_var = types.from_old_type(&Type::Variable(function_def.fx_type)); - let fn_type = types.function(pattern_types, lambda_set, ret_var); + let fn_type = types.function(pattern_types, lambda_set, ret_var, fx_var); constraints.push_type(types, fn_type) }; @@ -2083,6 +2103,12 @@ fn constrain_function_def( std::file!(), std::line!(), ), + constraints.store( + fx_type_index, + function_def.fx_type, + std::file!(), + std::line!(), + ), // Now, check the solved function type matches the annotation. constraints.equal_types( solved_fn_type, @@ -2119,6 +2145,7 @@ fn constrain_function_def( expr_var, function_def.closure_type, function_def.return_type, + function_def.fx_type, &function_def.early_returns, &function_def.arguments, loc_expr, @@ -2841,13 +2868,14 @@ fn constrain_typed_def( function_type: fn_var, closure_type: closure_var, return_type: ret_var, + fx_type: fx_var, captured_symbols, arguments, loc_body, name, .. }), - TypeTag::Function(_signature_closure_type, ret_type), + TypeTag::Function(_signature_closure_type, ret_type, fx_type), ) => { let arg_types = types.get_type_arguments(signature); @@ -2866,10 +2894,13 @@ fn constrain_typed_def( let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let ret_var = *ret_var; let closure_var = *closure_var; + let fx_var = *fx_var; let ret_type_index = constraints.push_type(types, ret_type); + let fx_type_index = constraints.push_type(types, fx_type); vars.push(ret_var); vars.push(closure_var); + vars.push(fx_var); constrain_typed_function_arguments( types, @@ -2899,8 +2930,9 @@ fn constrain_typed_def( types.from_old_type_slice(arguments.iter().map(|a| Type::Variable(a.0))); let lambda_set = types.from_old_type(&Type::Variable(closure_var)); let ret_var = types.from_old_type(&Type::Variable(ret_var)); + let fx_var = types.from_old_type(&Type::Variable(fx_var)); - let fn_type = types.function(arg_types, lambda_set, ret_var); + let fn_type = types.function(arg_types, lambda_set, ret_var, fx_var); constraints.push_type(types, fn_type) }; @@ -2940,6 +2972,7 @@ fn constrain_typed_def( // when we check that the solved function type matches the annotation, we can // display the fully inferred return variable. constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), + constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), // Now, check the solved function type matches the annotation. constraints.equal_types( solved_fn_type, @@ -3700,6 +3733,7 @@ fn constraint_recursive_function( expr_var, function_def.closure_type, function_def.return_type, + function_def.fx_type, &function_def.early_returns, &function_def.arguments, loc_expr, @@ -3747,11 +3781,12 @@ fn constraint_recursive_function( signature_index, )); - let (arg_types, _signature_closure_type, ret_type) = match types[signature] { - TypeTag::Function(signature_closure_type, ret_type) => ( + let (arg_types, _signature_closure_type, ret_type, fx_type) = match types[signature] { + TypeTag::Function(signature_closure_type, ret_type, fx_type) => ( types.get_type_arguments(signature), signature_closure_type, ret_type, + fx_type, ), _ => todo!("TODO {:?}", (loc_symbol, types[signature])), }; @@ -3767,11 +3802,14 @@ fn constraint_recursive_function( }; let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let ret_var = function_def.return_type; + let fx_var = function_def.fx_type; let closure_var = function_def.closure_type; let ret_type_index = constraints.push_type(types, ret_type); + let fx_type_index = constraints.push_type(types, fx_type); vars.push(ret_var); vars.push(closure_var); + vars.push(fx_var); let mut def_pattern_state = PatternState::default(); @@ -3819,11 +3857,12 @@ fn constraint_recursive_function( let fn_type = { // TODO(types-soa) optimize for Variable let lambda_set = types.from_old_type(&Type::Variable(closure_var)); - let typ = types.function(pattern_types, lambda_set, ret_type); + let typ = types.function(pattern_types, lambda_set, ret_type, fx_type); constraints.push_type(types, typ) }; let expr_con = { + // [purity-inference] TODO: constrain calls in body let expected = constraints.push_expected_type(NoExpectation(ret_type_index)); constrain_expr( types, @@ -3854,6 +3893,7 @@ fn constraint_recursive_function( // Store type into AST vars. We use Store so errors aren't reported twice constraints.store(signature_index, expr_var, std::file!(), std::line!()), constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), + constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), closure_constraint, ]; @@ -4305,13 +4345,14 @@ fn rec_defs_help( function_type: fn_var, closure_type: closure_var, return_type: ret_var, + fx_type: fx_var, captured_symbols, arguments, loc_body, name, .. }), - TypeTag::Function(_closure_type, ret_type), + TypeTag::Function(_closure_type, ret_type, fx_type), ) => { // NOTE if we ever have trouble with closure type unification, the ignored // `_closure_type` here is a good place to start investigating @@ -4331,11 +4372,14 @@ fn rec_defs_help( let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let ret_var = *ret_var; + let fx_var = *fx_var; let closure_var = *closure_var; let ret_type_index = constraints.push_type(types, ret_type); + let fx_type_index = constraints.push_type(types, fx_type); vars.push(ret_var); vars.push(closure_var); + vars.push(fx_var); constrain_typed_function_arguments( types, @@ -4364,12 +4408,13 @@ fn rec_defs_help( let fn_type_index = { // TODO(types-soa) optimize for variable let lambda_set = types.from_old_type(&Type::Variable(closure_var)); - let typ = types.function(pattern_types, lambda_set, ret_type); + let typ = types.function(pattern_types, lambda_set, ret_type, fx_type); constraints.push_type(types, typ) }; let expr_con = { let body_type = constraints.push_expected_type(NoExpectation(ret_type_index)); + // [purity-inference] TODO: unify calls in body with the fx_type constrain_expr( types, @@ -4412,6 +4457,7 @@ fn rec_defs_help( std::line!(), ), constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), + constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), closure_constraint, ]; diff --git a/crates/compiler/lower_params/src/lower.rs b/crates/compiler/lower_params/src/lower.rs index 069e48530f1..ab54c5ab8c8 100644 --- a/crates/compiler/lower_params/src/lower.rs +++ b/crates/compiler/lower_params/src/lower.rs @@ -92,7 +92,7 @@ impl<'a> LowerParams<'a> { .retain(|(sym, _)| !home_param_symbols.contains(sym)); if let Some(ann) = &mut decls.annotations[index] { - if let Type::Function(args, _, _) = &mut ann.signature { + if let Type::Function(args, _, _, _) = &mut ann.signature { args.push(Type::Variable(var)); } } diff --git a/crates/compiler/solve/src/to_var.rs b/crates/compiler/solve/src/to_var.rs index fb60a3fad82..0606910479c 100644 --- a/crates/compiler/solve/src/to_var.rs +++ b/crates/compiler/solve/src/to_var.rs @@ -402,7 +402,7 @@ pub(crate) fn type_to_var_help( env.register_with_known_var(destination, rank, content) } // This case is important for the rank of boolean variables - Function(closure_type, ret_type) => { + Function(closure_type, ret_type, fx_type) => { let arguments = types.get_type_arguments(typ_index); let new_arguments = env.subs.reserve_into_vars(arguments.len()); for (target_index, var_index) in @@ -413,8 +413,10 @@ pub(crate) fn type_to_var_help( } let ret_var = helper!(ret_type); + let fx_var = helper!(fx_type); let closure_var = helper!(closure_type, AmbientFunctionPolicy::Function(destination)); + // [purity-inference] TODO: add fx_type to FlatType::Func let content = Content::Structure(FlatType::Func(new_arguments, closure_var, ret_var)); diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 0621989e1af..b6eb3ba0c9f 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -376,6 +376,8 @@ pub enum TypeTag { Index, /// return type Index, + /// fx type + Index, ), /// Closure arguments are implicit ClosureTag { @@ -733,10 +735,11 @@ impl Types { arguments: Slice, lambda_set: Index, ret: Index, + fx: Index, ) -> Index { let index = self.reserve_type_tag(); - let tag = TypeTag::Function(lambda_set, ret); + let tag = TypeTag::Function(lambda_set, ret, fx); self.set_type_tag(index, tag, arguments); index } @@ -748,12 +751,13 @@ impl Types { Type::EmptyTagUnion => { self.set_type_tag(index, TypeTag::EmptyTagUnion, Slice::default()) } - Type::Function(arguments, lambda_set, return_type) => { + Type::Function(arguments, lambda_set, return_type, fx_type) => { let argument_slice = self.from_old_type_slice(arguments.iter()); let tag = TypeTag::Function( self.from_old_type(lambda_set), self.from_old_type(return_type), + self.from_old_type(fx_type), ); self.set_type_tag(index, tag, argument_slice) @@ -1093,14 +1097,15 @@ impl Types { Variable(v) => (Variable(subst!(v)), Default::default()), EmptyRecord => (EmptyRecord, Default::default()), EmptyTagUnion => (EmptyTagUnion, Default::default()), - Function(clos, ret) => { + Function(clos, ret, fx) => { let args = self.get_type_arguments(typ); let new_args = defer_slice!(args); let new_clos = defer!(clos); let new_ret = defer!(ret); + let new_fx = defer!(fx); - (Function(new_clos, new_ret), new_args) + (Function(new_clos, new_ret, new_fx), new_args) } ClosureTag { name, @@ -1328,7 +1333,7 @@ mod debug_types { let group = match types[tag] { TypeTag::EmptyRecord => f.text("{}"), TypeTag::EmptyTagUnion => f.text("[]"), - TypeTag::Function(clos, ret) => { + TypeTag::Function(clos, ret, fx) => { let args = types.get_type_arguments(tag); maybe_paren!( Free, @@ -1339,6 +1344,8 @@ mod debug_types { ) .append(f.text(" -")) .append(typ(types, f, Free, clos)) + .append(f.text(" -")) + .append(typ(types, f, Free, fx)) .append(f.text("->")) .append(f.line()) .append(typ(types, f, Arg, ret)) @@ -1653,8 +1660,8 @@ impl std::ops::Index> for Types { pub enum Type { EmptyRec, EmptyTagUnion, - /// A function. The types of its arguments, size of its closure, then the type of its return value. - Function(Vec, Box, Box), + /// A function. The types of its arguments, size of its closure, its return value, then the fx type. + Function(Vec, Box, Box, Box), Record(SendMap>, TypeExtension), Tuple(VecMap, TypeExtension), TagUnion(Vec<(TagName, Vec)>, TypeExtension), @@ -1730,8 +1737,8 @@ impl Clone for Type { match self { Self::EmptyRec => Self::EmptyRec, Self::EmptyTagUnion => Self::EmptyTagUnion, - Self::Function(arg0, arg1, arg2) => { - Self::Function(arg0.clone(), arg1.clone(), arg2.clone()) + Self::Function(arg0, arg1, arg2, arg3) => { + Self::Function(arg0.clone(), arg1.clone(), arg2.clone(), arg3.clone()) } Self::Record(arg0, arg1) => Self::Record(arg0.clone(), arg1.clone()), Self::Tuple(arg0, arg1) => Self::Tuple(arg0.clone(), arg1.clone()), @@ -1886,7 +1893,7 @@ impl fmt::Debug for Type { match self { Type::EmptyRec => write!(f, "{{}}"), Type::EmptyTagUnion => write!(f, "[]"), - Type::Function(args, closure, ret) => { + Type::Function(args, closure, ret, fx) => { write!(f, "Fn(")?; for (index, arg) in args.iter().enumerate() { @@ -1898,7 +1905,8 @@ impl fmt::Debug for Type { } write!(f, " |{closure:?}|")?; - write!(f, " -> ")?; + write!(f, " -{fx:?}")?; + write!(f, "-> ")?; ret.fmt(f)?; @@ -2143,7 +2151,7 @@ impl fmt::Debug for Type { impl Type { pub fn arity(&self) -> usize { - if let Type::Function(args, _, _) = self { + if let Type::Function(args, _, _, _) = self { args.len() } else { 0 @@ -2187,10 +2195,11 @@ impl Type { *typ = replacement.clone(); } } - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { stack.extend(args); stack.push(closure); stack.push(ret); + stack.push(fx); } ClosureTag { name: _, @@ -2316,10 +2325,11 @@ impl Type { *v = *replacement; } } - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { stack.extend(args); stack.push(closure); stack.push(ret); + stack.push(fx); } ClosureTag { name: _, @@ -2437,12 +2447,13 @@ impl Type { use Type::*; match self { - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { for arg in args { arg.substitute_alias(rep_symbol, rep_args, actual)?; } closure.substitute_alias(rep_symbol, rep_args, actual)?; - ret.substitute_alias(rep_symbol, rep_args, actual) + ret.substitute_alias(rep_symbol, rep_args, actual)?; + fx.substitute_alias(rep_symbol, rep_args, actual) } FunctionOrTagUnion(_, _, ext) => match ext { TypeExtension::Open(ext, _) => ext.substitute_alias(rep_symbol, rep_args, actual), @@ -2551,7 +2562,7 @@ impl Type { use Type::*; match self { - Function(args, closure, ret) => { + Function(args, closure, ret, _fx) => { ret.contains_symbol(rep_symbol) || closure.contains_symbol(rep_symbol) || args.iter().any(|arg| arg.contains_symbol(rep_symbol)) @@ -2615,10 +2626,11 @@ impl Type { match self { Variable(v) => *v == rep_variable, - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { ret.contains_variable(rep_variable) || closure.contains_variable(rep_variable) || args.iter().any(|arg| arg.contains_variable(rep_variable)) + || fx.contains_variable(rep_variable) } FunctionOrTagUnion(_, _, ext) => Self::contains_variable_ext(ext, rep_variable), ClosureTag { @@ -2761,8 +2773,11 @@ impl Type { } TypeExtension::Closed => fields.values().all(|field| field.as_inner().is_narrow()), }, - Type::Function(args, clos, ret) => { - args.iter().all(|a| a.is_narrow()) && clos.is_narrow() && ret.is_narrow() + Type::Function(args, clos, ret, fx) => { + args.iter().all(|a| a.is_narrow()) + && clos.is_narrow() + && ret.is_narrow() + && fx.is_narrow() } // Lists and sets are morally two-tagged unions, as they can be empty Type::Apply(Symbol::LIST_LIST | Symbol::SET_SET, _, _) => false, @@ -2801,12 +2816,13 @@ fn instantiate_aliases<'a, F>( use Type::*; match typ { - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { for arg in args { instantiate_aliases(arg, region, aliases, ctx); } instantiate_aliases(closure, region, aliases, ctx); instantiate_aliases(ret, region, aliases, ctx); + instantiate_aliases(fx, region, aliases, ctx); } FunctionOrTagUnion(_, _, ext) => { if let TypeExtension::Open(ext, _) = ext { @@ -2977,9 +2993,10 @@ fn symbols_help(initial: &Type) -> Vec { while let Some(tipe) = stack.pop() { match tipe { - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { stack.push(ret); stack.push(closure); + stack.push(fx); stack.extend(args); } FunctionOrTagUnion(_, _, ext) => { @@ -3046,12 +3063,13 @@ fn variables_help(tipe: &Type, accum: &mut ImSet) { accum.insert(*v); } - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { for arg in args { variables_help(arg, accum); } variables_help(closure, accum); variables_help(ret, accum); + variables_help(fx, accum); } Record(fields, ext) => { for (_, field) in fields { @@ -3175,7 +3193,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) { accum.type_variables.insert(*v); } - Function(args, closure, ret) => { + Function(args, closure, ret, fx) => { for arg in args { variables_help_detailed(arg, accum); } @@ -3186,6 +3204,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) { } variables_help_detailed(ret, accum); + variables_help_detailed(fx, accum); } Record(fields, ext) => { for (_, field) in fields { @@ -4407,7 +4426,7 @@ fn instantiate_lambda_sets_as_unspecialized( match typ { Type::EmptyRec => {} Type::EmptyTagUnion => {} - Type::Function(args, lambda_set, ret) => { + Type::Function(args, lambda_set, ret, fx) => { debug_assert!( matches!(**lambda_set, Type::Variable(..)), "lambda set already bound" @@ -4415,6 +4434,7 @@ fn instantiate_lambda_sets_as_unspecialized( **lambda_set = new_uls(); stack.push(ret); + stack.push(fx); stack.extend(args.iter_mut().rev()); } Type::Record(fields, ext) => { @@ -4497,16 +4517,20 @@ mod test { let l1 = Box::new(Type::Variable(var_store.fresh())); let l2 = Box::new(Type::Variable(var_store.fresh())); let l3 = Box::new(Type::Variable(var_store.fresh())); + let fx1 = Box::new(Type::Variable(var_store.fresh())); + let fx2 = Box::new(Type::Variable(var_store.fresh())); + let fx3 = Box::new(Type::Variable(var_store.fresh())); let mut typ = Type::Function( - vec![Type::Function(vec![], l2, Box::new(Type::EmptyRec))], + vec![Type::Function(vec![], l2, Box::new(Type::EmptyRec), fx1)], l1, Box::new(Type::TagUnion( vec![( TagName("A".into()), - vec![Type::Function(vec![], l3, Box::new(Type::EmptyRec))], + vec![Type::Function(vec![], l3, Box::new(Type::EmptyRec), fx2)], )], TypeExtension::Closed, )), + fx3, ); let able_var = var_store.fresh(); @@ -4527,11 +4551,11 @@ mod test { } match typ { - Type::Function(args, l1, ret) => { + Type::Function(args, l1, ret, _fx) => { check_uls!(*l1, 1); match args.as_slice() { - [Type::Function(args, l2, ret)] => { + [Type::Function(args, l2, ret, _fx)] => { check_uls!(**l2, 2); assert!(args.is_empty()); assert!(matches!(**ret, Type::EmptyRec)); @@ -4544,7 +4568,7 @@ mod test { [(name, args)] => { assert_eq!(name.0.as_str(), "A"); match args.as_slice() { - [Type::Function(args, l3, ret)] => { + [Type::Function(args, l3, ret, _fx)] => { check_uls!(**l3, 3); assert!(args.is_empty()); assert!(matches!(**ret, Type::EmptyRec)); From e8d7820f3437c42b1cf35a5fe5ec2b8e2951f96d Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sat, 5 Oct 2024 21:52:35 -0300 Subject: [PATCH 06/73] Add fx var to can's Call --- crates/compiler/can/src/copy.rs | 3 ++- crates/compiler/can/src/debug/pretty_print.rs | 2 +- crates/compiler/can/src/expr.rs | 11 ++++++++--- crates/compiler/can/src/traverse.rs | 2 +- crates/compiler/constrain/src/expr.rs | 10 +++++++--- crates/compiler/derive/src/decoding.rs | 2 ++ crates/compiler/derive/src/decoding/list.rs | 1 + crates/compiler/derive/src/decoding/record.rs | 3 +++ crates/compiler/derive/src/decoding/tuple.rs | 3 +++ crates/compiler/derive/src/encoding.rs | 10 ++++++++++ crates/compiler/derive/src/hash.rs | 1 + crates/compiler/derive/src/inspect.rs | 10 ++++++++++ crates/compiler/lower_params/src/lower.rs | 2 ++ crates/compiler/mono/src/ir.rs | 2 +- 14 files changed, 52 insertions(+), 10 deletions(-) diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 5507acbd6e2..bb8f4df9874 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -413,13 +413,14 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr } Call(f, args, called_via) => { - let (fn_var, fn_expr, clos_var, ret_var) = &**f; + let (fn_var, fn_expr, clos_var, ret_var, fx_var) = &**f; Call( Box::new(( sub!(*fn_var), fn_expr.map(|e| go_help!(e)), sub!(*clos_var), sub!(*ret_var), + sub!(*fx_var), )), args.iter() .map(|(var, expr)| (sub!(*var), expr.map(|e| go_help!(e)))) diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index 1ab6dec4774..00ea23b07e4 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -267,7 +267,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, .append(expr(c, Free, f, &body.value)) .group(), Call(fun, args, _) => { - let (_, fun, _, _) = &**fun; + let (_, fun, _, _, _) = &**fun; maybe_paren!( Free, p, diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 00ce483ed4e..aea53a55812 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -155,7 +155,7 @@ pub enum Expr { /// This is *only* for calling functions, not for tag application. /// The Tag variant contains any applied values inside it. Call( - Box<(Variable, Loc, Variable, Variable)>, + Box<(Variable, Loc, Variable, Variable, Variable)>, Vec<(Variable, Loc)>, CalledVia, ), @@ -930,6 +930,7 @@ pub fn canonicalize_expr<'a>( fn_expr, var_store.fresh(), var_store.fresh(), + var_store.fresh(), )), args, *application_style, @@ -969,6 +970,7 @@ pub fn canonicalize_expr<'a>( fn_expr, var_store.fresh(), var_store.fresh(), + var_store.fresh(), )), args, *application_style, @@ -2448,10 +2450,11 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { } Call(boxed_tuple, args, called_via) => { - let (fn_var, loc_expr, closure_var, expr_var) = *boxed_tuple; + let (fn_var, loc_expr, closure_var, expr_var, fx_var) = *boxed_tuple; match loc_expr.value { Var(symbol, _) if symbol.is_builtin() => { + // NOTE: This assumes builtins are not effectful! match builtin_defs_map(symbol, var_store) { Some(Def { loc_expr: @@ -2519,7 +2522,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { _ => { // For now, we only inline calls to builtins. Leave this alone! Call( - Box::new((fn_var, loc_expr, closure_var, expr_var)), + Box::new((fn_var, loc_expr, closure_var, expr_var, fx_var)), args, called_via, ) @@ -2787,6 +2790,7 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec) -> fn_expr, var_store.fresh(), var_store.fresh(), + var_store.fresh(), )), vec![ (var_store.fresh(), empty_string), @@ -2823,6 +2827,7 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec) -> fn_expr, var_store.fresh(), var_store.fresh(), + var_store.fresh(), )), vec![ (var_store.fresh(), loc_new_expr), diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index 744e1963b7e..dee8534a161 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -292,7 +292,7 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { visitor.visit_expr(&body.value, body.region, var); } Expr::Call(f, args, _called_via) => { - let (fn_var, loc_fn, _closure_var, _ret_var) = &**f; + let (fn_var, loc_fn, _closure_var, _ret_var, _fx_var) = &**f; walk_call(visitor, *fn_var, loc_fn, args); } Expr::Crash { msg, .. } => { diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index dc3ffd637e6..c7558d9d4a2 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -481,7 +481,7 @@ pub fn constrain_expr( } } Call(boxed, loc_args, called_via) => { - let (fn_var, loc_fn, closure_var, ret_var) = &**boxed; + let (fn_var, loc_fn, closure_var, ret_var, fx_var) = &**boxed; // The expression that evaluates to the function being called, e.g. `foo` in // (foo) bar baz let opt_symbol = if let Var(symbol, _) | AbilityMember(symbol, _, _) = loc_fn.value { @@ -512,6 +512,9 @@ pub fn constrain_expr( // The function's return type let ret_type = Variable(*ret_var); + // The function's effect type + let fx_type = Variable(*fx_var); + // type of values captured in the closure let closure_type = Variable(*closure_var); @@ -521,6 +524,7 @@ pub fn constrain_expr( vars.push(*fn_var); vars.push(*ret_var); vars.push(*closure_var); + vars.push(*fx_var); let mut arg_types = Vec::with_capacity(loc_args.len()); let mut arg_cons = Vec::with_capacity(loc_args.len()); @@ -555,8 +559,7 @@ pub fn constrain_expr( let arguments = types.from_old_type_slice(arg_types.iter()); let lambda_set = types.from_old_type(&closure_type); let ret = types.from_old_type(&ret_type); - // [purity-inference] TODO: Add fx var to call - let fx = types.from_old_type(&Type::Variable(Variable::PURE)); + let fx = types.from_old_type(&fx_type); let typ = types.function(arguments, lambda_set, ret, fx); constraints.push_type(types, typ) }; @@ -572,6 +575,7 @@ pub fn constrain_expr( constraints.equal_types_var(*fn_var, expected_fn_type, category.clone(), fn_region), constraints.and_constraint(arg_cons), constraints.equal_types_var(*ret_var, expected_final_type, category, region), + // [purity-inference] TODO: union with current function's fx var ]; let and_constraint = constraints.and_constraint(and_cons); diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 372539a1d60..7c825ba6f09 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -91,6 +91,7 @@ fn wrap_in_decode_custom_decode_with( Loc::at_zero(decode_with_var), this_decode_with_clos_var, this_decode_with_ret_var, + Variable::PURE, )); let decode_with_call = Call( decode_with_fn, @@ -198,6 +199,7 @@ fn wrap_in_decode_custom_decode_with( Loc::at_zero(decode_custom_var), this_decode_custom_clos_var, this_decode_custom_ret_var, + Variable::PURE, )); let decode_custom_call = Call( decode_custom_fn, diff --git a/crates/compiler/derive/src/decoding/list.rs b/crates/compiler/derive/src/decoding/list.rs index ac61ce0020f..4e9f2f65289 100644 --- a/crates/compiler/derive/src/decoding/list.rs +++ b/crates/compiler/derive/src/decoding/list.rs @@ -78,6 +78,7 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable Loc::at_zero(decode_list_member), this_decode_list_clos_var, this_decode_list_ret_var, + Variable::PURE, )); let decode_list_call = Call( diff --git a/crates/compiler/derive/src/decoding/record.rs b/crates/compiler/derive/src/decoding/record.rs index 2a7773ece80..2387f39e2df 100644 --- a/crates/compiler/derive/src/decoding/record.rs +++ b/crates/compiler/derive/src/decoding/record.rs @@ -130,6 +130,7 @@ pub(crate) fn decoder( )), decode_record_lambda_set, record_decoder_var, + Variable::PURE, )), vec![ (initial_state_var, Loc::at_zero(initial_state)), @@ -422,6 +423,7 @@ fn custom_decoder(env: &mut Env<'_>, args: DecodingFieldArgs) -> (Variable, Expr Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM, this_decode_custom_var)), decode_custom_closure_var, decode_custom_ret_var, + Variable::PURE, )), vec![(this_custom_callback_var, Loc::at_zero(custom_callback))], CalledVia::Space, @@ -1328,6 +1330,7 @@ pub(super) fn decode_with( Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH, this_decode_with_var)), lambda_set_var, rec_var, + Variable::PURE, )), vec![ (Variable::LIST_U8, Loc::at_zero(bytes_arg_expr)), diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index fbeb5a1f249..a57a24d31e9 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -120,6 +120,7 @@ pub(crate) fn decoder(env: &mut Env, _def_symbol: Symbol, arity: u32) -> (Expr, )), decode_record_lambda_set, tuple_decoder_var, + Variable::PURE, )), vec![ (state_var, Loc::at_zero(initial_state)), @@ -490,6 +491,7 @@ fn step_elem( Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH, this_decode_with_var)), lambda_set_var, rec_var, + Variable::PURE, )), vec![ ( @@ -598,6 +600,7 @@ fn step_elem( Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM, this_decode_custom_var)), decode_custom_closure_var, decode_custom_ret_var, + Variable::PURE, )), vec![(this_custom_callback_var, Loc::at_zero(custom_callback))], CalledVia::Space, diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index fca8b93a989..da59d3189ea 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -146,6 +146,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { Loc::at_zero(to_encoder_var), to_encoder_clos_var, elem_encoder_var, + Variable::PURE, )); // toEncoder elem @@ -231,6 +232,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { Loc::at_zero(encode_list), this_encode_list_clos_var, this_list_encoder_var, + Variable::PURE, )); // Encode.list lst to_elem_encoder @@ -370,6 +372,7 @@ fn to_encoder_record( Loc::at_zero(to_encoder_var), to_encoder_clos_var, encoder_var, + Variable::PURE, )); // toEncoder rcd.a @@ -451,6 +454,7 @@ fn to_encoder_record( Loc::at_zero(encode_record_var), encode_record_clos_var, encoder_var, + Variable::PURE, )); // Encode.record [ { key: .., value: .. }, .. ] @@ -574,6 +578,7 @@ fn to_encoder_tuple( Loc::at_zero(to_encoder_var), to_encoder_clos_var, encoder_var, + Variable::PURE, )); // toEncoder tup.0 @@ -635,6 +640,7 @@ fn to_encoder_tuple( Loc::at_zero(encode_tuple_var), encode_tuple_clos_var, encoder_var, + Variable::PURE, )); // Encode.tuple [ { key: .., value: .. }, .. ] @@ -776,6 +782,7 @@ fn to_encoder_tag_union( Loc::at_zero(to_encoder_var), to_encoder_clos_var, encoder_var, + Variable::PURE, )); // toEncoder rcd.a @@ -835,6 +842,7 @@ fn to_encoder_tag_union( Loc::at_zero(encode_tag_var), this_encode_tag_clos_var, this_encoder_var, + Variable::PURE, )); // Encode.tag "A" [ Encode.toEncoder v1, Encode.toEncoder v2 ] @@ -991,6 +999,7 @@ fn wrap_in_encode_custom( Loc::at_zero(Var(Symbol::ENCODE_APPEND_WITH, this_append_with_fn_var)), this_append_with_clos_var, Variable::LIST_U8, + Variable::PURE, )); // Encode.appendWith bytes encoder fmt @@ -1084,6 +1093,7 @@ fn wrap_in_encode_custom( Loc::at_zero(Var(Symbol::ENCODE_CUSTOM, this_custom_fn_var)), this_custom_clos_var, // -[clos]-> this_custom_encoder_var, // t' ~ Encoder fmt + Variable::PURE, )); // Encode.custom \bytes, fmt -> Encode.appendWith bytes encoder fmt diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index 7fcf34c93b9..58391ebdd33 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -489,6 +489,7 @@ fn call_hash_ability_member( Loc::at_zero(hash_fn_head), this_hash_clos_var, this_out_hasher_var, + Variable::PURE, )); let hash_arguments = vec![ diff --git a/crates/compiler/derive/src/inspect.rs b/crates/compiler/derive/src/inspect.rs index 4c900e61e80..a67b29ce1db 100644 --- a/crates/compiler/derive/src/inspect.rs +++ b/crates/compiler/derive/src/inspect.rs @@ -152,6 +152,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { Loc::at_zero(to_inspector_var), to_inspector_clos_var, elem_inspector_var, + Variable::PURE, )); // toInspector elem @@ -238,6 +239,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { Loc::at_zero(inspect_list), this_inspect_list_clos_var, this_list_inspector_var, + Variable::PURE, )); // Inspect.list lst to_elem_inspector @@ -382,6 +384,7 @@ fn to_inspector_record( Loc::at_zero(to_inspector_var), to_inspector_clos_var, inspector_var, + Variable::PURE, )); // toInspector rcd.a @@ -463,6 +466,7 @@ fn to_inspector_record( Loc::at_zero(inspect_record_var), inspect_record_clos_var, inspector_var, + Variable::PURE, )); // Inspect.record [ { key: .., value: .. }, .. ] @@ -587,6 +591,7 @@ fn to_inspector_tuple( Loc::at_zero(to_inspector_var), to_inspector_clos_var, inspector_var, + Variable::PURE, )); // toInspector tup.0 @@ -648,6 +653,7 @@ fn to_inspector_tuple( Loc::at_zero(inspect_tuple_var), inspect_tuple_clos_var, inspector_var, + Variable::PURE, )); // Inspect.tuple [ { key: .., value: .. }, .. ] @@ -789,6 +795,7 @@ fn to_inspector_tag_union( Loc::at_zero(to_inspector_var), to_inspector_clos_var, inspector_var, + Variable::PURE, )); // toInspector rcd.a @@ -852,6 +859,7 @@ fn to_inspector_tag_union( Loc::at_zero(inspect_tag_var), this_inspect_tag_clos_var, this_inspector_var, + Variable::PURE, )); // Inspect.tag "A" [ Inspect.toInspector v1, Inspect.toInspector v2 ] @@ -997,6 +1005,7 @@ fn wrap_in_inspect_custom( Loc::at_zero(Var(Symbol::INSPECT_APPLY, this_apply_fn_var)), this_apply_clos_var, fmt_var, + Variable::PURE, )); // Inspect.apply inspector fmt @@ -1081,6 +1090,7 @@ fn wrap_in_inspect_custom( Loc::at_zero(Var(Symbol::INSPECT_CUSTOM, this_custom_fn_var)), this_custom_clos_var, // -[clos]-> this_custom_inspector_var, // t' ~ Inspector fmt + Variable::PURE, )); // Inspect.custom \fmt -> Inspect.apply inspector fmt diff --git a/crates/compiler/lower_params/src/lower.rs b/crates/compiler/lower_params/src/lower.rs index ab54c5ab8c8..6365aadea58 100644 --- a/crates/compiler/lower_params/src/lower.rs +++ b/crates/compiler/lower_params/src/lower.rs @@ -520,6 +520,7 @@ impl<'a> LowerParams<'a> { Loc::at_zero(Var(symbol, var)), self.var_store.fresh(), self.var_store.fresh(), + self.var_store.fresh(), )); let body = Call( @@ -565,6 +566,7 @@ impl<'a> LowerParams<'a> { Loc::at_zero(Var(symbol, var)), self.var_store.fresh(), self.var_store.fresh(), + self.var_store.fresh(), )); Call( diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index a412b31f5c0..f9d4f635e97 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -5455,7 +5455,7 @@ pub fn with_hole<'a>( } Call(boxed, loc_args, _) => { - let (fn_var, loc_expr, _lambda_set_var, _ret_var) = *boxed; + let (fn_var, loc_expr, _lambda_set_var, _ret_var, _fx_var) = *boxed; // even if a call looks like it's by name, it may in fact be by-pointer. // E.g. in `(\f, x -> f x)` the call is in fact by pointer. From 7871ba182d9bea8e9aed1eb0e1d3bc231f5673a3 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sat, 5 Oct 2024 21:56:58 -0300 Subject: [PATCH 07/73] Remove irrelevant TODO --- crates/compiler/load_internal/src/docs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/compiler/load_internal/src/docs.rs b/crates/compiler/load_internal/src/docs.rs index b7ebbe9da43..541e4cc094a 100644 --- a/crates/compiler/load_internal/src/docs.rs +++ b/crates/compiler/load_internal/src/docs.rs @@ -444,7 +444,6 @@ fn contains_unexposed_type( Malformed(_) | Inferred | Wildcard | BoundVariable(_) => false, Function(loc_args, _arrow, loc_ret) => { - // [purity-inference] TODO: arrow let loc_args_contains_unexposed_type = loc_args.iter().any(|loc_arg| { contains_unexposed_type(&loc_arg.value, exposed_module_ids, module_ids) }); From 5a5abe3bc5b325c91b2eabbf1461ca91cb053c85 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 7 Oct 2024 21:14:08 -0300 Subject: [PATCH 08/73] Unify call's fx var with that of the enclosing function --- crates/compiler/can/src/constraint.rs | 12 +++- crates/compiler/can/src/copy.rs | 2 + crates/compiler/can/src/exhaustive.rs | 4 +- crates/compiler/can/src/scope.rs | 17 +++--- crates/compiler/checkmate/src/convert.rs | 2 + crates/compiler/constrain/src/expr.rs | 55 ++++++++++++------ crates/compiler/constrain/src/module.rs | 3 + crates/compiler/derive_key/src/decoding.rs | 1 + crates/compiler/derive_key/src/encoding.rs | 1 + crates/compiler/derive_key/src/hash.rs | 1 + crates/compiler/derive_key/src/inspect.rs | 4 +- crates/compiler/load/tests/helpers/mod.rs | 1 + crates/compiler/mono/src/ir.rs | 1 + crates/compiler/mono/src/layout.rs | 10 +++- crates/compiler/solve/src/ability.rs | 6 ++ crates/compiler/solve/src/deep_copy.rs | 2 +- crates/compiler/solve/src/solve.rs | 30 ++++++++++ crates/compiler/solve/src/specialize.rs | 2 + crates/compiler/types/src/pretty_print.rs | 13 ++++- crates/compiler/types/src/subs.rs | 44 +++++++++++++-- crates/compiler/unify/src/unify.rs | 65 +++++++++++++++++++++- crates/glue/src/load.rs | 8 ++- crates/glue/src/types.rs | 3 +- 23 files changed, 246 insertions(+), 41 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index d15f88280e8..5152ec6404e 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -606,7 +606,8 @@ impl Constraints { | Constraint::Resolve(..) | Constraint::IngestedFile(..) | Constraint::CheckCycle(..) - | Constraint::ImportParams(..) => false, + | Constraint::ImportParams(..) + | Constraint::CallFx(_, _) => false, } } @@ -698,6 +699,10 @@ impl Constraints { ) -> Constraint { Constraint::ImportParams(opt_type_index, module_id, region) } + + pub fn call_fx(&mut self, env_fx_var: Variable, call_fx_var: Variable) -> Constraint { + Constraint::CallFx(env_fx_var, call_fx_var) + } } roc_error_macros::assert_sizeof_default!(Constraint, 3 * 8); @@ -770,6 +775,8 @@ pub enum Constraint { Index, Region, ), + /// Unify the current function fx var with a call fx var + CallFx(Variable, Variable), /// Used for things that always unify, e.g. blanks and runtime errors True, SaveTheEnvironment, @@ -858,6 +865,9 @@ impl std::fmt::Debug for Constraint { Self::Pattern(arg0, arg1, arg2, arg3) => { write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } + Self::CallFx(arg0, arg1) => { + write!(f, "CallFx({arg0:?}, {arg1:?})") + } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(), diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index bb8f4df9874..df5e6d14f38 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -1183,6 +1183,8 @@ fn deep_copy_type_vars( }) } ErasedLambda => ErasedLambda, + Pure => Pure, + Effectful => Effectful, RangedNumber(range) => { perform_clone!(RangedNumber(range)) diff --git a/crates/compiler/can/src/exhaustive.rs b/crates/compiler/can/src/exhaustive.rs index 424be0b7476..73e2e316c13 100644 --- a/crates/compiler/can/src/exhaustive.rs +++ b/crates/compiler/can/src/exhaustive.rs @@ -141,7 +141,9 @@ fn index_var( | Content::RigidAbleVar(_, _) | Content::LambdaSet(_) | Content::ErasedLambda - | Content::RangedNumber(..) => return Err(TypeError), + | Content::RangedNumber(..) + | Content::Pure + | Content::Effectful => return Err(TypeError), Content::Error => return Err(TypeError), Content::RecursionVar { structure, diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index fe6608f4ffe..2ee5d47625a 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -538,14 +538,15 @@ pub fn create_alias( } if !hidden.is_empty() { - internal_error!( - "Found unbound type variables {:?} \n in type alias {:?} {:?} {:?} : {:?}", - hidden, - name, - &vars, - &infer_ext_in_output_variables, - &typ - ) + // [purity-inference] TODO: restore + // internal_error!( + // "Found unbound type variables {:?} \n in type alias {:?} {:?} {:?} : {:?}", + // hidden, + // name, + // &vars, + // &infer_ext_in_output_variables, + // &typ + // ) } true diff --git a/crates/compiler/checkmate/src/convert.rs b/crates/compiler/checkmate/src/convert.rs index 0cae1ab3346..6a133facabf 100644 --- a/crates/compiler/checkmate/src/convert.rs +++ b/crates/compiler/checkmate/src/convert.rs @@ -77,6 +77,8 @@ impl AsSchema for subs::Content { } => B::Recursive(opt_name.as_schema(subs), structure.as_schema(subs)), A::LambdaSet(lambda_set) => lambda_set.as_schema(subs), A::ErasedLambda => B::ErasedLambda(), + A::Pure => todo!("[purity-inference] checkmate"), + A::Effectful => todo!("[purity-inference] checkmate"), A::Structure(flat_type) => flat_type.as_schema(subs), A::Alias(name, type_vars, real_var, kind) => B::Alias( name.as_schema(subs), diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index c7558d9d4a2..196c767f33a 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -58,6 +58,25 @@ pub struct Env { pub rigids: MutMap, pub resolutions_to_make: Vec, pub home: ModuleId, + /// The enclosing function's fx var to be unified with inner calls + pub fn_fx_var: Option, +} + +impl Env { + pub fn with_fx(&mut self, fx_var: Variable, f: F) -> T + where + F: FnOnce(&mut Env) -> T, + { + let prev_fx_var = self.fn_fx_var; + + self.fn_fx_var = Some(fx_var); + + let result = f(self); + + self.fn_fx_var = prev_fx_var; + + result + } } fn constrain_untyped_args( @@ -149,15 +168,16 @@ fn constrain_untyped_closure( loc_body_expr.region, )); - // [purity-inference] TODO: constrain calls in body with fx_var - let ret_constraint = constrain_expr( - types, - constraints, - env, - loc_body_expr.region, - &loc_body_expr.value, - body_type, - ); + let ret_constraint = env.with_fx(fx_var, |env| { + constrain_expr( + types, + constraints, + env, + loc_body_expr.region, + &loc_body_expr.value, + body_type, + ) + }); let mut early_return_constraints = Vec::with_capacity(early_returns.len()); for (early_return_variable, early_return_region) in early_returns { @@ -575,7 +595,7 @@ pub fn constrain_expr( constraints.equal_types_var(*fn_var, expected_fn_type, category.clone(), fn_region), constraints.and_constraint(arg_cons), constraints.equal_types_var(*ret_var, expected_final_type, category, region), - // [purity-inference] TODO: union with current function's fx var + constraints.call_fx(env.fn_fx_var.unwrap_or(Variable::PURE), *fx_var), ]; let and_constraint = constraints.and_constraint(and_cons); @@ -1970,6 +1990,7 @@ fn constrain_function_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], + fn_fx_var: Some(function_def.fx_type), }; let region = loc_function_def.region; @@ -2221,6 +2242,7 @@ fn constrain_destructure_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], + fn_fx_var: env.fn_fx_var, }; let signature_index = constraints.push_type(types, signature); @@ -2323,6 +2345,7 @@ fn constrain_value_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], + fn_fx_var: env.fn_fx_var, }; let loc_pattern = Loc::at(loc_symbol.region, Pattern::Identifier(loc_symbol.value)); @@ -2610,6 +2633,7 @@ pub fn constrain_decls( home, rigids: MutMap::default(), resolutions_to_make: vec![], + fn_fx_var: None, }; debug_assert_eq!(declarations.declarations.len(), declarations.symbols.len()); @@ -2841,6 +2865,7 @@ fn constrain_typed_def( home: env.home, resolutions_to_make: vec![], rigids: ftv, + fn_fx_var: env.fn_fx_var, }; let signature_index = constraints.push_type(types, signature); @@ -3865,8 +3890,7 @@ fn constraint_recursive_function( constraints.push_type(types, typ) }; - let expr_con = { - // [purity-inference] TODO: constrain calls in body + let expr_con = env.with_fx(fx_var, |env| { let expected = constraints.push_expected_type(NoExpectation(ret_type_index)); constrain_expr( types, @@ -3876,7 +3900,7 @@ fn constraint_recursive_function( &loc_body_expr.value, expected, ) - }; + }); let expr_con = attach_resolution_constraints(constraints, env, expr_con); vars.push(expr_var); @@ -4415,10 +4439,9 @@ fn rec_defs_help( let typ = types.function(pattern_types, lambda_set, ret_type, fx_type); constraints.push_type(types, typ) }; - let expr_con = { + let expr_con = env.with_fx(fx_var, |env| { let body_type = constraints.push_expected_type(NoExpectation(ret_type_index)); - // [purity-inference] TODO: unify calls in body with the fx_type constrain_expr( types, @@ -4428,7 +4451,7 @@ fn rec_defs_help( &loc_body_expr.value, body_type, ) - }; + }); let expr_con = attach_resolution_constraints(constraints, env, expr_con); vars.push(*fn_var); diff --git a/crates/compiler/constrain/src/module.rs b/crates/compiler/constrain/src/module.rs index bb0d3a9d5f8..cd20d58fd67 100644 --- a/crates/compiler/constrain/src/module.rs +++ b/crates/compiler/constrain/src/module.rs @@ -56,6 +56,7 @@ fn constrain_params( home, rigids: MutMap::default(), resolutions_to_make: vec![], + fn_fx_var: None, }; let index = constraints.push_variable(module_params.whole_var); @@ -114,6 +115,7 @@ fn constrain_symbols_from_requires( home, rigids, resolutions_to_make: vec![], + fn_fx_var: None, }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(loc_symbol.value)); @@ -181,6 +183,7 @@ pub fn frontload_ability_constraints( home, rigids, resolutions_to_make: vec![], + fn_fx_var: None, }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(*member_name)); diff --git a/crates/compiler/derive_key/src/decoding.rs b/crates/compiler/derive_key/src/decoding.rs index e1129b6e222..e819209cffd 100644 --- a/crates/compiler/derive_key/src/decoding.rs +++ b/crates/compiler/derive_key/src/decoding.rs @@ -101,6 +101,7 @@ impl FlatDecodable { | Content::FlexAbleVar(_, _) | Content::RigidAbleVar(_, _) => Err(UnboundVar), Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable), + Content::Pure | Content::Effectful => Err(Underivable), } } diff --git a/crates/compiler/derive_key/src/encoding.rs b/crates/compiler/derive_key/src/encoding.rs index 8c3608f69da..39dee1f5540 100644 --- a/crates/compiler/derive_key/src/encoding.rs +++ b/crates/compiler/derive_key/src/encoding.rs @@ -135,6 +135,7 @@ impl FlatEncodable { | Content::FlexAbleVar(_, _) | Content::RigidAbleVar(_, _) => Err(UnboundVar), Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable), + Content::Pure | Content::Effectful => Err(Underivable), } } diff --git a/crates/compiler/derive_key/src/hash.rs b/crates/compiler/derive_key/src/hash.rs index edc5ffbb01b..9502abfef34 100644 --- a/crates/compiler/derive_key/src/hash.rs +++ b/crates/compiler/derive_key/src/hash.rs @@ -146,6 +146,7 @@ impl FlatHash { | Content::FlexAbleVar(_, _) | Content::RigidAbleVar(_, _) => Err(UnboundVar), Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable), + Content::Pure | Content::Effectful => Err(Underivable), } } diff --git a/crates/compiler/derive_key/src/inspect.rs b/crates/compiler/derive_key/src/inspect.rs index 0f3ab9c16e7..30470b9bf7c 100644 --- a/crates/compiler/derive_key/src/inspect.rs +++ b/crates/compiler/derive_key/src/inspect.rs @@ -178,7 +178,9 @@ impl FlatInspectable { | Content::FlexAbleVar(_, _) | Content::RigidAbleVar(_, _) | Content::LambdaSet(_) - | Content::ErasedLambda => { + | Content::ErasedLambda + | Content::Pure + | Content::Effectful => { unreachable!("There must have been a bug in the solver, because we're trying to derive Inspect on a non-concrete type."); } } diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index c3a6fff90b0..79235d7a4ef 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -203,6 +203,7 @@ pub fn can_expr_with<'a>( rigids: MutMap::default(), home, resolutions_to_make: vec![], + fn_fx_var: None, }, loc_expr.region, &loc_expr.value, diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index f9d4f635e97..a52a8ba3b45 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -10188,6 +10188,7 @@ fn find_lambda_sets_help( } } Content::ErasedLambda => {} + Content::Pure | Content::Effectful => {} } } diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 0bd719b3074..49e9b8953a2 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -515,6 +515,7 @@ impl<'a> RawFunctionLayout<'a> { internal_error!("lambda set should only appear under a function, where it's handled independently."); } ErasedLambda => internal_error!("erased lambda type should only appear under a function, where it's handled independently"), + Pure|Effectful => internal_error!("fx vars should not be pure or effectful"), Structure(flat_type) => Self::layout_from_flat_type(env, flat_type), RangedNumber(..) => Layout::new_help(env, var, content).then(Self::ZeroArgumentThunk), @@ -2199,7 +2200,9 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) { | Content::RigidAbleVar(_, _) | Content::RangedNumber(_) | Content::Error - | Content::ErasedLambda => {} + | Content::ErasedLambda + | Content::Pure + | Content::Effectful => {} } } (max_depth_any_ctor, max_depth_only_lset, total) @@ -2479,6 +2482,9 @@ impl<'a> Layout<'a> { ErasedLambda => { internal_error!("erased lambda type should only appear under a function, where it's handled independently."); } + Pure | Effectful => { + internal_error!("fx vars should only appear under a function, where they're handled independently."); + } Structure(flat_type) => layout_from_flat_type(env, flat_type), Alias(symbol, _args, actual_var, _) => { @@ -4598,7 +4604,7 @@ fn layout_from_num_content<'a>(content: &Content) -> Cacheable> Alias(_, _, _, _) => { todo!("TODO recursively resolve type aliases in num_from_content"); } - Structure(_) | RangedNumber(..) | LambdaSet(_) | ErasedLambda => { + Structure(_) | RangedNumber(..) | LambdaSet(_) | ErasedLambda | Pure | Effectful => { panic!("Invalid Num.Num type application: {content:?}"); } Error => Err(LayoutProblem::Erroneous), diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 64ab0fb9e7a..32a264b26fc 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -829,6 +829,12 @@ trait DerivableVisitor { context: NotDerivableContext::NoContext, }) } + Pure | Effectful => { + return Err(NotDerivable { + var, + context: NotDerivableContext::NoContext, + }) + } Error => { return Err(NotDerivable { var, diff --git a/crates/compiler/solve/src/deep_copy.rs b/crates/compiler/solve/src/deep_copy.rs index 74ed6104b37..5493e584622 100644 --- a/crates/compiler/solve/src/deep_copy.rs +++ b/crates/compiler/solve/src/deep_copy.rs @@ -263,7 +263,7 @@ fn deep_copy_var_help( subs.set_content_unchecked(copy, Structure(new_flat_type)); } - FlexVar(_) | FlexAbleVar(_, _) | Error | ErasedLambda => { + FlexVar(_) | FlexAbleVar(_, _) | Error | ErasedLambda | Pure | Effectful => { subs.set_content_unchecked(copy, content); } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 0b90fbb1c97..13d7a3aeb3d 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -559,6 +559,34 @@ fn solve( } } } + CallFx(env_call_fx, call_fx_var) => { + match unify( + &mut env.uenv(), + *env_call_fx, + *call_fx_var, + UnificationMode::EQ, + Polarity::OF_VALUE, + ) { + Success { + vars, + must_implement_ability, + lambda_sets_to_specialize, + extra_metadata: _, + } => { + env.introduce(rank, &vars); + + debug_assert!(must_implement_ability.is_empty()); + debug_assert!(lambda_sets_to_specialize.is_empty()); + } + Failure(vars, _actual_type, _exected_type, _bad_impls) => { + env.introduce(rank, &vars); + + todo!("[purity-inference] can this actually happen?"); + } + } + + state + } Store(source_index, target, _filename, _linenr) => { // a special version of Eq that is used to store types in the AST. // IT DOES NOT REPORT ERRORS! @@ -2429,6 +2457,8 @@ fn adjust_rank_content( ErasedLambda => group_rank, + Pure | Effectful => group_rank, + RangedNumber(_) => group_rank, } } diff --git a/crates/compiler/solve/src/specialize.rs b/crates/compiler/solve/src/specialize.rs index abf299830c7..5f27f27c1d5 100644 --- a/crates/compiler/solve/src/specialize.rs +++ b/crates/compiler/solve/src/specialize.rs @@ -667,6 +667,8 @@ fn make_specialization_decision( | RigidVar(..) | LambdaSet(..) | ErasedLambda + | Pure + | Effectful | RangedNumber(..) => { internal_error!("unexpected") } diff --git a/crates/compiler/types/src/pretty_print.rs b/crates/compiler/types/src/pretty_print.rs index d024ed01ee0..53d77b8728b 100644 --- a/crates/compiler/types/src/pretty_print.rs +++ b/crates/compiler/types/src/pretty_print.rs @@ -399,7 +399,12 @@ fn find_names_needed( find_under_alias, ); } - Error | Structure(EmptyRecord) | Structure(EmptyTagUnion) | ErasedLambda => { + Error + | Structure(EmptyRecord) + | Structure(EmptyTagUnion) + | Pure + | Effectful + | ErasedLambda => { // Errors and empty records don't need names. } } @@ -869,6 +874,12 @@ fn write_content<'a>( // Easy mode 🤠 buf.push('?'); } + Pure => { + buf.push_str("->"); + } + Effectful => { + buf.push_str("=>"); + } RangedNumber(range) => { buf.push_str("Range("); for (i, &var) in range.variable_slice().iter().enumerate() { diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index e6a703e69f1..2d354c8c40b 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -711,6 +711,8 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt: write!(f, ", ^<{ambient_function_var:?}>)") } Content::ErasedLambda => write!(f, "ErasedLambda"), + Content::Pure => write!(f, "PureFunction"), + Content::Effectful => write!(f, "EffectfulFunction"), Content::RangedNumber(range) => { write!(f, "RangedNumber( {range:?})") } @@ -1635,6 +1637,8 @@ impl Subs { ); subs.set_content(Variable::ERASED_LAMBDA, Content::ErasedLambda); + subs.set_content(Variable::PURE, Content::Pure); + subs.set_content(Variable::EFFECTFUL, Content::Effectful); subs } @@ -2151,6 +2155,7 @@ impl Subs { | Content::RangedNumber(_) | Content::Error => return false, Content::LambdaSet(_) | Content::ErasedLambda => return false, + Content::Pure | Content::Effectful => return false, Content::Structure(FlatType::Func(..)) => return true, Content::Structure(_) => return false, Content::Alias(_, _, real_var, _) => { @@ -2322,6 +2327,9 @@ pub enum Content { Alias(Symbol, AliasVariables, Variable, AliasKind), RangedNumber(crate::num::NumericRange), Error, + /// The fx type variable for a given function + Pure, + Effectful, } /// Stores the lambdas an arrow might pass through; for example @@ -3507,6 +3515,7 @@ fn occurs( occurs_union(subs, root_var, ctx, safe!(UnionLabels, solved)) } ErasedLambda => Ok(()), + Pure | Effectful => Ok(()), RangedNumber(_range_vars) => Ok(()), })(); @@ -3593,7 +3602,9 @@ fn explicit_substitute( | RigidAbleVar(_, _) | RecursionVar { .. } | Error - | ErasedLambda => in_var, + | ErasedLambda + | Pure + | Effectful => in_var, Structure(flat_type) => { match flat_type { @@ -3774,7 +3785,9 @@ fn get_var_names( subs.set_mark(var, Mark::GET_VAR_NAMES); match desc.content { - Error | FlexVar(None) | FlexAbleVar(None, _) | ErasedLambda => taken_names, + Error | FlexVar(None) | FlexAbleVar(None, _) | ErasedLambda | Pure | Effectful => { + taken_names + } FlexVar(Some(name_index)) | FlexAbleVar(Some(name_index), _) => add_name( subs, @@ -4093,6 +4106,11 @@ fn content_to_err_type( ErrorType::Error } + Pure | Effectful => { + // Not exposed directly + ErrorType::Error + } + RangedNumber(range) => { if state.context == ErrorTypeContext::ExpandRanges { let mut types = Vec::new(); @@ -4695,6 +4713,8 @@ impl StorageSubs { ambient_function: Self::offset_variable(offsets, *ambient_function_var), }), ErasedLambda => ErasedLambda, + Pure => Pure, + Effectful => Effectful, RangedNumber(range) => RangedNumber(*range), Error => Content::Error, } @@ -5058,7 +5078,7 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) -> copy } - FlexVar(None) | ErasedLambda | Error => copy, + FlexVar(None) | ErasedLambda | Error | Pure | Effectful => copy, RecursionVar { opt_name, @@ -5293,6 +5313,7 @@ fn is_registered(content: &Content) -> bool { | Content::RigidAbleVar(..) => false, Content::Structure(FlatType::EmptyRecord | FlatType::EmptyTagUnion) => false, Content::ErasedLambda => false, + Content::Pure | Content::Effectful => todo!("[purity-inference]"), Content::Structure(_) | Content::RecursionVar { .. } @@ -5690,6 +5711,16 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl copy } + Pure => { + env.target.set(copy, make_descriptor(Pure)); + copy + } + + Effectful => { + env.target.set(copy, make_descriptor(Effectful)); + copy + } + RangedNumber(range) => { let new_content = RangedNumber(range); @@ -5764,7 +5795,7 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) { } }) } - FlexVar(_) | FlexAbleVar(_, _) | ErasedLambda | Error => (), + FlexVar(_) | FlexAbleVar(_, _) | ErasedLambda | Error | Pure | Effectful => (), RecursionVar { structure, .. } => { stack.push(*structure); @@ -5952,7 +5983,9 @@ pub fn get_member_lambda_sets_at_region(subs: &Subs, var: Variable, target_regio structure: _, opt_name: _, } - | Content::ErasedLambda => {} + | Content::ErasedLambda + | Content::Pure + | Content::Effectful => {} } } @@ -5978,6 +6011,7 @@ fn is_inhabited(subs: &Subs, var: Variable) -> bool { // cannot have a tag union without a non-recursive variant. | Content::RecursionVar { .. } => {} Content::LambdaSet(_) | Content::ErasedLambda => {} + Content::Pure | Content::Effectful => {} Content::Structure(structure) => match structure { FlatType::Apply(_, args) => stack.extend(subs.get_subs_slice(*args)), FlatType::Func(args, _, ret) => { diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index eb745dd3aa5..badfc244141 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -510,6 +510,8 @@ fn unify_context(env: &mut Env, pool: &mut Pool, ctx: Context) } LambdaSet(lset) => unify_lambda_set(env, pool, &ctx, *lset, &ctx.second_desc.content), ErasedLambda => unify_erased_lambda(env, pool, &ctx, &ctx.second_desc.content), + Pure => unify_pure(env, &ctx, &ctx.second_desc.content), + Effectful => unify_effectful(env, &ctx, &ctx.second_desc.content), &RangedNumber(range_vars) => unify_ranged_number(env, pool, &ctx, range_vars), Error => { // Error propagates. Whatever we're comparing it to doesn't matter! @@ -580,6 +582,7 @@ fn unify_ranged_number( None => not_in_range_mismatch(), }, LambdaSet(..) | ErasedLambda => mismatch!(), + Pure | Effectful => mismatch!("Cannot unify RangedNumber with fx var"), Error => merge(env, ctx, Error), } } @@ -901,6 +904,7 @@ fn unify_alias( } LambdaSet(..) => mismatch!("cannot unify alias {:?} with lambda set {:?}: lambda sets should never be directly behind an alias!", ctx.first, other_content), ErasedLambda => mismatch!("cannot unify alias {:?} with an erased lambda!", ctx.first), + Pure|Effectful => mismatch!("cannot unify alias {:?} with an fx var!", ctx.first), Error => merge(env, ctx, Error), } } @@ -1100,6 +1104,7 @@ fn unify_structure( ) } ErasedLambda => mismatch!(), + Pure | Effectful => mismatch!("Cannot unify structure {:?} with fx vars", &flat_type), RangedNumber(other_range_vars) => { check_and_merge_valid_range(env, pool, ctx, ctx.second, *other_range_vars, ctx.first) } @@ -1135,6 +1140,50 @@ fn unify_erased_lambda( Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"), RangedNumber(..) => mismatch!("Lambda sets are never numbers"), Alias(..) => mismatch!("Lambda set can never be directly under an alias!"), + Pure | Effectful => mismatch!("Lambda set cannot unify with fx vars"), + Error => merge(env, ctx, Error), + } +} + +#[inline(always)] +#[must_use] +fn unify_pure(env: &mut Env, ctx: &Context, other: &Content) -> Outcome { + match other { + Pure => merge(env, ctx, Pure), + Effectful => merge(env, ctx, Effectful), + FlexVar(_) => merge(env, ctx, Pure), + RigidVar(_) + | FlexAbleVar(_, _) + | RigidAbleVar(_, _) + | RecursionVar { .. } + | Content::LambdaSet(_) + | ErasedLambda + | Structure(_) + | Alias(_, _, _, _) + | RangedNumber(_) => { + mismatch!("Cannot unify pure with {:?}", other) + } + Error => merge(env, ctx, Error), + } +} + +#[inline(always)] +#[must_use] +fn unify_effectful(env: &mut Env, ctx: &Context, other: &Content) -> Outcome { + match other { + Pure | Effectful => merge(env, ctx, Effectful), + FlexVar(_) => merge(env, ctx, Effectful), + RigidVar(_) + | FlexAbleVar(_, _) + | RigidAbleVar(_, _) + | RecursionVar { .. } + | Content::LambdaSet(_) + | ErasedLambda + | Structure(_) + | Alias(_, _, _, _) + | RangedNumber(_) => { + mismatch!("Cannot unify effectful with {:?}", other) + } Error => merge(env, ctx, Error), } } @@ -1190,6 +1239,7 @@ fn unify_lambda_set( Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"), RangedNumber(..) => mismatch!("Lambda sets are never numbers"), Alias(..) => mismatch!("Lambda set can never be directly under an alias!"), + Pure | Effectful => mismatch!("Lambda sets never unify with fx vars"), Error => merge(env, ctx, Error), } } @@ -3435,7 +3485,9 @@ fn unify_rigid( | Structure(_) | Alias(..) | LambdaSet(..) - | ErasedLambda => { + | ErasedLambda + | Pure + | Effectful => { // Type mismatch! Rigid can only unify with flex, even if the // rigid names are the same. mismatch!("Rigid {:?} with {:?}", ctx.first, &other) @@ -3496,7 +3548,9 @@ fn unify_rigid_able( | Alias(..) | RangedNumber(..) | LambdaSet(..) - | ErasedLambda => { + | ErasedLambda + | Pure + | Effectful => { // Type mismatch! Rigid can only unify with flex, even if the // rigid names are the same. mismatch!("Rigid {:?} with {:?}", ctx.first, &other) @@ -3537,7 +3591,9 @@ fn unify_flex( | Alias(_, _, _, _) | RangedNumber(..) | LambdaSet(..) - | ErasedLambda => { + | ErasedLambda + | Pure + | Effectful => { // TODO special-case boolean here // In all other cases, if left is flex, defer to right. merge(env, ctx, *other) @@ -3633,6 +3689,7 @@ fn unify_flex_able( RigidVar(_) => mismatch!("FlexAble can never unify with non-able Rigid"), LambdaSet(..) | ErasedLambda => mismatch!("FlexAble with LambdaSet"), + Pure | Effectful => mismatch!("FlexAble with fx var"), Alias(name, _args, _real_var, AliasKind::Opaque) => { // Opaque type wins @@ -3801,6 +3858,8 @@ fn unify_recursion( ErasedLambda => mismatch!(), + Pure | Effectful => mismatch!("RecursionVar with fx var"), + Error => merge(env, ctx, Error), }; diff --git a/crates/glue/src/load.rs b/crates/glue/src/load.rs index d0ceadd38d7..6d0d46262b6 100644 --- a/crates/glue/src/load.rs +++ b/crates/glue/src/load.rs @@ -297,7 +297,13 @@ fn number_lambda_sets(subs: &Subs, initial: Variable) -> Vec { use roc_types::types::Uls; match subs.get_content_without_compacting(var) { - RigidVar(_) | RigidAbleVar(_, _) | FlexVar(_) | FlexAbleVar(_, _) | Error => (), + RigidVar(_) + | RigidAbleVar(_, _) + | FlexVar(_) + | FlexAbleVar(_, _) + | Pure + | Effectful + | Error => (), RecursionVar { .. } => { // we got here, so we've treated this type already diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index f5febcde874..dcbed74539d 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -8,7 +8,7 @@ use roc_builtins::bitcode::{ IntWidth::{self, *}, }; use roc_collections::{MutMap, VecMap}; -use roc_error_macros::todo_lambda_erasure; +use roc_error_macros::{internal_error, todo_lambda_erasure}; use roc_module::{ ident::TagName, symbol::{Interns, Symbol}, @@ -1595,6 +1595,7 @@ fn add_type_help<'a>( type_id } Content::ErasedLambda => todo_lambda_erasure!(), + Content::Pure | Content::Effectful => internal_error!("fx vars should not appear here"), Content::LambdaSet(lambda_set) => { let tags = lambda_set.solved; From 777688326240e3f6b0880c5e91e8b4e6fc27bc3b Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 8 Oct 2024 20:59:10 -0300 Subject: [PATCH 09/73] Unify functions fx vars --- crates/compiler/can/src/copy.rs | 10 +++- crates/compiler/can/src/exhaustive.rs | 2 +- crates/compiler/checkmate/src/convert.rs | 3 +- crates/compiler/derive/src/decoding.rs | 3 + crates/compiler/derive/src/decoding/list.rs | 1 + crates/compiler/derive/src/decoding/record.rs | 25 ++++++-- crates/compiler/derive/src/decoding/tuple.rs | 25 ++++++-- crates/compiler/derive/src/encoding.rs | 22 ++++++- crates/compiler/derive/src/hash.rs | 8 ++- crates/compiler/derive/src/inspect.rs | 22 ++++++- crates/compiler/load_internal/src/file.rs | 7 ++- crates/compiler/mono/src/ir.rs | 17 ++++-- crates/compiler/mono/src/layout.rs | 6 +- crates/compiler/solve/src/ability.rs | 3 +- crates/compiler/solve/src/deep_copy.rs | 5 +- crates/compiler/solve/src/module.rs | 4 +- crates/compiler/solve/src/solve.rs | 2 +- crates/compiler/solve/src/to_var.rs | 3 +- crates/compiler/types/src/pretty_print.rs | 22 +++++-- crates/compiler/types/src/subs.rs | 60 ++++++++++++------- crates/compiler/unify/src/fix.rs | 8 ++- crates/compiler/unify/src/unify.rs | 19 ++++-- crates/glue/src/load.rs | 2 +- crates/glue/src/types.rs | 6 +- .../src/analysis/completion.rs | 3 +- crates/repl_eval/src/eval.rs | 4 +- 26 files changed, 222 insertions(+), 70 deletions(-) diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index df5e6d14f38..8fd743c0a76 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -986,15 +986,21 @@ fn deep_copy_type_vars( Structure(Apply(symbol, new_arguments)) }) } - Func(arguments, closure_var, ret_var) => { + Func(arguments, closure_var, ret_var, fx_var) => { descend_slice!(arguments); let new_closure_var = descend_var!(closure_var); let new_ret_var = descend_var!(ret_var); + let new_fx_var = descend_var!(fx_var); perform_clone!({ let new_arguments = clone_var_slice!(arguments); - Structure(Func(new_arguments, new_closure_var, new_ret_var)) + Structure(Func( + new_arguments, + new_closure_var, + new_ret_var, + new_fx_var, + )) }) } Record(fields, ext_var) => { diff --git a/crates/compiler/can/src/exhaustive.rs b/crates/compiler/can/src/exhaustive.rs index 73e2e316c13..705cfdc8ea0 100644 --- a/crates/compiler/can/src/exhaustive.rs +++ b/crates/compiler/can/src/exhaustive.rs @@ -152,7 +152,7 @@ fn index_var( var = *structure; } Content::Structure(structure) => match structure { - FlatType::Func(_, _, _) => return Err(TypeError), + FlatType::Func(_, _, _, _) => return Err(TypeError), FlatType::Apply(Symbol::LIST_LIST, args) => { match (subs.get_subs_slice(*args), ctor) { ([elem_var], IndexCtor::List) => { diff --git a/crates/compiler/checkmate/src/convert.rs b/crates/compiler/checkmate/src/convert.rs index 6a133facabf..854613dfdd1 100644 --- a/crates/compiler/checkmate/src/convert.rs +++ b/crates/compiler/checkmate/src/convert.rs @@ -98,10 +98,11 @@ impl AsSchema for subs::FlatType { subs::FlatType::Apply(symbol, variables) => { Content::Apply(symbol.as_schema(subs), variables.as_schema(subs)) } - subs::FlatType::Func(arguments, closure, ret) => Content::Function( + subs::FlatType::Func(arguments, closure, ret, _fx) => Content::Function( arguments.as_schema(subs), closure.as_schema(subs), ret.as_schema(subs), + // [purity-inference] TODO: checkmate ), subs::FlatType::Record(fields, ext) => { Content::Record(fields.as_schema(subs), ext.as_schema(subs)) diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 7c825ba6f09..03f83f331b5 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -78,6 +78,7 @@ fn wrap_in_decode_custom_decode_with( this_decode_with_var_slice, this_decode_with_clos_var, this_decode_with_ret_var, + Variable::PURE, )), ); @@ -140,6 +141,7 @@ fn wrap_in_decode_custom_decode_with( args_slice, fn_clos_var, decode_with_result_var, + Variable::PURE, )), ); @@ -186,6 +188,7 @@ fn wrap_in_decode_custom_decode_with( this_decode_custom_args, this_decode_custom_clos_var, this_decode_custom_ret_var, + Variable::PURE, )), ); diff --git a/crates/compiler/derive/src/decoding/list.rs b/crates/compiler/derive/src/decoding/list.rs index 4e9f2f65289..20134be7c57 100644 --- a/crates/compiler/derive/src/decoding/list.rs +++ b/crates/compiler/derive/src/decoding/list.rs @@ -65,6 +65,7 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable elem_decoder_var_slice, this_decode_list_clos_var, this_decode_list_ret_var, + Variable::PURE, )), ); diff --git a/crates/compiler/derive/src/decoding/record.rs b/crates/compiler/derive/src/decoding/record.rs index 2387f39e2df..97974bd5aae 100644 --- a/crates/compiler/derive/src/decoding/record.rs +++ b/crates/compiler/derive/src/decoding/record.rs @@ -112,6 +112,7 @@ pub(crate) fn decoder( .insert_into_vars([initial_state_var, step_var, finalizer_var]), decode_record_lambda_set, record_decoder_var, + Variable::PURE, ); synth_var(env.subs, Content::Structure(flat_type)) @@ -343,7 +344,12 @@ pub(super) fn step_field( env.subs.set_content( function_type, - Content::Structure(FlatType::Func(args_slice, closure_type, keep_or_skip_var)), + Content::Structure(FlatType::Func( + args_slice, + closure_type, + keep_or_skip_var, + Variable::PURE, + )), ) }; @@ -408,8 +414,12 @@ fn custom_decoder(env: &mut Env<'_>, args: DecodingFieldArgs) -> (Variable, Expr let decode_custom_closure_var = env.subs.fresh_unnamed_flex_var(); let this_decode_custom_var = { let subs_slice = env.subs.insert_into_vars([this_custom_callback_var]); - let flat_type = - FlatType::Func(subs_slice, decode_custom_closure_var, decode_custom_ret_var); + let flat_type = FlatType::Func( + subs_slice, + decode_custom_closure_var, + decode_custom_ret_var, + Variable::PURE, + ); synth_var(env.subs, Content::Structure(flat_type)) }; @@ -579,6 +589,7 @@ fn custom_decoder_lambda(env: &mut Env<'_>, args: DecodingFieldArgs) -> (Variabl subs_slice, custom_callback_lambda_set_var, custom_callback_ret_var, + Variable::PURE, )), ); @@ -989,6 +1000,7 @@ pub(super) fn finalizer( env.subs.insert_into_vars([state_record_var, fmt_arg_var]), closure_type, return_type_var, + Variable::PURE, ); // Fix up function_var so it's not Content::Error anymore @@ -1280,7 +1292,12 @@ fn make_decode_with_vars( .insert_into_vars([bytes_arg_var, decoder_var, fmt_arg_var]); let this_decode_with_var = synth_var( env.subs, - Content::Structure(FlatType::Func(subs_slice, lambda_set_var, rec_var)), + Content::Structure(FlatType::Func( + subs_slice, + lambda_set_var, + rec_var, + Variable::PURE, + )), ); env.unify(decode_with_var, this_decode_with_var); diff --git a/crates/compiler/derive/src/decoding/tuple.rs b/crates/compiler/derive/src/decoding/tuple.rs index a57a24d31e9..7dc7dd9a430 100644 --- a/crates/compiler/derive/src/decoding/tuple.rs +++ b/crates/compiler/derive/src/decoding/tuple.rs @@ -102,6 +102,7 @@ pub(crate) fn decoder(env: &mut Env, _def_symbol: Symbol, arity: u32) -> (Expr, .insert_into_vars([state_var, step_var, finalizer_var]), decode_record_lambda_set, tuple_decoder_var, + Variable::PURE, ); synth_var(env.subs, Content::Structure(flat_type)) @@ -277,7 +278,12 @@ fn step_elem( .insert_into_vars([bytes_arg_var, decoder_var, fmt_arg_var]); let this_decode_with_var = synth_var( env.subs, - Content::Structure(FlatType::Func(subs_slice, lambda_set_var, rec_var)), + Content::Structure(FlatType::Func( + subs_slice, + lambda_set_var, + rec_var, + Variable::PURE, + )), ); env.unify(decode_with_var, this_decode_with_var); @@ -547,6 +553,7 @@ fn step_elem( subs_slice, custom_callback_lambda_set_var, custom_callback_ret_var, + Variable::PURE, )), ); @@ -585,8 +592,12 @@ fn step_elem( let decode_custom_closure_var = env.subs.fresh_unnamed_flex_var(); let this_decode_custom_var = { let subs_slice = env.subs.insert_into_vars([this_custom_callback_var]); - let flat_type = - FlatType::Func(subs_slice, decode_custom_closure_var, decode_custom_ret_var); + let flat_type = FlatType::Func( + subs_slice, + decode_custom_closure_var, + decode_custom_ret_var, + Variable::PURE, + ); synth_var(env.subs, Content::Structure(flat_type)) }; @@ -707,7 +718,12 @@ fn step_elem( env.subs.set_content( function_type, - Content::Structure(FlatType::Func(args_slice, closure_type, keep_or_skip_var)), + Content::Structure(FlatType::Func( + args_slice, + closure_type, + keep_or_skip_var, + Variable::PURE, + )), ) }; @@ -893,6 +909,7 @@ fn finalizer( env.subs.insert_into_vars([state_record_var]), closure_type, return_type_var, + Variable::PURE, ); // Fix up function_var so it's not Content::Error anymore diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index da59d3189ea..fe8be02a8ea 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -132,6 +132,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { elem_var_slice, to_encoder_clos_var, elem_encoder_var, + Variable::PURE, )), ); @@ -181,6 +182,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { elem_var_slice, to_elem_encoder_lset, elem_encoder_var, + Variable::PURE, )), ); @@ -218,6 +220,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { this_encode_list_args_slice, this_encode_list_clos_var, this_list_encoder_var, + Variable::PURE, )), ); @@ -277,6 +280,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { list_var_slice, fn_clos_var, this_encoder_var, + Variable::PURE, )), ); @@ -358,6 +362,7 @@ fn to_encoder_record( field_var_slice, to_encoder_clos_var, encoder_var, + Variable::PURE, )), ); @@ -440,6 +445,7 @@ fn to_encoder_record( fields_list_var_slice, encode_record_clos_var, encoder_var, + Variable::PURE, )), ); @@ -490,6 +496,7 @@ fn to_encoder_record( record_var_slice, fn_clos_var, this_encoder_var, + Variable::PURE, )), ); @@ -564,6 +571,7 @@ fn to_encoder_tuple( elem_var_slice, to_encoder_clos_var, encoder_var, + Variable::PURE, )), ); @@ -626,6 +634,7 @@ fn to_encoder_tuple( elem_encoders_list_var_slice, encode_tuple_clos_var, encoder_var, + Variable::PURE, )), ); @@ -676,6 +685,7 @@ fn to_encoder_tuple( tuple_var_slice, fn_clos_var, this_encoder_var, + Variable::PURE, )), ); @@ -767,6 +777,7 @@ fn to_encoder_tag_union( var_slice_of_sym_var, to_encoder_clos_var, encoder_var, + Variable::PURE, )), ); @@ -828,6 +839,7 @@ fn to_encoder_tag_union( this_encode_tag_args_var_slice, this_encode_tag_clos_var, this_encoder_var, + Variable::PURE, )), ); @@ -917,6 +929,7 @@ fn to_encoder_tag_union( tag_union_var_slice, fn_clos_var, this_encoder_var, + Variable::PURE, )), ); @@ -986,6 +999,7 @@ fn wrap_in_encode_custom( this_append_with_args_var_slice, this_append_with_clos_var, Variable::LIST_U8, + Variable::PURE, )), ); @@ -1036,7 +1050,12 @@ fn wrap_in_encode_custom( let args_slice = env.subs.insert_into_vars(vec![bytes_var, fmt_var]); env.subs.set_content( fn_var, - Content::Structure(FlatType::Func(args_slice, fn_clos_var, Variable::LIST_U8)), + Content::Structure(FlatType::Func( + args_slice, + fn_clos_var, + Variable::LIST_U8, + Variable::PURE, + )), ); // \bytes, fmt -[[fn_name captured_var]]-> Encode.appendWith bytes encoder fmt @@ -1080,6 +1099,7 @@ fn wrap_in_encode_custom( this_custom_args_var_slice, this_custom_clos_var, this_custom_encoder_var, + Variable::PURE, )), ); diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index 58391ebdd33..6b9db461c17 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -475,6 +475,7 @@ fn call_hash_ability_member( this_arguments_slice, this_hash_clos_var, this_out_hasher_var, + Variable::PURE, )), ); @@ -533,7 +534,12 @@ fn build_outer_derived_closure( let args_slice = env.subs.insert_into_vars([hasher_var, val_var]); env.subs.set_content( fn_var, - Content::Structure(FlatType::Func(args_slice, fn_clos_var, body_var)), + Content::Structure(FlatType::Func( + args_slice, + fn_clos_var, + body_var, + Variable::PURE, + )), ); (fn_var, fn_clos_var) diff --git a/crates/compiler/derive/src/inspect.rs b/crates/compiler/derive/src/inspect.rs index a67b29ce1db..8a41356ca6c 100644 --- a/crates/compiler/derive/src/inspect.rs +++ b/crates/compiler/derive/src/inspect.rs @@ -137,6 +137,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { elem_var_slice, to_inspector_clos_var, elem_inspector_var, + Variable::PURE, )), ); @@ -187,6 +188,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { elem_var_slice, to_elem_inspector_lset, elem_inspector_var, + Variable::PURE, )), ); @@ -225,6 +227,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { this_inspect_list_args_slice, this_inspect_list_clos_var, this_list_inspector_var, + Variable::PURE, )), ); @@ -288,6 +291,7 @@ fn to_inspector_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { list_var_slice, fn_clos_var, this_inspector_var, + Variable::PURE, )), ); @@ -369,6 +373,7 @@ fn to_inspector_record( field_var_slice, to_inspector_clos_var, inspector_var, + Variable::PURE, )), ); @@ -452,6 +457,7 @@ fn to_inspector_record( fields_list_var_slice, inspect_record_clos_var, inspector_var, + Variable::PURE, )), ); @@ -502,6 +508,7 @@ fn to_inspector_record( record_var_slice, fn_clos_var, this_inspector_var, + Variable::PURE, )), ); @@ -576,6 +583,7 @@ fn to_inspector_tuple( elem_var_slice, to_inspector_clos_var, inspector_var, + Variable::PURE, )), ); @@ -639,6 +647,7 @@ fn to_inspector_tuple( elem_inspectors_list_var_slice, inspect_tuple_clos_var, inspector_var, + Variable::PURE, )), ); @@ -689,6 +698,7 @@ fn to_inspector_tuple( tuple_var_slice, fn_clos_var, this_inspector_var, + Variable::PURE, )), ); @@ -780,6 +790,7 @@ fn to_inspector_tag_union( var_slice_of_sym_var, to_inspector_clos_var, inspector_var, + Variable::PURE, )), ); @@ -845,6 +856,7 @@ fn to_inspector_tag_union( this_inspect_tag_args_var_slice, this_inspect_tag_clos_var, this_inspector_var, + Variable::PURE, )), ); @@ -934,6 +946,7 @@ fn to_inspector_tag_union( tag_union_var_slice, fn_clos_var, this_inspector_var, + Variable::PURE, )), ); @@ -992,6 +1005,7 @@ fn wrap_in_inspect_custom( this_apply_args_var_slice, this_apply_clos_var, fmt_var, + Variable::PURE, )), ); @@ -1040,7 +1054,12 @@ fn wrap_in_inspect_custom( let args_slice = env.subs.insert_into_vars(vec![fmt_var]); env.subs.set_content( fn_var, - Content::Structure(FlatType::Func(args_slice, fn_clos_var, fmt_var)), + Content::Structure(FlatType::Func( + args_slice, + fn_clos_var, + fmt_var, + Variable::PURE, + )), ); // \fmt -[[fn_name captured_var]]-> Inspect.apply inspector fmt @@ -1077,6 +1096,7 @@ fn wrap_in_inspect_custom( this_custom_args_var_slice, this_custom_clos_var, this_custom_inspector_var, + Variable::PURE, )), ); diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 59cdc5a3ba9..ab52bbb55aa 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -4389,7 +4389,12 @@ fn synth_list_len_type(subs: &mut Subs) -> Variable { let fn_args_slice = slice_extend_new(&mut subs.variables, [list_a]); subs.set_content( fn_var, - Content::Structure(FlatType::Func(fn_args_slice, clos_list_len, Variable::U64)), + Content::Structure(FlatType::Func( + fn_args_slice, + clos_list_len, + Variable::U64, + Variable::PURE, + )), ); fn_var } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index a52a8ba3b45..ca11902be24 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -4494,7 +4494,7 @@ pub fn with_hole<'a>( debug_assert!(!matches!( env.subs.get_content_without_compacting(variant_var), - Content::Structure(FlatType::Func(_, _, _)) + Content::Structure(FlatType::Func(_, _, _, _)) )); convert_tag_union( env, @@ -4519,14 +4519,16 @@ pub fn with_hole<'a>( let content = env.subs.get_content_without_compacting(variable); - if let Content::Structure(FlatType::Func(arg_vars, _, ret_var)) = content { + if let Content::Structure(FlatType::Func(arg_vars, _, ret_var, fx_var)) = content { let ret_var = *ret_var; + let fx_var = *fx_var; let arg_vars = *arg_vars; tag_union_to_function( env, arg_vars, ret_var, + fx_var, tag_name, closure_name, ext_var, @@ -6163,7 +6165,7 @@ fn late_resolve_ability_specialization( if let Some(spec_symbol) = opt_resolved { // Fast path: specialization is monomorphic, was found during solving. spec_symbol - } else if let Content::Structure(FlatType::Func(_, lambda_set, _)) = + } else if let Content::Structure(FlatType::Func(_, lambda_set, _, fx_var)) = env.subs.get_content_without_compacting(specialization_var) { // Fast path: the member is a function, so the lambda set will tell us the @@ -6686,6 +6688,7 @@ fn tag_union_to_function<'a>( env: &mut Env<'a, '_>, argument_variables: VariableSubsSlice, return_variable: Variable, + fx_variable: Variable, tag_name: TagName, proc_symbol: Symbol, ext_var: Variable, @@ -6695,6 +6698,8 @@ fn tag_union_to_function<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { + // [purity-inference] TODO: Do we need fx_variable for anything? + let mut loc_pattern_args = vec![]; let mut loc_expr_args = vec![]; @@ -6888,7 +6893,7 @@ fn register_capturing_closure<'a>( let is_self_recursive = !matches!(recursive, roc_can::expr::Recursive::NotRecursive); let captured_symbols = match *env.subs.get_content_without_compacting(function_type) { - Content::Structure(FlatType::Func(args, closure_var, ret)) => { + Content::Structure(FlatType::Func(args, closure_var, ret, fx_var)) => { let lambda_set_layout = { LambdaSet::from_var_pub( layout_cache, @@ -10092,7 +10097,7 @@ pub fn find_lambda_sets( // ignore the lambda set of top-level functions match subs.get_without_compacting(initial).content { - Content::Structure(FlatType::Func(arguments, _, result)) => { + Content::Structure(FlatType::Func(arguments, _, result, _fx)) => { let arguments = &subs.variables[arguments.indices()]; stack.extend(arguments.iter().copied()); @@ -10129,7 +10134,7 @@ fn find_lambda_sets_help( FlatType::Apply(_, arguments) => { stack.extend(subs.get_subs_slice(*arguments).iter().rev()); } - FlatType::Func(arguments, lambda_set_var, ret_var) => { + FlatType::Func(arguments, lambda_set_var, ret_var, fx_var) => { use std::collections::hash_map::Entry; // Only insert a lambda_set_var if we didn't already have a value for this key. if let Entry::Vacant(entry) = result.entry(*lambda_set_var) { diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 49e9b8953a2..f5c6955aafd 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -593,7 +593,7 @@ impl<'a> RawFunctionLayout<'a> { let arena = env.arena; match flat_type { - Func(args, closure_var, ret_var) => { + Func(args, closure_var, ret_var, fx_var) => { let mut fn_args = Vec::with_capacity_in(args.len(), arena); let mut cache_criteria = CACHEABLE; @@ -2151,7 +2151,7 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) { stack.push((*var, depth_any + 1, depth_lset)); } } - FlatType::Func(args, lset, ret) => { + FlatType::Func(args, lset, ret, fx_var) => { for var in subs.get_subs_slice(*args) { stack.push((*var, depth_any + 1, depth_lset)); } @@ -3321,7 +3321,7 @@ fn layout_from_flat_type<'a>( } } } - Func(args, closure_var, ret_var) => { + Func(args, closure_var, ret_var, fx_var) => { if env.is_seen(closure_var) { // TODO(recursive-layouts): after the naked pointer is updated, we can cache `var` to // point to the updated layout. diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 32a264b26fc..358f139ab00 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -718,11 +718,12 @@ trait DerivableVisitor { push_var_slice!(vars) } } - Func(args, _clos, ret) => { + Func(args, _clos, ret, fx) => { let descend = Self::visit_func(var)?; if descend.0 { push_var_slice!(args); stack.push(ret); + stack.push(fx); } } Record(fields, ext) => { diff --git a/crates/compiler/solve/src/deep_copy.rs b/crates/compiler/solve/src/deep_copy.rs index 5493e584622..bd3d16e9598 100644 --- a/crates/compiler/solve/src/deep_copy.rs +++ b/crates/compiler/solve/src/deep_copy.rs @@ -177,13 +177,14 @@ fn deep_copy_var_help( Apply(symbol, new_arguments) } - Func(arguments, closure_var, ret_var) => { + Func(arguments, closure_var, ret_var, fx_var) => { let new_ret_var = work!(ret_var); let new_closure_var = work!(closure_var); + let new_fx_var = work!(fx_var); let new_arguments = copy_sequence!(arguments.len(), arguments); - Func(new_arguments, new_closure_var, new_ret_var) + Func(new_arguments, new_closure_var, new_ret_var, new_fx_var) } same @ EmptyRecord | same @ EmptyTagUnion => same, diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index 00e09bb5d3c..e8a5e3a0b61 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -186,7 +186,9 @@ pub fn exposed_types_storage_subs( .as_inner() .get_content_without_compacting(imported_lset_ambient_function_var) { - Content::Structure(FlatType::Func(_, lambda_set_var, _)) => *lambda_set_var, + Content::Structure(FlatType::Func(_, lambda_set_var, _, _)) => { + *lambda_set_var + } content => internal_error!( "ambient lambda set function import is not a function, found: {:?}", roc_types::subs::SubsFmtContent(content, storage_subs.as_inner()) diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 13d7a3aeb3d..66725f78c30 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -2194,7 +2194,7 @@ fn adjust_rank_content( rank } - Func(arg_vars, closure_var, ret_var) => { + Func(arg_vars, closure_var, ret_var, _fx_var) => { let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ret_var); // TODO investigate further. diff --git a/crates/compiler/solve/src/to_var.rs b/crates/compiler/solve/src/to_var.rs index 0606910479c..07d838ca323 100644 --- a/crates/compiler/solve/src/to_var.rs +++ b/crates/compiler/solve/src/to_var.rs @@ -416,9 +416,8 @@ pub(crate) fn type_to_var_help( let fx_var = helper!(fx_type); let closure_var = helper!(closure_type, AmbientFunctionPolicy::Function(destination)); - // [purity-inference] TODO: add fx_type to FlatType::Func let content = - Content::Structure(FlatType::Func(new_arguments, closure_var, ret_var)); + Content::Structure(FlatType::Func(new_arguments, closure_var, ret_var, fx_var)); env.register_with_known_var(destination, rank, content) } diff --git a/crates/compiler/types/src/pretty_print.rs b/crates/compiler/types/src/pretty_print.rs index 53d77b8728b..a2ff7db2e1c 100644 --- a/crates/compiler/types/src/pretty_print.rs +++ b/crates/compiler/types/src/pretty_print.rs @@ -177,7 +177,7 @@ fn find_names_needed( ); } } - Structure(Func(arg_vars, closure_var, ret_var)) => { + Structure(Func(arg_vars, closure_var, ret_var, fx_var)) => { for index in arg_vars.into_iter() { let var = subs[index]; find_names_needed( @@ -207,6 +207,15 @@ fn find_names_needed( names_taken, find_under_alias, ); + + find_names_needed( + *fx_var, + subs, + roots, + root_appearances, + names_taken, + find_under_alias, + ); } Structure(Record(sorted_fields, ext_var)) => { for index in sorted_fields.iter_variables() { @@ -1118,12 +1127,13 @@ fn write_flat_type<'a>( ), EmptyRecord => buf.push_str(EMPTY_RECORD), EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION), - Func(args, closure, ret) => write_fn( + Func(args, closure, ret, fx) => write_fn( env, ctx, subs.get_subs_slice(*args), *closure, *ret, + *fx, subs, buf, parens, @@ -1456,6 +1466,7 @@ fn write_fn<'a>( args: &[Variable], closure: Variable, ret: Variable, + fx: Variable, subs: &'a Subs, buf: &mut String, parens: Parens, @@ -1479,11 +1490,14 @@ fn write_fn<'a>( } if !env.debug.print_lambda_sets { - buf.push_str(" -> "); + buf.push(' '); + write_content(env, ctx, fx, subs, buf, Parens::Unnecessary, Polarity::Neg); + buf.push(' '); } else { buf.push_str(" -"); write_content(env, ctx, closure, subs, buf, parens, pol); - buf.push_str("-> "); + write_content(env, ctx, fx, subs, buf, Parens::Unnecessary, Polarity::Neg); + buf.push(' '); } write_content(env, ctx, ret, subs, buf, Parens::InFn, Polarity::Pos); diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index 2d354c8c40b..871b2cef1b3 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -711,8 +711,8 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt: write!(f, ", ^<{ambient_function_var:?}>)") } Content::ErasedLambda => write!(f, "ErasedLambda"), - Content::Pure => write!(f, "PureFunction"), - Content::Effectful => write!(f, "EffectfulFunction"), + Content::Pure => write!(f, "Pure"), + Content::Effectful => write!(f, "Effectful"), Content::RangedNumber(range) => { write!(f, "RangedNumber( {range:?})") } @@ -735,7 +735,7 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f write!(f, "Apply({name:?}, {slice:?})") } - FlatType::Func(arguments, lambda_set, result) => { + FlatType::Func(arguments, lambda_set, result, fx) => { let slice = subs.get_subs_slice(*arguments); write!(f, "Func([")?; for var in slice { @@ -743,15 +743,18 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f write!(f, "<{:?}>{:?},", *var, SubsFmtContent(content, subs))?; } let result_content = subs.get_content_without_compacting(*result); + let fx_content = subs.get_content_without_compacting(*fx); let lambda_content = subs.get_content_without_compacting(*lambda_set); write!( f, - "], <{:?}={:?}>{:?}, <{:?}>{:?})", + "], <{:?}={:?}>{:?}, <{:?}>{:?}, <{:?}>{:?})", lambda_set, subs.get_root_key_without_compacting(*lambda_set), SubsFmtContent(lambda_content, subs), *result, - SubsFmtContent(result_content, subs) + SubsFmtContent(result_content, subs), + *fx, + SubsFmtContent(fx_content, subs), ) } FlatType::Record(fields, ext) => { @@ -2580,7 +2583,7 @@ impl TagExt { #[derive(Clone, Copy, Debug)] pub enum FlatType { Apply(Symbol, VariableSubsSlice), - Func(VariableSubsSlice, Variable, Variable), + Func(VariableSubsSlice, Variable, Variable, Variable), Record(RecordFields, Variable), Tuple(TupleElems, Variable), TagUnion(UnionTags, TagExt), @@ -3453,10 +3456,11 @@ fn occurs( ctx, safe!([Variable], subs.get_subs_slice(*args)).iter(), ), - Func(arg_vars, closure_var, ret_var) => { + Func(arg_vars, closure_var, ret_var, fx_var) => { let it = iter::once(safe!(Variable, ret_var)) .chain(iter::once(safe!(Variable, closure_var))) - .chain(safe!([Variable], subs.get_subs_slice(*arg_vars)).iter()); + .chain(safe!([Variable], subs.get_subs_slice(*arg_vars)).iter()) + .chain(iter::once(safe!(Variable, fx_var))); short_circuit(subs, root_var, ctx, it) } Record(vars_by_field, ext) => { @@ -3617,7 +3621,7 @@ fn explicit_substitute( subs.set_content(in_var, Structure(Apply(symbol, args))); } - Func(arg_vars, closure_var, ret_var) => { + Func(arg_vars, closure_var, ret_var, fx_var) => { for var_index in arg_vars.into_iter() { let var = subs[var_index]; let answer = explicit_substitute(subs, from, to, var, seen); @@ -3627,10 +3631,11 @@ fn explicit_substitute( let new_ret_var = explicit_substitute(subs, from, to, ret_var, seen); let new_closure_var = explicit_substitute(subs, from, to, closure_var, seen); + let new_fx_var = explicit_substitute(subs, from, to, fx_var, seen); subs.set_content( in_var, - Structure(Func(arg_vars, new_closure_var, new_ret_var)), + Structure(Func(arg_vars, new_closure_var, new_ret_var, new_fx_var)), ); } TagUnion(tags, ext) => { @@ -3851,9 +3856,10 @@ fn get_var_names( }) } - FlatType::Func(arg_vars, closure_var, ret_var) => { + FlatType::Func(arg_vars, closure_var, ret_var, fx_var) => { let taken_names = get_var_names(subs, ret_var, taken_names); let taken_names = get_var_names(subs, closure_var, taken_names); + debug_assert!(get_var_names(subs, fx_var, Default::default()).is_empty()); let mut accum = taken_names; @@ -4200,7 +4206,7 @@ fn flat_type_to_err_type( ErrorType::Type(symbol, arg_types) } - Func(arg_vars, closure_var, ret_var) => { + Func(arg_vars, closure_var, ret_var, _fx_var) => { let args = arg_vars .into_iter() .map(|index| { @@ -4212,6 +4218,7 @@ fn flat_type_to_err_type( let ret = var_to_err_type(subs, state, ret_var, Polarity::Pos); let closure = var_to_err_type(subs, state, closure_var, pol); + // [purity-inference] TODO: add fx var to the error type ErrorType::Function(args, Box::new(closure), Box::new(ret)) } @@ -4643,10 +4650,11 @@ impl StorageSubs { FlatType::Apply(symbol, arguments) => { FlatType::Apply(*symbol, Self::offset_variable_slice(offsets, *arguments)) } - FlatType::Func(arguments, lambda_set, result) => FlatType::Func( + FlatType::Func(arguments, lambda_set, result, fx) => FlatType::Func( Self::offset_variable_slice(offsets, *arguments), Self::offset_variable(offsets, *lambda_set), Self::offset_variable(offsets, *result), + Self::offset_variable(offsets, *fx), ), FlatType::Record(record_fields, ext) => FlatType::Record( Self::offset_record_fields(offsets, *record_fields), @@ -4951,11 +4959,13 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) -> Apply(symbol, new_arguments) } - Func(arguments, closure_var, ret_var) => { + Func(arguments, closure_var, ret_var, fx_var) => { let new_ret_var = storage_copy_var_to_help(env, ret_var); let new_closure_var = storage_copy_var_to_help(env, closure_var); + let new_fx_var = storage_copy_var_to_help(env, fx_var); + let new_arguments = env.target.reserve_into_vars(arguments.len()); for (target_index, var_index) in (new_arguments.indices()).zip(arguments) { @@ -4964,7 +4974,7 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) -> env.target.variables[target_index] = copy_var; } - Func(new_arguments, new_closure_var, new_ret_var) + Func(new_arguments, new_closure_var, new_ret_var, new_fx_var) } same @ EmptyRecord | same @ EmptyTagUnion => same, @@ -5313,7 +5323,10 @@ fn is_registered(content: &Content) -> bool { | Content::RigidAbleVar(..) => false, Content::Structure(FlatType::EmptyRecord | FlatType::EmptyTagUnion) => false, Content::ErasedLambda => false, - Content::Pure | Content::Effectful => todo!("[purity-inference]"), + Content::Pure | Content::Effectful => { + // [purity-inference] TODO + false + } Content::Structure(_) | Content::RecursionVar { .. } @@ -5418,11 +5431,13 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl Apply(symbol, new_arguments) } - Func(arguments, closure_var, ret_var) => { + Func(arguments, closure_var, ret_var, fx_var) => { let new_ret_var = copy_import_to_help(env, max_rank, ret_var); let new_closure_var = copy_import_to_help(env, max_rank, closure_var); + let new_fx_var = copy_import_to_help(env, max_rank, fx_var); + let new_arguments = env.target.reserve_into_vars(arguments.len()); for (target_index, var_index) in (new_arguments.indices()).zip(arguments) { @@ -5431,7 +5446,7 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl env.target.variables[target_index] = copy_var; } - Func(new_arguments, new_closure_var, new_ret_var) + Func(new_arguments, new_closure_var, new_ret_var, new_fx_var) } same @ EmptyRecord | same @ EmptyTagUnion => same, @@ -5806,15 +5821,17 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) { stack.extend(var_slice!(*args)); } - Func(arg_vars, closure_var, ret_var) => { + Func(arg_vars, closure_var, ret_var, fx_var) => { let arg_vars = *arg_vars; let ret_var = *ret_var; let closure_var = *closure_var; + let fx_var = *fx_var; stack.extend(var_slice!(arg_vars)); stack.push(ret_var); stack.push(closure_var); + stack.push(fx_var); } EmptyRecord | EmptyTagUnion => (), @@ -5935,10 +5952,11 @@ pub fn get_member_lambda_sets_at_region(subs: &Subs, var: Variable, target_regio FlatType::Apply(_, vars) => { stack.extend(subs.get_subs_slice(*vars)); } - FlatType::Func(args, lset, ret) => { + FlatType::Func(args, lset, ret, fx) => { stack.extend(subs.get_subs_slice(*args)); stack.push(*lset); stack.push(*ret); + stack.push(*fx); } FlatType::Record(fields, ext) => { stack.extend(subs.get_subs_slice(fields.variables())); @@ -6014,7 +6032,7 @@ fn is_inhabited(subs: &Subs, var: Variable) -> bool { Content::Pure | Content::Effectful => {} Content::Structure(structure) => match structure { FlatType::Apply(_, args) => stack.extend(subs.get_subs_slice(*args)), - FlatType::Func(args, _, ret) => { + FlatType::Func(args, _, ret, _fx) => { stack.extend(subs.get_subs_slice(*args)); stack.push(*ret); } diff --git a/crates/compiler/unify/src/fix.rs b/crates/compiler/unify/src/fix.rs index c53477bf5d4..829fb1277be 100644 --- a/crates/compiler/unify/src/fix.rs +++ b/crates/compiler/unify/src/fix.rs @@ -265,8 +265,8 @@ fn find_chain(subs: &Subs, left: Variable, right: Variable) -> impl Iterator { // lambda sets are ignored; see the comment in the LambdaSet case above. let check_args = |_| { @@ -278,7 +278,9 @@ fn find_chain(subs: &Subs, left: Variable, right: Variable) -> impl Iterator( outcome } - (Func(l_args, l_closure, l_ret), Func(r_args, r_closure, r_ret)) + (Func(l_args, l_closure, l_ret, l_fx), Func(r_args, r_closure, r_ret, r_fx)) if l_args.len() == r_args.len() => { let arg_outcome = unify_zip_slices(env, pool, *l_args, *r_args, ctx.mode); let ret_outcome = unify_pool(env, pool, *l_ret, *r_ret, ctx.mode); let closure_outcome = unify_pool(env, pool, *l_closure, *r_closure, ctx.mode); + let fx_outcome = unify_pool(env, pool, *l_fx, *r_fx, ctx.mode); let mut outcome = ret_outcome; outcome.union(closure_outcome); outcome.union(arg_outcome); + outcome.union(fx_outcome); if outcome.mismatches.is_empty() { let merged_closure_var = choose_merged_var(env, *l_closure, *r_closure); @@ -3327,13 +3329,13 @@ fn unify_flat_type( outcome.union(merge( env, ctx, - Structure(Func(*r_args, merged_closure_var, *r_ret)), + Structure(Func(*r_args, merged_closure_var, *r_ret, *r_fx)), )); } outcome } - (FunctionOrTagUnion(tag_names, tag_symbols, ext), Func(args, closure, ret)) => { + (FunctionOrTagUnion(tag_names, tag_symbols, ext), Func(args, closure, ret, fx)) => { unify_function_or_tag_union_and_func( env, pool, @@ -3344,10 +3346,11 @@ fn unify_flat_type( *args, *ret, *closure, + *fx, true, ) } - (Func(args, closure, ret), FunctionOrTagUnion(tag_names, tag_symbols, ext)) => { + (Func(args, closure, ret, fx), FunctionOrTagUnion(tag_names, tag_symbols, ext)) => { unify_function_or_tag_union_and_func( env, pool, @@ -3358,6 +3361,7 @@ fn unify_flat_type( *args, *ret, *closure, + *fx, false, ) } @@ -3928,6 +3932,7 @@ fn unify_function_or_tag_union_and_func( function_arguments: VariableSubsSlice, function_return: Variable, function_lambda_set: Variable, + function_fx: Variable, left: bool, ) -> Outcome { let tag_names = env.get_subs_slice(tag_names_slice).to_vec(); @@ -3946,6 +3951,12 @@ fn unify_function_or_tag_union_and_func( unify_pool(env, pool, function_return, new_tag_union_var, ctx.mode) }; + outcome.union(if left { + unify_pool(env, pool, Variable::PURE, function_fx, ctx.mode) + } else { + unify_pool(env, pool, function_fx, Variable::PURE, ctx.mode) + }); + { let lambda_names = env.get_subs_slice(tag_fn_lambdas).to_vec(); let new_lambda_names = slice_extend_new(&mut env.symbol_names, lambda_names); diff --git a/crates/glue/src/load.rs b/crates/glue/src/load.rs index 6d0d46262b6..78c53136f7f 100644 --- a/crates/glue/src/load.rs +++ b/crates/glue/src/load.rs @@ -314,7 +314,7 @@ fn number_lambda_sets(subs: &Subs, initial: Variable) -> Vec { stack.extend(var_slice!(*args)); } - Func(arg_vars, closure_var, ret_var) => { + Func(arg_vars, closure_var, ret_var, _fx_var) => { lambda_sets.push(subs.get_root_key_without_compacting(*closure_var)); stack.push(*ret_var); diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index dcbed74539d..74e830ff308 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -1236,7 +1236,8 @@ impl<'a> Env<'a> { .expect("Something weird ended up in the content"); match self.subs.get_content_without_compacting(var) { - Content::Structure(FlatType::Func(args, closure_var, ret_var)) => { + Content::Structure(FlatType::Func(args, closure_var, ret_var, fx_var)) => { + // [purity-inference] TODO: fx var // this is a toplevel type, so the closure must be empty let is_toplevel = true; add_function_type( @@ -1405,7 +1406,8 @@ fn add_type_help<'a>( } } }, - Content::Structure(FlatType::Func(args, closure_var, ret_var)) => { + Content::Structure(FlatType::Func(args, closure_var, ret_var, _fx_var)) => { + // [purity-inference] TODO: fx var let is_toplevel = false; // or in any case, we cannot assume that we are add_function_type( diff --git a/crates/language_server/src/analysis/completion.rs b/crates/language_server/src/analysis/completion.rs index 8bacf8c3b15..6b61670c113 100644 --- a/crates/language_server/src/analysis/completion.rs +++ b/crates/language_server/src/analysis/completion.rs @@ -239,7 +239,8 @@ fn make_completion_item( let typ = match subs.get(var).content { roc_types::subs::Content::Structure(var) => match var { roc_types::subs::FlatType::Apply(_, _) => CompletionItemKind::FUNCTION, - roc_types::subs::FlatType::Func(_, _, _) => CompletionItemKind::FUNCTION, + // [purity-inference] TODO: Categorize by purity? + roc_types::subs::FlatType::Func(_, _, _, _) => CompletionItemKind::FUNCTION, roc_types::subs::FlatType::EmptyTagUnion | roc_types::subs::FlatType::TagUnion(_, _) => CompletionItemKind::ENUM, _ => CompletionItemKind::VARIABLE, diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 53c63b34bcd..8343346308d 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -479,7 +479,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( single_tag_union_to_ast(env, mem, addr, field_layouts, tag_name, &[]) } - Content::Structure(FlatType::Func(_, _, _)) => { + Content::Structure(FlatType::Func(_, _, _, _)) => { // a function with a struct as the closure environment Expr::REPL_OPAQUE_FUNCTION } @@ -597,7 +597,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>( let raw_content = env.subs.get_content_without_compacting(raw_var); let expr = match (raw_content, layout) { - (Content::Structure(FlatType::Func(_, _, _)), _) | (_, LayoutRepr::LambdaSet(_) | LayoutRepr::FunctionPointer(_) | LayoutRepr::Erased(_)) => { + (Content::Structure(FlatType::Func(_, _, _,_)), _) | (_, LayoutRepr::LambdaSet(_) | LayoutRepr::FunctionPointer(_) | LayoutRepr::Erased(_)) => { Expr::REPL_OPAQUE_FUNCTION } (_, LayoutRepr::Builtin(Builtin::Bool)) => { From b9b85a222f96447992fb4d247254670556032f6e Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 8 Oct 2024 21:32:05 -0300 Subject: [PATCH 10/73] Do not use const fx vars when canonicalizing annotations --- crates/compiler/can/src/annotation.rs | 7 ++-- crates/compiler/can/src/scope.rs | 17 +++++---- crates/compiler/solve/src/to_var.rs | 10 ++++++ crates/compiler/types/src/types.rs | 51 +++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 25ed1d73829..e4a4259d6f1 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -591,11 +591,10 @@ fn can_annotation_help( introduced_variables.insert_lambda_set(lambda_set); let closure = Type::Variable(lambda_set); - let fx_var = match arrow { - FunctionArrow::Pure => Variable::PURE, - FunctionArrow::Effectful => Variable::EFFECTFUL, + let fx_type = match arrow { + FunctionArrow::Pure => Type::Pure, + FunctionArrow::Effectful => Type::Effectful, }; - let fx_type = Type::Variable(fx_var); Type::Function(args, Box::new(closure), Box::new(ret), Box::new(fx_type)) } diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index 2ee5d47625a..fe6608f4ffe 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -538,15 +538,14 @@ pub fn create_alias( } if !hidden.is_empty() { - // [purity-inference] TODO: restore - // internal_error!( - // "Found unbound type variables {:?} \n in type alias {:?} {:?} {:?} : {:?}", - // hidden, - // name, - // &vars, - // &infer_ext_in_output_variables, - // &typ - // ) + internal_error!( + "Found unbound type variables {:?} \n in type alias {:?} {:?} {:?} : {:?}", + hidden, + name, + &vars, + &infer_ext_in_output_variables, + &typ + ) } true diff --git a/crates/compiler/solve/src/to_var.rs b/crates/compiler/solve/src/to_var.rs index 07d838ca323..eed99a1fabd 100644 --- a/crates/compiler/solve/src/to_var.rs +++ b/crates/compiler/solve/src/to_var.rs @@ -752,6 +752,16 @@ pub(crate) fn type_to_var_help( env.register_with_known_var(destination, rank, content) } + Pure => { + let content = Content::Pure; + + env.register_with_known_var(destination, rank, content) + } + Effectful => { + let content = Content::Effectful; + + env.register_with_known_var(destination, rank, content) + } Error => { let content = Content::Error; diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index b6eb3ba0c9f..c037380f9f4 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -421,6 +421,9 @@ pub enum TypeTag { RecursiveTagUnion(Variable, UnionTags, ExtImplicitOpenness), Record(RecordFields), Tuple(TupleElems), + // A function fx type + Pure, + Effectful, } /// Look-aside slice of types used in [Types], when the slice does not correspond to the direct @@ -1005,6 +1008,8 @@ impl Types { self.set_type_tag(index, TypeTag::RangedNumber(*range), Slice::default()) } Type::Error => self.set_type_tag(index, TypeTag::Error, Slice::default()), + Type::Pure => self.set_type_tag(index, TypeTag::Pure, Slice::default()), + Type::Effectful => self.set_type_tag(index, TypeTag::Effectful, Slice::default()), } } @@ -1257,6 +1262,8 @@ impl Types { } RangedNumber(range) => (RangedNumber(range), Default::default()), Error => (Error, Default::default()), + Pure => (Pure, Default::default()), + Effectful => (Effectful, Default::default()), }; self.set_type_tag(dest_index, tag, args); @@ -1466,6 +1473,8 @@ mod debug_types { .align(), ) } + TypeTag::Pure => f.text("Pure"), + TypeTag::Effectful => f.text("Effectful"), }; group.group() } @@ -1693,6 +1702,9 @@ pub enum Type { Apply(Symbol, Vec>, Region), Variable(Variable), RangedNumber(NumericRange), + /// A function's fx type + Pure, + Effectful, /// A type error, which will code gen to a runtime error Error, } @@ -1781,6 +1793,8 @@ impl Clone for Type { Self::Variable(arg0) => Self::Variable(*arg0), Self::RangedNumber(arg1) => Self::RangedNumber(*arg1), Self::Error => Self::Error, + Type::Pure => Self::Pure, + Type::Effectful => Self::Effectful, } } } @@ -2145,6 +2159,8 @@ impl fmt::Debug for Type { Type::UnspecializedLambdaSet { unspecialized } => { write!(f, "{unspecialized:?}") } + Type::Pure => write!(f, "->"), + Type::Effectful => write!(f, "=>"), } } } @@ -2308,7 +2324,7 @@ impl Type { ); } - EmptyRec | EmptyTagUnion | Error => {} + EmptyRec | EmptyTagUnion | Error | Pure | Effectful => {} } } } @@ -2431,7 +2447,7 @@ impl Type { ); } - EmptyRec | EmptyTagUnion | Error => {} + EmptyRec | EmptyTagUnion | Error | Pure | Effectful => {} } } } @@ -2547,7 +2563,13 @@ impl Type { } RangedNumber(_) => Ok(()), UnspecializedLambdaSet { .. } => Ok(()), - EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => Ok(()), + EmptyRec + | EmptyTagUnion + | ClosureTag { .. } + | Error + | Variable(_) + | Pure + | Effectful => Ok(()), } } @@ -2610,7 +2632,13 @@ impl Type { UnspecializedLambdaSet { unspecialized: Uls(_, sym, _), } => *sym == rep_symbol, - EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => false, + EmptyRec + | EmptyTagUnion + | ClosureTag { .. } + | Error + | Variable(_) + | Pure + | Effectful => false, } } @@ -2672,7 +2700,7 @@ impl Type { .iter() .any(|arg| arg.value.contains_variable(rep_variable)), RangedNumber(_) => false, - EmptyRec | EmptyTagUnion | Error => false, + EmptyRec | EmptyTagUnion | Error | Pure | Effectful => false, } } @@ -2981,7 +3009,7 @@ fn instantiate_aliases<'a, F>( } RangedNumber(_) => {} UnspecializedLambdaSet { .. } => {} - EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {} + EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) | Pure | Effectful => {} } } @@ -3043,7 +3071,13 @@ fn symbols_help(initial: &Type) -> Vec { } => { // ignore the member symbol because unspecialized lambda sets are internal-only } - EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {} + EmptyRec + | EmptyTagUnion + | ClosureTag { .. } + | Error + | Variable(_) + | Pure + | Effectful => {} } } @@ -3165,6 +3199,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet) { variables_help(&x.value, accum); } } + Pure | Effectful => {} } } @@ -3308,6 +3343,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) { variables_help_detailed(&x.value, accum); } } + Pure | Effectful => {} } } @@ -4503,6 +4539,7 @@ fn instantiate_lambda_sets_as_unspecialized( Type::Variable(_) => {} Type::RangedNumber(_) => {} Type::Error => {} + Type::Pure | Type::Effectful => {} } } } From 7af05cc6c979bef949a2313d39c6db77c2c9b811 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 10 Oct 2024 19:49:48 -0300 Subject: [PATCH 11/73] Constrain function annotation fx to body --- crates/compiler/can/src/constraint.rs | 12 +- crates/compiler/constrain/src/expr.rs | 129 ++++++++++++------ crates/compiler/constrain/src/module.rs | 6 +- crates/compiler/load/tests/helpers/mod.rs | 2 +- .../compiler/lower_params/src/type_error.rs | 2 + crates/compiler/solve/src/solve.rs | 28 ---- crates/compiler/types/src/types.rs | 2 + crates/compiler/unify/src/unify.rs | 6 +- crates/glue/src/types.rs | 2 +- crates/reporting/src/error/type.rs | 2 + 10 files changed, 103 insertions(+), 88 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index 5152ec6404e..d15f88280e8 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -606,8 +606,7 @@ impl Constraints { | Constraint::Resolve(..) | Constraint::IngestedFile(..) | Constraint::CheckCycle(..) - | Constraint::ImportParams(..) - | Constraint::CallFx(_, _) => false, + | Constraint::ImportParams(..) => false, } } @@ -699,10 +698,6 @@ impl Constraints { ) -> Constraint { Constraint::ImportParams(opt_type_index, module_id, region) } - - pub fn call_fx(&mut self, env_fx_var: Variable, call_fx_var: Variable) -> Constraint { - Constraint::CallFx(env_fx_var, call_fx_var) - } } roc_error_macros::assert_sizeof_default!(Constraint, 3 * 8); @@ -775,8 +770,6 @@ pub enum Constraint { Index, Region, ), - /// Unify the current function fx var with a call fx var - CallFx(Variable, Variable), /// Used for things that always unify, e.g. blanks and runtime errors True, SaveTheEnvironment, @@ -865,9 +858,6 @@ impl std::fmt::Debug for Constraint { Self::Pattern(arg0, arg1, arg2, arg3) => { write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } - Self::CallFx(arg0, arg1) => { - write!(f, "CallFx({arg0:?}, {arg1:?})") - } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(), diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 196c767f33a..faa25a4c472 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -59,21 +59,32 @@ pub struct Env { pub resolutions_to_make: Vec, pub home: ModuleId, /// The enclosing function's fx var to be unified with inner calls - pub fn_fx_var: Option, + pub enclosing_fx: Option, +} + +#[derive(Clone, Copy)] +pub struct EnclosingFx { + pub fx_var: Variable, + pub ann_region: Option, } impl Env { - pub fn with_fx(&mut self, fx_var: Variable, f: F) -> T + pub fn with_enclosing_fx( + &mut self, + fx_var: Variable, + ann_region: Option, + f: F, + ) -> T where F: FnOnce(&mut Env) -> T, { - let prev_fx_var = self.fn_fx_var; + let prev = self.enclosing_fx.take(); - self.fn_fx_var = Some(fx_var); + self.enclosing_fx = Some(EnclosingFx { fx_var, ann_region }); let result = f(self); - self.fn_fx_var = prev_fx_var; + self.enclosing_fx = prev; result } @@ -168,7 +179,7 @@ fn constrain_untyped_closure( loc_body_expr.region, )); - let ret_constraint = env.with_fx(fx_var, |env| { + let ret_constraint = env.with_enclosing_fx(fx_var, None, |env| { constrain_expr( types, constraints, @@ -590,12 +601,35 @@ pub fn constrain_expr( let category = Category::CallResult(opt_symbol, *called_via); + let fx_expected_type = match env.enclosing_fx { + Some(enclosing_fn) => { + let enclosing_fx_index = constraints.push_variable(enclosing_fn.fx_var); + + constraints.push_expected_type(ForReason( + Reason::CallInFunction(enclosing_fn.ann_region), + enclosing_fx_index, + region, + )) + } + None => constraints.push_expected_type(ForReason( + Reason::CallInTopLevelDef, + // top-level defs are only allowed to call pure functions + constraints.push_variable(Variable::PURE), + region, + )), + }; + let and_cons = [ fn_con, constraints.equal_types_var(*fn_var, expected_fn_type, category.clone(), fn_region), constraints.and_constraint(arg_cons), - constraints.equal_types_var(*ret_var, expected_final_type, category, region), - constraints.call_fx(env.fn_fx_var.unwrap_or(Variable::PURE), *fx_var), + constraints.equal_types_var( + *ret_var, + expected_final_type, + category.clone(), + region, + ), + constraints.equal_types_var(*fx_var, fx_expected_type, category, region), ]; let and_constraint = constraints.and_constraint(and_cons); @@ -1990,7 +2024,10 @@ fn constrain_function_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], - fn_fx_var: Some(function_def.fx_type), + enclosing_fx: Some(EnclosingFx { + fx_var: function_def.fx_type, + ann_region: Some(annotation.region), + }), }; let region = loc_function_def.region; @@ -2110,6 +2147,13 @@ fn constrain_function_def( let defs_constraint = constraints.and_constraint(argument_pattern_state.constraints); let cons = [ + // Store fx type first so errors are reported at call site + constraints.store( + fx_type_index, + function_def.fx_type, + std::file!(), + std::line!(), + ), constraints.let_constraint( [], argument_pattern_state.vars, @@ -2128,12 +2172,6 @@ fn constrain_function_def( std::file!(), std::line!(), ), - constraints.store( - fx_type_index, - function_def.fx_type, - std::file!(), - std::line!(), - ), // Now, check the solved function type matches the annotation. constraints.equal_types( solved_fn_type, @@ -2242,7 +2280,7 @@ fn constrain_destructure_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], - fn_fx_var: env.fn_fx_var, + enclosing_fx: env.enclosing_fx, }; let signature_index = constraints.push_type(types, signature); @@ -2345,7 +2383,7 @@ fn constrain_value_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], - fn_fx_var: env.fn_fx_var, + enclosing_fx: env.enclosing_fx, }; let loc_pattern = Loc::at(loc_symbol.region, Pattern::Identifier(loc_symbol.value)); @@ -2633,7 +2671,7 @@ pub fn constrain_decls( home, rigids: MutMap::default(), resolutions_to_make: vec![], - fn_fx_var: None, + enclosing_fx: None, }; debug_assert_eq!(declarations.declarations.len(), declarations.symbols.len()); @@ -2865,7 +2903,7 @@ fn constrain_typed_def( home: env.home, resolutions_to_make: vec![], rigids: ftv, - fn_fx_var: env.fn_fx_var, + enclosing_fx: env.enclosing_fx, }; let signature_index = constraints.push_type(types, signature); @@ -2974,20 +3012,25 @@ fn constrain_typed_def( ret_type_index, )); - let ret_constraint = constrain_expr( - types, - constraints, - env, - loc_body_expr.region, - &loc_body_expr.value, - body_type, - ); + let ret_constraint = env.with_enclosing_fx(fx_var, Some(annotation.region), |env| { + constrain_expr( + types, + constraints, + env, + loc_body_expr.region, + &loc_body_expr.value, + body_type, + ) + }); + let ret_constraint = attach_resolution_constraints(constraints, env, ret_constraint); vars.push(*fn_var); let defs_constraint = constraints.and_constraint(argument_pattern_state.constraints); let cons = [ + // Store fx type first so errors are reported at call site + constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), constraints.let_constraint( [], argument_pattern_state.vars, @@ -3890,7 +3933,7 @@ fn constraint_recursive_function( constraints.push_type(types, typ) }; - let expr_con = env.with_fx(fx_var, |env| { + let expr_con = env.with_enclosing_fx(fx_var, Some(annotation.region), |env| { let expected = constraints.push_expected_type(NoExpectation(ret_type_index)); constrain_expr( types, @@ -4439,19 +4482,21 @@ fn rec_defs_help( let typ = types.function(pattern_types, lambda_set, ret_type, fx_type); constraints.push_type(types, typ) }; - let expr_con = env.with_fx(fx_var, |env| { - let body_type = - constraints.push_expected_type(NoExpectation(ret_type_index)); - - constrain_expr( - types, - constraints, - env, - loc_body_expr.region, - &loc_body_expr.value, - body_type, - ) - }); + let expr_con = + env.with_enclosing_fx(fx_var, Some(annotation.region), |env| { + let body_type = + constraints.push_expected_type(NoExpectation(ret_type_index)); + + constrain_expr( + types, + constraints, + env, + loc_body_expr.region, + &loc_body_expr.value, + body_type, + ) + }); + let expr_con = attach_resolution_constraints(constraints, env, expr_con); vars.push(*fn_var); @@ -4460,6 +4505,8 @@ fn rec_defs_help( constraints.and_constraint(argument_pattern_state.constraints); let expected_index = constraints.push_expected_type(expected); let cons = [ + // Store fx type first so errors are reported at call site + constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), constraints.let_constraint( [], argument_pattern_state.vars, diff --git a/crates/compiler/constrain/src/module.rs b/crates/compiler/constrain/src/module.rs index cd20d58fd67..e125fce918c 100644 --- a/crates/compiler/constrain/src/module.rs +++ b/crates/compiler/constrain/src/module.rs @@ -56,7 +56,7 @@ fn constrain_params( home, rigids: MutMap::default(), resolutions_to_make: vec![], - fn_fx_var: None, + enclosing_fx: None, }; let index = constraints.push_variable(module_params.whole_var); @@ -115,7 +115,7 @@ fn constrain_symbols_from_requires( home, rigids, resolutions_to_make: vec![], - fn_fx_var: None, + enclosing_fx: None, }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(loc_symbol.value)); @@ -183,7 +183,7 @@ pub fn frontload_ability_constraints( home, rigids, resolutions_to_make: vec![], - fn_fx_var: None, + enclosing_fx: None, }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(*member_name)); diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index 79235d7a4ef..9a701774ab8 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -203,7 +203,7 @@ pub fn can_expr_with<'a>( rigids: MutMap::default(), home, resolutions_to_make: vec![], - fn_fx_var: None, + enclosing_fx: None, }, loc_expr.region, &loc_expr.value, diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 7e2e78479c6..da6dd6995a0 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -182,6 +182,8 @@ fn remove_for_reason( } | Reason::CrashArg | Reason::ImportParams(_) + | Reason::CallInFunction(_) + | Reason::CallInTopLevelDef | Reason::FunctionOutput => {} } } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 66725f78c30..08df3add1c3 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -559,34 +559,6 @@ fn solve( } } } - CallFx(env_call_fx, call_fx_var) => { - match unify( - &mut env.uenv(), - *env_call_fx, - *call_fx_var, - UnificationMode::EQ, - Polarity::OF_VALUE, - ) { - Success { - vars, - must_implement_ability, - lambda_sets_to_specialize, - extra_metadata: _, - } => { - env.introduce(rank, &vars); - - debug_assert!(must_implement_ability.is_empty()); - debug_assert!(lambda_sets_to_specialize.is_empty()); - } - Failure(vars, _actual_type, _exected_type, _bad_impls) => { - env.introduce(rank, &vars); - - todo!("[purity-inference] can this actually happen?"); - } - } - - state - } Store(source_index, target, _filename, _linenr) => { // a special version of Eq that is used to store types in the AST. // IT DOES NOT REPORT ERRORS! diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index c037380f9f4..7839ba0b4a0 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3448,6 +3448,8 @@ pub enum Reason { foreign_symbol: ForeignSymbol, arg_index: HumanIndex, }, + CallInFunction(Option), + CallInTopLevelDef, FloatLiteral, IntLiteral, NumLiteral, diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index d1a5944deb6..f5936b4e88e 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1151,7 +1151,7 @@ fn unify_pure(env: &mut Env, ctx: &Context, other: &Content) - match other { Pure => merge(env, ctx, Pure), Effectful => merge(env, ctx, Effectful), - FlexVar(_) => merge(env, ctx, Pure), + FlexVar(_) => merge(env, ctx, *other), RigidVar(_) | FlexAbleVar(_, _) | RigidAbleVar(_, _) @@ -1171,8 +1171,8 @@ fn unify_pure(env: &mut Env, ctx: &Context, other: &Content) - #[must_use] fn unify_effectful(env: &mut Env, ctx: &Context, other: &Content) -> Outcome { match other { - Pure | Effectful => merge(env, ctx, Effectful), - FlexVar(_) => merge(env, ctx, Effectful), + Effectful | FlexVar(_) => merge(env, ctx, Effectful), + Pure => mismatch!("Cannot unify effectful with pure"), RigidVar(_) | FlexAbleVar(_, _) | RigidAbleVar(_, _) diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index 74e830ff308..c7fdcb9befe 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -1236,7 +1236,7 @@ impl<'a> Env<'a> { .expect("Something weird ended up in the content"); match self.subs.get_content_without_compacting(var) { - Content::Structure(FlatType::Func(args, closure_var, ret_var, fx_var)) => { + Content::Structure(FlatType::Func(args, closure_var, ret_var, _fx_var)) => { // [purity-inference] TODO: fx var // this is a toplevel type, so the closure must be empty let is_toplevel = true; diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 33a04f48105..2e4573d6f2f 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1679,6 +1679,8 @@ fn to_expr_report<'b>( severity, } } + Reason::CallInFunction(_) => todo!("[purity-inference] CallInFunction"), + Reason::CallInTopLevelDef => todo!("[purity-inference] CallInTopLevelDef"), }, } } From bc3ab0186a8af4ae25615c5c686bb1b6265a1f3f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sun, 13 Oct 2024 22:50:27 -0300 Subject: [PATCH 12/73] Generate effectful hosted functions --- .../src/{task_module.rs => effect_module.rs} | 41 ++++++++++++++++++- crates/compiler/can/src/lib.rs | 2 +- crates/compiler/can/src/module.rs | 4 +- examples/cli/effects-platform/Effect.roc | 7 ++++ .../cli/effects-platform/PlatformTasks.roc | 7 ---- examples/cli/effects-platform/host.zig | 34 --------------- examples/cli/effects-platform/main.roc | 6 +-- examples/cli/effects.roc | 14 +++---- 8 files changed, 59 insertions(+), 56 deletions(-) rename crates/compiler/can/src/{task_module.rs => effect_module.rs} (82%) create mode 100644 examples/cli/effects-platform/Effect.roc delete mode 100644 examples/cli/effects-platform/PlatformTasks.roc diff --git a/crates/compiler/can/src/task_module.rs b/crates/compiler/can/src/effect_module.rs similarity index 82% rename from crates/compiler/can/src/task_module.rs rename to crates/compiler/can/src/effect_module.rs index ca2efcbb06d..c2e80fad9ff 100644 --- a/crates/compiler/can/src/task_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -33,7 +33,7 @@ pub fn build_host_exposed_def( let def_body = { match typ.shallow_structural_dealias() { - Type::Function(args, _, _, _) => { + Type::Function(args, _, _, fx) if **fx == Type::Pure => { for i in 0..args.len() { let name = format!("closure_arg_{ident}_{i}"); @@ -109,6 +109,45 @@ pub fn build_host_exposed_def( loc_body: Box::new(Loc::at_zero(body)), }) } + Type::Function(args, _, _, fx) if **fx == Type::Effectful => { + for i in 0..args.len() { + let name = format!("{ident}_arg_{i}"); + + let arg_symbol = { + let ident = name.clone().into(); + scope.introduce(ident, Region::zero()).unwrap() + }; + + let arg_var = var_store.fresh(); + + arguments.push(( + arg_var, + AnnotatedMark::new(var_store), + Loc::at_zero(Pattern::Identifier(arg_symbol)), + )); + + linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var))); + } + + let foreign_symbol_name = format!("roc_fx_{ident}"); + let foreign_call = Expr::ForeignCall { + foreign_symbol: foreign_symbol_name.into(), + args: linked_symbol_arguments, + ret_var: var_store.fresh(), + }; + + Expr::Closure(ClosureData { + function_type: var_store.fresh(), + closure_type: var_store.fresh(), + return_type: var_store.fresh(), + fx_type: var_store.fresh(), + name: symbol, + captured_symbols: std::vec::Vec::new(), + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Loc::at_zero(foreign_call)), + }) + } _ => { // not a function diff --git a/crates/compiler/can/src/lib.rs b/crates/compiler/can/src/lib.rs index 17fc63d2cee..5bb8ce19065 100644 --- a/crates/compiler/can/src/lib.rs +++ b/crates/compiler/can/src/lib.rs @@ -14,6 +14,7 @@ pub mod copy; pub mod def; mod derive; pub mod desugar; +pub mod effect_module; pub mod env; pub mod exhaustive; pub mod expected; @@ -25,7 +26,6 @@ pub mod procedure; pub mod scope; pub mod string; pub mod suffixed; -pub mod task_module; pub mod traverse; pub use derive::DERIVED_REGION; diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 38276a9fd3a..c2fc06aaa26 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -533,7 +533,7 @@ pub fn canonicalize_module_defs<'a>( aliases: Default::default(), }; - let hosted_def = crate::task_module::build_host_exposed_def( + let hosted_def = crate::effect_module::build_host_exposed_def( &mut scope, *symbol, &ident, var_store, annotation, ); @@ -586,7 +586,7 @@ pub fn canonicalize_module_defs<'a>( aliases: Default::default(), }; - let hosted_def = crate::task_module::build_host_exposed_def( + let hosted_def = crate::effect_module::build_host_exposed_def( &mut scope, *symbol, &ident, var_store, annotation, ); diff --git a/examples/cli/effects-platform/Effect.roc b/examples/cli/effects-platform/Effect.roc new file mode 100644 index 00000000000..ba79d4fce54 --- /dev/null +++ b/examples/cli/effects-platform/Effect.roc @@ -0,0 +1,7 @@ +hosted Effect + exposes [putLine, getLine] + imports [] + +putLine : Str => {} + +getLine : {} => Str diff --git a/examples/cli/effects-platform/PlatformTasks.roc b/examples/cli/effects-platform/PlatformTasks.roc deleted file mode 100644 index a5e70d31e9a..00000000000 --- a/examples/cli/effects-platform/PlatformTasks.roc +++ /dev/null @@ -1,7 +0,0 @@ -hosted PlatformTasks - exposes [putLine, getLine] - imports [] - -putLine : Str -> Task {} * - -getLine : Task Str * diff --git a/examples/cli/effects-platform/host.zig b/examples/cli/effects-platform/host.zig index 04b650705f8..7bb9fc2a5d8 100644 --- a/examples/cli/effects-platform/host.zig +++ b/examples/cli/effects-platform/host.zig @@ -12,9 +12,6 @@ const Allocator = mem.Allocator; extern fn roc__mainForHost_1_exposed_generic([*]u8) void; extern fn roc__mainForHost_1_exposed_size() i64; -extern fn roc__mainForHost_0_caller(*const u8, [*]u8, [*]u8) void; -extern fn roc__mainForHost_0_size() i64; -extern fn roc__mainForHost_0_result_size() i64; const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; @@ -127,8 +124,6 @@ pub export fn main() u8 { roc__mainForHost_1_exposed_generic(output); - call_the_closure(output); - const nanos = timer.read(); const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0); @@ -141,35 +136,6 @@ fn to_seconds(tms: std.os.timespec) f64 { return @as(f64, @floatFromInt(tms.tv_sec)) + (@as(f64, @floatFromInt(tms.tv_nsec)) / 1_000_000_000.0); } -fn call_the_closure(closure_data_pointer: [*]u8) void { - const allocator = std.heap.page_allocator; - - const size = roc__mainForHost_0_result_size(); - - if (size == 0) { - // the function call returns an empty record - // allocating 0 bytes causes issues because the allocator will return a NULL pointer - // So it's special-cased - const flags: u8 = 0; - var result: [1]u8 = .{0}; - roc__mainForHost_0_caller(&flags, closure_data_pointer, &result); - - return; - } - - const raw_output = allocator.alignedAlloc(u8, @alignOf(u64), @as(usize, @intCast(size))) catch unreachable; - var output = @as([*]u8, @ptrCast(raw_output)); - - defer { - allocator.free(raw_output); - } - - const flags: u8 = 0; - roc__mainForHost_0_caller(&flags, closure_data_pointer, output); - - return; -} - pub export fn roc_fx_getLine() str.RocStr { return roc_fx_getLine_help() catch return str.RocStr.empty(); } diff --git a/examples/cli/effects-platform/main.roc b/examples/cli/effects-platform/main.roc index 109d4150a24..6e9934684f9 100644 --- a/examples/cli/effects-platform/main.roc +++ b/examples/cli/effects-platform/main.roc @@ -1,9 +1,9 @@ platform "effects" - requires {} { main : Task {} [] } + requires {} { main : {} => {} } exposes [] packages {} imports [] provides [mainForHost] -mainForHost : Task {} [] -mainForHost = main +mainForHost : {} => {} +mainForHost = \{} -> main {} diff --git a/examples/cli/effects.roc b/examples/cli/effects.roc index 4cadf7b8d85..0a05c04055e 100644 --- a/examples/cli/effects.roc +++ b/examples/cli/effects.roc @@ -1,11 +1,9 @@ app [main] { pf: platform "effects-platform/main.roc" } -import pf.PlatformTasks +import pf.Effect -main : Task {} [] -main = - line = PlatformTasks.getLine! - PlatformTasks.putLine! "You entered: $(line)" - PlatformTasks.putLine! "It is known" - - Task.ok {} +main : {} => {} +main = \{} -> + line = Effect.getLine {} + _ = Effect.putLine "You entered: $(line)" + Effect.putLine "It is known" From f677592f97909b28c0d5941bf3b82fcd74c685f8 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sun, 13 Oct 2024 22:59:53 -0300 Subject: [PATCH 13/73] Ignore unused fx vars in mono --- crates/compiler/mono/src/ir.rs | 13 ++++--------- crates/compiler/mono/src/layout.rs | 6 +++--- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index ca11902be24..128f8ecf8f7 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -4519,16 +4519,14 @@ pub fn with_hole<'a>( let content = env.subs.get_content_without_compacting(variable); - if let Content::Structure(FlatType::Func(arg_vars, _, ret_var, fx_var)) = content { + if let Content::Structure(FlatType::Func(arg_vars, _, ret_var, _fx_var)) = content { let ret_var = *ret_var; - let fx_var = *fx_var; let arg_vars = *arg_vars; tag_union_to_function( env, arg_vars, ret_var, - fx_var, tag_name, closure_name, ext_var, @@ -6165,7 +6163,7 @@ fn late_resolve_ability_specialization( if let Some(spec_symbol) = opt_resolved { // Fast path: specialization is monomorphic, was found during solving. spec_symbol - } else if let Content::Structure(FlatType::Func(_, lambda_set, _, fx_var)) = + } else if let Content::Structure(FlatType::Func(_, lambda_set, _, _fx_var)) = env.subs.get_content_without_compacting(specialization_var) { // Fast path: the member is a function, so the lambda set will tell us the @@ -6688,7 +6686,6 @@ fn tag_union_to_function<'a>( env: &mut Env<'a, '_>, argument_variables: VariableSubsSlice, return_variable: Variable, - fx_variable: Variable, tag_name: TagName, proc_symbol: Symbol, ext_var: Variable, @@ -6698,8 +6695,6 @@ fn tag_union_to_function<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - // [purity-inference] TODO: Do we need fx_variable for anything? - let mut loc_pattern_args = vec![]; let mut loc_expr_args = vec![]; @@ -6893,7 +6888,7 @@ fn register_capturing_closure<'a>( let is_self_recursive = !matches!(recursive, roc_can::expr::Recursive::NotRecursive); let captured_symbols = match *env.subs.get_content_without_compacting(function_type) { - Content::Structure(FlatType::Func(args, closure_var, ret, fx_var)) => { + Content::Structure(FlatType::Func(args, closure_var, ret, _fx_var)) => { let lambda_set_layout = { LambdaSet::from_var_pub( layout_cache, @@ -10134,7 +10129,7 @@ fn find_lambda_sets_help( FlatType::Apply(_, arguments) => { stack.extend(subs.get_subs_slice(*arguments).iter().rev()); } - FlatType::Func(arguments, lambda_set_var, ret_var, fx_var) => { + FlatType::Func(arguments, lambda_set_var, ret_var, _fx_var) => { use std::collections::hash_map::Entry; // Only insert a lambda_set_var if we didn't already have a value for this key. if let Entry::Vacant(entry) = result.entry(*lambda_set_var) { diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index f5c6955aafd..ed65781b4ce 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -593,7 +593,7 @@ impl<'a> RawFunctionLayout<'a> { let arena = env.arena; match flat_type { - Func(args, closure_var, ret_var, fx_var) => { + Func(args, closure_var, ret_var, _fx_var) => { let mut fn_args = Vec::with_capacity_in(args.len(), arena); let mut cache_criteria = CACHEABLE; @@ -2151,7 +2151,7 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) { stack.push((*var, depth_any + 1, depth_lset)); } } - FlatType::Func(args, lset, ret, fx_var) => { + FlatType::Func(args, lset, ret, _fx_var) => { for var in subs.get_subs_slice(*args) { stack.push((*var, depth_any + 1, depth_lset)); } @@ -3321,7 +3321,7 @@ fn layout_from_flat_type<'a>( } } } - Func(args, closure_var, ret_var, fx_var) => { + Func(args, closure_var, ret_var, _fx_var) => { if env.is_seen(closure_var) { // TODO(recursive-layouts): after the naked pointer is updated, we can cache `var` to // point to the updated layout. From 2cce5ad0233206bae4b182d3d9544831561fe7a2 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 14 Oct 2024 20:05:33 -0300 Subject: [PATCH 14/73] Allow unsuffixed statements in parser Moves the "STATEMENT AFTER EXPRESSION" error from the parser to canonicalization. We'll later use this to allow this case in effectful functions. --- crates/compiler/can/src/def.rs | 3 + crates/compiler/can/src/desugar.rs | 15 +- crates/compiler/can/src/effect_module.rs | 1 + crates/compiler/can/src/suffixed.rs | 1 + ...fixed_tests__closure_with_annotations.snap | 1 + ...ed__suffixed_tests__closure_with_defs.snap | 1 + crates/compiler/fmt/src/def.rs | 3 + crates/compiler/load/tests/test_reporting.rs | 10 +- crates/compiler/load_internal/src/docs.rs | 4 + .../compiler/load_internal/tests/test_load.rs | 43 ---- crates/compiler/parse/src/ast.rs | 7 +- crates/compiler/parse/src/expr.rs | 18 +- crates/compiler/parse/src/normalize.rs | 2 +- crates/compiler/parse/src/parser.rs | 1 - crates/compiler/problem/src/can.rs | 3 + ...tful_closure_statements.expr.formatted.roc | 11 + ...fectful_closure_statements.expr.result-ast | 188 ++++++++++++++++++ .../effectful_closure_statements.expr.roc | 11 + .../test_syntax/tests/test_snapshots.rs | 1 + crates/language_server/src/analysis/tokens.rs | 1 + crates/repl_ui/src/repl_state.rs | 1 + crates/reporting/src/error/canonicalize.rs | 28 +++ crates/reporting/src/error/parse.rs | 33 --- 23 files changed, 280 insertions(+), 107 deletions(-) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.roc diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index cf1d40b0b75..f07585e63e4 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -1182,6 +1182,7 @@ fn canonicalize_value_defs<'a>( } PendingValue::InvalidIngestedFile => { /* skip */ } PendingValue::ImportNameConflict => { /* skip */ } + PendingValue::StmtAfterExpr => { /* skip */ } } } @@ -3019,6 +3020,7 @@ enum PendingValue<'a> { SignatureDefMismatch, InvalidIngestedFile, ImportNameConflict, + StmtAfterExpr, } struct PendingExpectOrDbg<'a> { @@ -3293,6 +3295,7 @@ fn to_pending_value_def<'a>( PendingValue::Def(PendingValueDef::IngestedFile(loc_pattern, ingested_file.annotation.map(|ann| ann.annotation), ingested_file.path)) } + StmtAfterExpr => PendingValue::StmtAfterExpr, Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"), } } diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 59e1a2344e2..bd9dfc485f4 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -11,8 +11,8 @@ use roc_module::called_via::{BinOp, CalledVia}; use roc_module::ident::ModuleName; use roc_parse::ast::Expr::{self, *}; use roc_parse::ast::{ - AssignedField, Collection, Defs, ModuleImportParams, Pattern, StrLiteral, StrSegment, - TypeAnnotation, ValueDef, WhenBranch, + is_expr_suffixed, AssignedField, Collection, Defs, ModuleImportParams, Pattern, StrLiteral, + StrSegment, TypeAnnotation, ValueDef, WhenBranch, }; use roc_problem::can::Problem; use roc_region::all::{Loc, Region}; @@ -200,11 +200,20 @@ fn desugar_value_def<'a>( } IngestedFileImport(_) => *def, + StmtAfterExpr => internal_error!("unexpected StmtAfterExpr"), + Stmt(stmt_expr) => { // desugar `stmt_expr!` to // _ : {} // _ = stmt_expr! + if !is_expr_suffixed(&stmt_expr.value) { + // [purity-inference] TODO: this is ok in purity inference mode + env.problems.push(Problem::StmtAfterExpr(stmt_expr.region)); + + return ValueDef::StmtAfterExpr; + } + let region = stmt_expr.region; let new_pat = env .arena @@ -364,7 +373,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) // TODO support desugaring of Dbg and ExpectFx Dbg { .. } | ExpectFx { .. } => value_def, - ModuleImport { .. } | IngestedFileImport(_) => value_def, + ModuleImport { .. } | IngestedFileImport(_) | StmtAfterExpr => value_def, Stmt(..) => { internal_error!( diff --git a/crates/compiler/can/src/effect_module.rs b/crates/compiler/can/src/effect_module.rs index c2e80fad9ff..ab573a4c5bd 100644 --- a/crates/compiler/can/src/effect_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -141,6 +141,7 @@ pub fn build_host_exposed_def( closure_type: var_store.fresh(), return_type: var_store.fresh(), fx_type: var_store.fresh(), + early_returns: vec![], name: symbol, captured_symbols: std::vec::Vec::new(), recursive: Recursive::NotRecursive, diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index 8a2900a385d..53735bcd8ba 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -683,6 +683,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None, AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } => Some((body_pattern, body_expr, Some((ann_pattern, ann_type)))), Body (def_pattern, def_expr) => Some((def_pattern, def_expr, None)), + StmtAfterExpr => None, }; match maybe_suffixed_value_def { diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index 3e94d3b70d9..064c592e051 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -53,6 +53,7 @@ Defs { [], ), ], + Pure, @22-30 Apply( "", "Task", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index e4158ccf1d6..b68387bd88c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -62,6 +62,7 @@ Defs { [], ), ], + Pure, @34-45 Apply( "", "Task", diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 642dab3f542..6c65806aa6d 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -4,6 +4,7 @@ use crate::expr::fmt_str_literal; use crate::pattern::fmt_pattern; use crate::spaces::{fmt_default_newline, fmt_default_spaces, fmt_spaces, INDENT}; use crate::Buf; +use roc_error_macros::internal_error; use roc_parse::ast::{ AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, @@ -423,6 +424,7 @@ impl<'a> Formattable for ValueDef<'a> { ModuleImport(module_import) => module_import.is_multiline(), IngestedFileImport(ingested_file_import) => ingested_file_import.is_multiline(), Stmt(loc_expr) => loc_expr.is_multiline(), + StmtAfterExpr => internal_error!("shouldn't exist before can"), } } @@ -464,6 +466,7 @@ impl<'a> Formattable for ValueDef<'a> { ModuleImport(module_import) => module_import.format(buf, indent), IngestedFileImport(ingested_file_import) => ingested_file_import.format(buf, indent), Stmt(loc_expr) => loc_expr.format_with_options(buf, parens, newlines, indent), + StmtAfterExpr => internal_error!("shouldn't exist before can"), } } } diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 405ffdfe368..55c5abfaa1b 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -4322,21 +4322,15 @@ mod test_reporting { " ), @r###" - ── STATEMENT AFTER EXPRESSION in tmp/double_equals_in_def/Test.roc ───────────── + ── STATEMENT AFTER EXPRESSION in /code/proj/Main.roc ─────────────────────────── I just finished parsing an expression with a series of definitions, and this line is indented as if it's intended to be part of that expression: - 1│ app "test" provides [main] to "./platform" - 2│ - 3│ main = - 4│ x = 3 - 5│ y = 6│ x == 5 - 7│ Num.add 1 2 - ^ + ^^^^^^ However, I already saw the final expression in that series of definitions. diff --git a/crates/compiler/load_internal/src/docs.rs b/crates/compiler/load_internal/src/docs.rs index 541e4cc094a..f9b78d7cd86 100644 --- a/crates/compiler/load_internal/src/docs.rs +++ b/crates/compiler/load_internal/src/docs.rs @@ -282,6 +282,10 @@ fn generate_entry_docs( // Don't generate docs for ingested file imports } + ValueDef::StmtAfterExpr { .. } => { + // Ignore. Canonicalization will produce an error. + } + ValueDef::Stmt(loc_expr) => { if let roc_parse::ast::Expr::Var { ident: identifier, .. diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index 5abd823c05a..aa9c6fc47ae 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -846,49 +846,6 @@ fn platform_does_not_exist() { } } -#[test] -fn platform_parse_error() { - let modules = vec![ - ( - "platform/main.roc", - indoc!( - r#" - platform "hello-c" - requires {} { main : Str } - exposes [] - packages {} - imports [] - provides [mainForHost] - blah 1 2 3 # causing a parse error on purpose - - mainForHost : Str - "# - ), - ), - ( - "main.roc", - indoc!( - r#" - app "hello-world" - packages { pf: "platform/main.roc" } - imports [] - provides [main] to pf - - main = "Hello, World!\n" - "# - ), - ), - ]; - - match multiple_modules("platform_parse_error", modules) { - Err(report) => { - assert!(report.contains("STATEMENT AFTER EXPRESSION")); - assert!(report.contains("blah 1 2 3 # causing a parse error on purpose")); - } - Ok(_) => unreachable!("we expect failure here"), - } -} - #[test] // See https://github.com/roc-lang/roc/issues/2413 fn platform_exposes_main_return_by_pointer_issue() { diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index b2d89990439..37818fadcb6 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -841,6 +841,8 @@ pub enum ValueDef<'a> { IngestedFileImport(IngestedFileImport<'a>), Stmt(&'a Loc>), + + StmtAfterExpr, } impl<'a> ValueDef<'a> { @@ -1093,7 +1095,9 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> { } } ValueDef::Stmt(loc_expr) => self.push_pending_from_expr(&loc_expr.value), - ValueDef::Annotation(_, _) | ValueDef::IngestedFileImport(_) => {} + ValueDef::Annotation(_, _) + | ValueDef::IngestedFileImport(_) + | ValueDef::StmtAfterExpr => {} } self.index += 1; @@ -2752,6 +2756,7 @@ impl<'a> Malformed for ValueDef<'a> { annotation, }) => path.is_malformed() || annotation.is_malformed(), ValueDef::Stmt(loc_expr) => loc_expr.is_malformed(), + ValueDef::StmtAfterExpr => false, } } } diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 9be4c7c3bc7..d656bf09bfa 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -3120,7 +3120,7 @@ fn stmts_to_defs<'a>( break; } Stmt::Expr(e) => { - if is_expr_suffixed(&e) && i + 1 < stmts.len() { + if i + 1 < stmts.len() { defs.push_value_def( ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))), sp_stmt.item.region, @@ -3128,10 +3128,6 @@ fn stmts_to_defs<'a>( &[], ); } else { - if last_expr.is_some() { - return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); - } - let e = if sp_stmt.before.is_empty() { e } else { @@ -3142,10 +3138,6 @@ fn stmts_to_defs<'a>( } } Stmt::Backpassing(pats, call) => { - if last_expr.is_some() { - return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); - } - if i + 1 >= stmts.len() { return Err(EExpr::BackpassContinue(sp_stmt.item.region.end())); } @@ -3169,10 +3161,6 @@ fn stmts_to_defs<'a>( } Stmt::TypeDef(td) => { - if last_expr.is_some() { - return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); - } - if let ( TypeDef::Alias { header, @@ -3224,10 +3212,6 @@ fn stmts_to_defs<'a>( } } Stmt::ValueDef(vd) => { - if last_expr.is_some() { - return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); - } - // NOTE: it shouldn't be necessary to convert ValueDef::Dbg into an expr, but // it turns out that ValueDef::Dbg exposes some bugs in the rest of the compiler. // In particular, it seems that the solver thinks the dbg expr must be a bool. diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index f0b4e43387e..8151400c087 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -439,6 +439,7 @@ impl<'a> Normalize<'a> for ValueDef<'a> { IngestedFileImport(ingested_file_import.normalize(arena)) } Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.normalize(arena))), + StmtAfterExpr => StmtAfterExpr, } } } @@ -1072,7 +1073,6 @@ impl<'a> Normalize<'a> for EExpr<'a> { EExpr::IndentEnd(_pos) => EExpr::IndentEnd(Position::zero()), EExpr::UnexpectedComma(_pos) => EExpr::UnexpectedComma(Position::zero()), EExpr::UnexpectedTopLevelExpr(_pos) => EExpr::UnexpectedTopLevelExpr(Position::zero()), - EExpr::StmtAfterExpr(_pos) => EExpr::StmtAfterExpr(Position::zero()), EExpr::RecordUpdateOldBuilderField(_pos) => { EExpr::RecordUpdateOldBuilderField(Region::zero()) } diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index 5b10ac111c2..174e9387b48 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -303,7 +303,6 @@ pub enum EExpr<'a> { Start(Position), End(Position), BadExprEnd(Position), - StmtAfterExpr(Position), Space(BadInputError, Position), Dot(Position), diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index 8591c640b8d..63e66441031 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -251,6 +251,7 @@ pub enum Problem { ReturnAtEndOfFunction { region: Region, }, + StmtAfterExpr(Region), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -335,6 +336,7 @@ impl Problem { Problem::ReturnOutsideOfFunction { .. } => Warning, Problem::StatementsAfterReturn { .. } => Warning, Problem::ReturnAtEndOfFunction { .. } => Warning, + Problem::StmtAfterExpr(_) => Fatal, } } @@ -505,6 +507,7 @@ impl Problem { | Problem::BadRecursion(cycle_entries) => { cycle_entries.first().map(|entry| entry.expr_region) } + Problem::StmtAfterExpr(region) => Some(*region), Problem::RuntimeError(RuntimeError::UnresolvedTypeVar) | Problem::RuntimeError(RuntimeError::ErroneousType) | Problem::RuntimeError(RuntimeError::NonExhaustivePattern) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.formatted.roc new file mode 100644 index 00000000000..20e58cb41e4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.formatted.roc @@ -0,0 +1,11 @@ +\{} -> + echo "Welcome to the DMV!" + age = readInt + + if age < 16 then + echo "You're too young to drive!" + exit 1 + else + {} + + echo "Let's get started on your driver's license application." \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast new file mode 100644 index 00000000000..e5afcf14b48 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast @@ -0,0 +1,188 @@ +SpaceAfter( + Closure( + [ + @1-3 RecordDestructure( + [], + ), + ], + @11-222 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @11-37, + @42-55, + @61-154, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 3, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Stmt( + @11-37 Apply( + @11-15 Var { + module_name: "", + ident: "echo", + }, + [ + @16-37 Str( + PlainLine( + "Welcome to the DMV!", + ), + ), + ], + Space, + ), + ), + Body( + @42-45 Identifier { + ident: "age", + }, + @48-55 Var { + module_name: "", + ident: "readInt", + }, + ), + Stmt( + @61-154 If { + if_thens: [ + ( + @64-72 BinOps( + [ + ( + @64-67 Var { + module_name: "", + ident: "age", + }, + @68-69 LessThan, + ), + ], + @70-72 Num( + "16", + ), + ), + @86-134 SpaceBefore( + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @86-119, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Stmt( + @86-119 Apply( + @86-90 Var { + module_name: "", + ident: "echo", + }, + [ + @91-119 Str( + PlainLine( + "You're too young to drive!", + ), + ), + ], + Space, + ), + ), + ], + }, + @128-134 SpaceBefore( + Apply( + @128-132 Var { + module_name: "", + ident: "exit", + }, + [ + @133-134 Num( + "1", + ), + ], + Space, + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], + ), + [ + Newline, + ], + ), + ), + ], + final_else: @152-154 SpaceBefore( + Record( + [], + ), + [ + Newline, + ], + ), + indented_else: false, + }, + ), + ], + }, + @160-222 SpaceBefore( + Apply( + @160-164 Var { + module_name: "", + ident: "echo", + }, + [ + @165-222 Str( + PlainLine( + "Let's get started on your driver's license application.", + ), + ), + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.roc new file mode 100644 index 00000000000..861fc0b97ed --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.roc @@ -0,0 +1,11 @@ +\{} -> + echo "Welcome to the DMV!" + age = readInt + + if age < 16 then + echo "You're too young to drive!" + exit 1 + else + {} + + echo "Let's get started on your driver's license application." diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 164fcb6d111..ac2325f72a8 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -313,6 +313,7 @@ mod test_snapshots { pass/defs_suffixed_middle_extra_indents.moduledefs, pass/destructure_tag_assignment.expr, pass/docs.expr, + pass/effectful_closure_statements.expr, pass/empty_app_header.header, pass/empty_hosted_header.header, pass/empty_list.expr, diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 3f182ebc479..6e4035ca6b7 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -643,6 +643,7 @@ impl IterTokens for ValueDef<'_> { onetoken(Token::Import, import.name.item.region, arena) } ValueDef::Stmt(loc_expr) => loc_expr.iter_tokens(arena), + ValueDef::StmtAfterExpr => BumpVec::new_in(arena), } } } diff --git a/crates/repl_ui/src/repl_state.rs b/crates/repl_ui/src/repl_state.rs index 2b612d832c2..f3db9cffa13 100644 --- a/crates/repl_ui/src/repl_state.rs +++ b/crates/repl_ui/src/repl_state.rs @@ -246,6 +246,7 @@ impl ReplState { return ReplAction::Nothing; } ValueDef::Stmt(_) => todo!(), + ValueDef::StmtAfterExpr => todo!("effects in repl"), } } } diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index f8e7865d31e..73a86295776 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -64,6 +64,7 @@ const ABILITY_IMPLEMENTATION_NOT_IDENTIFIER: &str = "ABILITY IMPLEMENTATION NOT const DUPLICATE_IMPLEMENTATION: &str = "DUPLICATE IMPLEMENTATION"; const UNNECESSARY_IMPLEMENTATIONS: &str = "UNNECESSARY IMPLEMENTATIONS"; const INCOMPLETE_ABILITY_IMPLEMENTATION: &str = "INCOMPLETE ABILITY IMPLEMENTATION"; +const STATEMENT_AFTER_EXPRESSION: &str = "STATEMENT AFTER EXPRESSION"; pub fn can_problem<'b>( alloc: &'b RocDocAllocator<'b>, @@ -1400,6 +1401,33 @@ pub fn can_problem<'b>( title = "UNNECESSARY RETURN".to_string(); } + + Problem::StmtAfterExpr(region) => { + // TODO: Update when [purity-inference] is fully implemented + doc = alloc.stack([ + alloc + .reflow(r"I just finished parsing an expression with a series of definitions,"), + alloc.reflow( + r"and this line is indented as if it's intended to be part of that expression:", + ), + alloc.region(lines.convert_region(region), severity), + alloc.concat([alloc.reflow( + "However, I already saw the final expression in that series of definitions.", + )]), + alloc.tip().append( + alloc.reflow( + "An expression like `4`, `\"hello\"`, or `functionCall MyThing` is like `return 4` in other programming languages. To me, it seems like you did `return 4` followed by more code in the lines after, that code would never be executed!" + ) + ), + alloc.tip().append( + alloc.reflow( + "If you are working with `Task`, this error can happen if you forgot a `!` somewhere." + ) + ) + ]); + + title = STATEMENT_AFTER_EXPRESSION.to_string(); + } }; Report { diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index ae8dcf957a6..62a36dda738 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -669,39 +669,6 @@ fn to_expr_report<'a>( severity, } } - EExpr::StmtAfterExpr(pos) => { - let surroundings = Region::new(start, *pos); - let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); - - let doc = alloc.stack([ - alloc - .reflow(r"I just finished parsing an expression with a series of definitions,"), - alloc.reflow( - r"and this line is indented as if it's intended to be part of that expression:", - ), - alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), - alloc.reflow( - "However, I already saw the final expression in that series of definitions." - ), - alloc.tip().append( - alloc.reflow( - "An expression like `4`, `\"hello\"`, or `functionCall MyThing` is like `return 4` in other programming languages. To me, it seems like you did `return 4` followed by more code in the lines after, that code would never be executed!" - ) - ), - alloc.tip().append( - alloc.reflow( - "If you are working with `Task`, this error can happen if you forgot a `!` somewhere." - ) - ) - ]); - - Report { - filename, - doc, - title: "STATEMENT AFTER EXPRESSION".to_string(), - severity, - } - } EExpr::Return(EReturn::Return(pos) | EReturn::IndentReturnValue(pos), start) => { to_expr_report( alloc, From 7a7650c11dd052e77357663a72e3cd3607ba5834 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 24 Oct 2024 11:59:31 -0300 Subject: [PATCH 15/73] Parse lowercase idents ending in `!` --- crates/compiler/builtins/roc/Task.roc | 4 +- crates/compiler/can/src/effect_module.rs | 3 +- crates/compiler/parse/src/ident.rs | 25 +- .../fail/exposed_type_bang.header.result-ast | 1 + .../fail/exposed_type_bang.header.roc | 1 + ...ucture_field_bang_no_space.expr.result-ast | 1 + ...d_destructure_field_bang_no_space.expr.roc | 3 + .../pass/ann_effectful_fn.expr.result-ast | 6 +- .../pass/call_bang.expr.formatted.roc | 1 + .../snapshots/pass/call_bang.expr.result-ast | 17 ++ .../tests/snapshots/pass/call_bang.expr.roc | 1 + .../call_bang_no_space.expr.formatted.roc | 1 + .../pass/call_bang_no_space.expr.result-ast | 18 ++ .../pass/call_bang_no_space.expr.roc | 1 + .../pass/def_bang.expr.formatted.roc | 4 + .../snapshots/pass/def_bang.expr.result-ast | 64 ++++++ .../tests/snapshots/pass/def_bang.expr.roc | 4 + ...dle_extra_indents.moduledefs.formatted.roc | 4 +- ...middle_extra_indents.moduledefs.result-ast | 4 +- ...ffixed_middle_extra_indents.moduledefs.roc | 4 +- ...fectful_closure_statements.expr.result-ast | 24 +- ...> pizza_question.moduledefs.formatted.roc} | 7 +- ...t => pizza_question.moduledefs.result-ast} | 83 +++---- ...defs.roc => pizza_question.moduledefs.roc} | 7 +- ..._destructure_field_bang.expr.formatted.roc | 3 + ...ord_destructure_field_bang.expr.result-ast | 60 +++++ .../record_destructure_field_bang.expr.roc | 3 + ...cord_literal_field_bang.expr.formatted.roc | 4 + .../record_literal_field_bang.expr.result-ast | 46 ++++ .../pass/record_literal_field_bang.expr.roc | 4 + .../pass/suffixed_bang.expr.result-ast | 13 -- .../snapshots/pass/suffixed_bang.expr.roc | 1 - ...ang_multiple_defs.moduledefs.formatted.roc | 5 - ...d_bang_multiple_defs.moduledefs.result-ast | 117 ---------- ...suffixed_bang_multiple_defs.moduledefs.roc | 5 - .../suffixed_bang_nested.expr.formatted.roc | 1 - .../pass/suffixed_bang_nested.expr.result-ast | 45 ---- .../pass/suffixed_bang_nested.expr.roc | 1 - .../suffixed_bang_one_def.full.formatted.roc | 17 -- .../suffixed_bang_one_def.full.result-ast | 213 ------------------ .../pass/suffixed_bang_one_def.full.roc | 16 -- ...ixed_bang_optional_last.full.formatted.roc | 9 - ...uffixed_bang_optional_last.full.result-ast | 134 ----------- .../pass/suffixed_bang_optional_last.full.roc | 12 - .../tag_destructure_bang.expr.formatted.roc | 3 + .../pass/tag_destructure_bang.expr.result-ast | 63 ++++++ .../pass/tag_destructure_bang.expr.roc | 3 + ...structure_bang_no_space.expr.formatted.roc | 3 + ..._destructure_bang_no_space.expr.result-ast | 63 ++++++ .../tag_destructure_bang_no_space.expr.roc | 3 + .../tuple_destructure_bang.expr.formatted.roc | 3 + .../tuple_destructure_bang.expr.result-ast | 60 +++++ .../pass/tuple_destructure_bang.expr.roc | 3 + .../test_syntax/tests/test_snapshots.rs | 17 +- examples/cli/effects-platform/Effect.roc | 6 +- examples/cli/effects.roc | 6 +- examples/inspect-logging.roc | 50 ++-- 57 files changed, 569 insertions(+), 711 deletions(-) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.roc rename crates/compiler/test_syntax/tests/snapshots/pass/{pizza_bang.moduledefs.formatted.roc => pizza_question.moduledefs.formatted.roc} (52%) rename crates/compiler/test_syntax/tests/snapshots/pass/{pizza_bang.moduledefs.result-ast => pizza_question.moduledefs.result-ast} (66%) rename crates/compiler/test_syntax/tests/snapshots/pass/{pizza_bang.moduledefs.roc => pizza_question.moduledefs.roc} (53%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.roc diff --git a/crates/compiler/builtins/roc/Task.roc b/crates/compiler/builtins/roc/Task.roc index 4a904c6a7da..368ee64bd65 100644 --- a/crates/compiler/builtins/roc/Task.roc +++ b/crates/compiler/builtins/roc/Task.roc @@ -222,8 +222,8 @@ sequence = \taskList -> Task.loop (taskList, List.withCapacity (List.len taskList)) \(tasks, values) -> when tasks is [task, .. as rest] -> - value = task! - Task.ok (Step (rest, List.append values value)) + Task.map task \value -> + Step (rest, List.append values value) [] -> Task.ok (Done values) diff --git a/crates/compiler/can/src/effect_module.rs b/crates/compiler/can/src/effect_module.rs index ab573a4c5bd..394de4b4a3c 100644 --- a/crates/compiler/can/src/effect_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -129,7 +129,8 @@ pub fn build_host_exposed_def( linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var))); } - let foreign_symbol_name = format!("roc_fx_{ident}"); + let ident_without_bang = ident.trim_end_matches('!'); + let foreign_symbol_name = format!("roc_fx_{ident_without_bang}"); let foreign_call = Expr::ForeignCall { foreign_symbol: foreign_symbol_name.into(), args: linked_symbol_arguments, diff --git a/crates/compiler/parse/src/ident.rs b/crates/compiler/parse/src/ident.rs index 9e9c2f5bc94..34595ca78e2 100644 --- a/crates/compiler/parse/src/ident.rs +++ b/crates/compiler/parse/src/ident.rs @@ -252,21 +252,27 @@ fn is_alnum(ch: char) -> bool { } fn chomp_lowercase_part(buffer: &[u8]) -> Result<&str, Progress> { - chomp_part(char::is_lowercase, is_alnum, buffer) + chomp_part(char::is_lowercase, is_alnum, true, buffer) } fn chomp_uppercase_part(buffer: &[u8]) -> Result<&str, Progress> { - chomp_part(char::is_uppercase, is_alnum, buffer) + chomp_part(char::is_uppercase, is_alnum, false, buffer) } fn chomp_anycase_part(buffer: &[u8]) -> Result<&str, Progress> { - chomp_part(char::is_alphabetic, is_alnum, buffer) + use encode_unicode::CharExt; + + let allow_bang = + char::from_utf8_slice_start(buffer).map_or(false, |(leading, _)| leading.is_lowercase()); + + chomp_part(char::is_alphabetic, is_alnum, allow_bang, buffer) } fn chomp_integer_part(buffer: &[u8]) -> Result<&str, Progress> { chomp_part( |ch| char::is_ascii_digit(&ch), |ch| char::is_ascii_digit(&ch), + false, buffer, ) } @@ -276,7 +282,12 @@ fn is_plausible_ident_continue(ch: char) -> bool { } #[inline(always)] -fn chomp_part(leading_is_good: F, rest_is_good: G, buffer: &[u8]) -> Result<&str, Progress> +fn chomp_part( + leading_is_good: F, + rest_is_good: G, + allow_bang: bool, + buffer: &[u8], +) -> Result<&str, Progress> where F: Fn(char) -> bool, G: Fn(char) -> bool, @@ -296,6 +307,9 @@ where while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { if rest_is_good(ch) { chomped += width; + } else if allow_bang && ch == '!' { + chomped += width; + break; } else { // we're done break; @@ -474,6 +488,9 @@ fn chomp_identifier_chain<'a>( while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { if ch.is_alphabetic() || ch.is_ascii_digit() { chomped += width; + } else if ch == '!' && !first_is_uppercase { + chomped += width; + break; } else { // we're done break; diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.result-ast new file mode 100644 index 00000000000..7f3bb8b55d9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.result-ast @@ -0,0 +1 @@ +Header(Exposes(ListEnd(@12), @7)) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.roc b/crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.roc new file mode 100644 index 00000000000..a2a4c48ccfc --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/exposed_type_bang.header.roc @@ -0,0 +1 @@ +module [Type!] diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.result-ast new file mode 100644 index 00000000000..2129436e149 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.result-ast @@ -0,0 +1 @@ +Expr(Record(End(@1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.roc new file mode 100644 index 00000000000..7ccda8158d7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_destructure_field_bang_no_space.expr.roc @@ -0,0 +1,3 @@ +{launchTheNukes!wrong, code} = config + +launchTheNukes! code diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast index 54d323f80ec..998b247a5c5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast @@ -2,16 +2,16 @@ SpaceAfter( Defs( Defs { tags: [ - Index(2147483648), + EitherIndex(2147483648), ], regions: [ @0-89, ], space_before: [ - Slice(start = 0, length = 0), + Slice { start: 0, length: 0 }, ], space_after: [ - Slice(start = 0, length = 0), + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.formatted.roc new file mode 100644 index 00000000000..5457c0fa2cf --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.formatted.roc @@ -0,0 +1 @@ +launchTheNukes! 123 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.result-ast new file mode 100644 index 00000000000..59a93a6b325 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.result-ast @@ -0,0 +1,17 @@ +SpaceAfter( + Apply( + @0-15 Var { + module_name: "", + ident: "launchTheNukes!", + }, + [ + @16-19 Num( + "123", + ), + ], + Space, + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.roc new file mode 100644 index 00000000000..70f14aa0f9d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang.expr.roc @@ -0,0 +1 @@ +launchTheNukes! 123 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.formatted.roc new file mode 100644 index 00000000000..5b3974ff1f4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.formatted.roc @@ -0,0 +1 @@ +fxFn! arg \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.result-ast new file mode 100644 index 00000000000..d928b61572f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.result-ast @@ -0,0 +1,18 @@ +SpaceAfter( + Apply( + @0-5 Var { + module_name: "", + ident: "fxFn!", + }, + [ + @5-8 Var { + module_name: "", + ident: "arg", + }, + ], + Space, + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.roc new file mode 100644 index 00000000000..e7630b8f6f9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/call_bang_no_space.expr.roc @@ -0,0 +1 @@ +fxFn!arg diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.formatted.roc new file mode 100644 index 00000000000..4f3fbc5fbbd --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.formatted.roc @@ -0,0 +1,4 @@ +launchTheNukes! = \{} -> + boom + +launchTheNukes! {} \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast new file mode 100644 index 00000000000..dc5947f6a5d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast @@ -0,0 +1,64 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @0-33, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-15 Identifier { + ident: "launchTheNukes!", + }, + @18-33 Closure( + [ + @19-21 RecordDestructure( + [], + ), + ], + @29-33 SpaceBefore( + Var { + module_name: "", + ident: "boom", + }, + [ + Newline, + ], + ), + ), + ), + ], + }, + @35-53 SpaceBefore( + Apply( + @35-50 Var { + module_name: "", + ident: "launchTheNukes!", + }, + [ + @51-53 Record( + [], + ), + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.roc new file mode 100644 index 00000000000..b36b72219c7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.roc @@ -0,0 +1,4 @@ +launchTheNukes! = \{} -> + boom + +launchTheNukes! {} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc index c0294a60f05..c3038ab3eb7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc @@ -1,8 +1,8 @@ main = a = "Foo" - Stdout.line! a + Stdout.line? a - printBar! + printBar? printBar = b = "Bar" diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast index fc98d2f84ac..5d64a16465d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast @@ -63,7 +63,7 @@ Defs { Stmt( @97-111 Apply( @97-108 TrySuffix { - target: Task, + target: Result, expr: Var { module_name: "Stdout", ident: "line", @@ -82,7 +82,7 @@ Defs { }, @141-150 SpaceBefore( TrySuffix { - target: Task, + target: Result, expr: Var { module_name: "", ident: "printBar", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc index 12e697a2942..2a60df6dfda 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc @@ -1,8 +1,8 @@ main = a = "Foo" - Stdout.line! a + Stdout.line? a - printBar! + printBar? printBar = b = "Bar" diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast index e5afcf14b48..6fe182b7bb4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast @@ -9,9 +9,9 @@ SpaceAfter( Defs( Defs { tags: [ - Index(2147483648), - Index(2147483649), - Index(2147483650), + EitherIndex(2147483648), + EitherIndex(2147483649), + EitherIndex(2147483650), ], regions: [ @11-37, @@ -19,14 +19,14 @@ SpaceAfter( @61-154, ], space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - Slice(start = 1, length = 2), + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 2 }, ], space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 3, length = 0), + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 3, length: 0 }, ], spaces: [ Newline, @@ -83,16 +83,16 @@ SpaceAfter( Defs( Defs { tags: [ - Index(2147483648), + EitherIndex(2147483648), ], regions: [ @86-119, ], space_before: [ - Slice(start = 0, length = 0), + Slice { start: 0, length: 0 }, ], space_after: [ - Slice(start = 0, length = 0), + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.formatted.roc similarity index 52% rename from crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.formatted.roc index 2c5a82c0270..e561b11c436 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.formatted.roc @@ -1,8 +1,7 @@ main = - Arg.list! {} + parseArgs? {} |> List.dropFirst 1 - |> List.mapTry Str.toU8 - |> Task.fromResult! + |> List.mapTry? Str.toU8 |> List.sum |> \total -> "Sum of numbers: $(Num.toStr total)" - |> Stdout.line! + |> Str.toUpper diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast similarity index 66% rename from crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast index fecd191cee6..5b551e9d95f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast @@ -3,7 +3,7 @@ Defs { EitherIndex(2147483648), ], regions: [ - @0-189, + @0-166, ], space_before: [ Slice { start: 0, length: 0 }, @@ -20,21 +20,21 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-189 SpaceBefore( + @11-166 SpaceBefore( BinOps( [ ( - @11-23 SpaceAfter( + @11-24 SpaceAfter( Apply( - @11-19 TrySuffix { - target: Task, + @11-20 TrySuffix { + target: Result, expr: Var { - module_name: "Arg", - ident: "list", + module_name: "", + ident: "parseArgs", }, }, [ - @21-23 Record( + @22-24 Record( [], ), ], @@ -44,17 +44,17 @@ Defs { Newline, ], ), - @28-30 Pizza, + @29-31 Pizza, ), ( - @31-47 SpaceAfter( + @32-48 SpaceAfter( Apply( - @31-45 Var { + @32-46 Var { module_name: "List", ident: "dropFirst", }, [ - @46-47 Num( + @47-48 Num( "1", ), ], @@ -64,17 +64,20 @@ Defs { Newline, ], ), - @52-54 Pizza, + @53-55 Pizza, ), ( - @55-75 SpaceAfter( + @56-77 SpaceAfter( Apply( - @55-66 Var { - module_name: "List", - ident: "mapTry", + @56-67 TrySuffix { + target: Result, + expr: Var { + module_name: "List", + ident: "mapTry", + }, }, [ - @67-75 Var { + @69-77 Var { module_name: "Str", ident: "toU8", }, @@ -85,25 +88,10 @@ Defs { Newline, ], ), - @80-82 Pizza, - ), - ( - @83-98 SpaceAfter( - TrySuffix { - target: Task, - expr: Var { - module_name: "Task", - ident: "fromResult", - }, - }, - [ - Newline, - ], - ), - @104-106 Pizza, + @82-84 Pizza, ), ( - @107-115 SpaceAfter( + @85-93 SpaceAfter( Var { module_name: "List", ident: "sum", @@ -112,30 +100,30 @@ Defs { Newline, ], ), - @120-122 Pizza, + @98-100 Pizza, ), ( - @123-169 SpaceAfter( + @101-147 SpaceAfter( Closure( [ - @124-129 Identifier { + @102-107 Identifier { ident: "total", }, ], - @133-169 Str( + @111-147 Str( Line( [ Plaintext( "Sum of numbers: ", ), Interpolated( - @152-167 Apply( - @152-161 Var { + @130-145 Apply( + @130-139 Var { module_name: "Num", ident: "toStr", }, [ - @162-167 Var { + @140-145 Var { module_name: "", ident: "total", }, @@ -151,15 +139,12 @@ Defs { Newline, ], ), - @174-176 Pizza, + @152-154 Pizza, ), ], - @177-188 TrySuffix { - target: Task, - expr: Var { - module_name: "Stdout", - ident: "line", - }, + @155-166 Var { + module_name: "Str", + ident: "toUpper", }, ), [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.roc similarity index 53% rename from crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.roc index 177bf92dfab..e53dc93f391 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.roc @@ -1,8 +1,7 @@ main = - Arg.list! {} + parseArgs? {} |> List.dropFirst 1 - |> List.mapTry Str.toU8 - |> Task.fromResult! + |> List.mapTry? Str.toU8 |> List.sum |> \total -> "Sum of numbers: $(Num.toStr total)" - |> Stdout.line! + |> Str.toUpper diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.formatted.roc new file mode 100644 index 00000000000..0951ac74b40 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.formatted.roc @@ -0,0 +1,3 @@ +{ launchTheNukes!, code } = config + +launchTheNukes! code \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast new file mode 100644 index 00000000000..8a76150ddfd --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast @@ -0,0 +1,60 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @0-32, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-23 RecordDestructure( + [ + @1-16 Identifier { + ident: "launchTheNukes!", + }, + @18-22 Identifier { + ident: "code", + }, + ], + ), + @26-32 Var { + module_name: "", + ident: "config", + }, + ), + ], + }, + @34-54 SpaceBefore( + Apply( + @34-49 Var { + module_name: "", + ident: "launchTheNukes!", + }, + [ + @50-54 Var { + module_name: "", + ident: "code", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.roc new file mode 100644 index 00000000000..b0e9999adfd --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.roc @@ -0,0 +1,3 @@ +{launchTheNukes!, code} = config + +launchTheNukes! code diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.formatted.roc new file mode 100644 index 00000000000..4507e40a7ab --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.formatted.roc @@ -0,0 +1,4 @@ +{ + answer: 42, + launchTheNukes!: \{} -> boom, +} \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.result-ast new file mode 100644 index 00000000000..cf39e09cfcd --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.result-ast @@ -0,0 +1,46 @@ +SpaceAfter( + Record( + [ + @6-16 SpaceBefore( + RequiredValue( + @6-12 "answer", + [], + @14-16 Num( + "42", + ), + ), + [ + Newline, + ], + ), + @22-50 SpaceBefore( + SpaceAfter( + RequiredValue( + @22-37 "launchTheNukes!", + [], + @39-50 Closure( + [ + @40-42 RecordDestructure( + [], + ), + ], + @46-50 Var { + module_name: "", + ident: "boom", + }, + ), + ), + [ + Newline, + ], + ), + [ + Newline, + ], + ), + ], + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.roc new file mode 100644 index 00000000000..716fb8ed3e8 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_literal_field_bang.expr.roc @@ -0,0 +1,4 @@ +{ + answer: 42, + launchTheNukes!: \{} -> boom +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast deleted file mode 100644 index f75bc40a558..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast +++ /dev/null @@ -1,13 +0,0 @@ -TrySuffix { - target: Task, - expr: TrySuffix { - target: Task, - expr: TrySuffix { - target: Task, - expr: Var { - module_name: "Stdout", - ident: "line", - }, - }, - }, -} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc deleted file mode 100644 index 1a0baacb29c..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc +++ /dev/null @@ -1 +0,0 @@ -Stdout.line!!! \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc deleted file mode 100644 index 3365e68d383..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc +++ /dev/null @@ -1,5 +0,0 @@ -main = - a! "Bar" - x = B.b! "Foo" - - c! x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast deleted file mode 100644 index 96c4edb4cd8..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast +++ /dev/null @@ -1,117 +0,0 @@ -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-49, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @12-49 SpaceBefore( - Defs( - Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - ], - regions: [ - @12-21, - @26-39, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Stmt( - @12-21 Apply( - @12-13 TrySuffix { - target: Task, - expr: Var { - module_name: "", - ident: "a", - }, - }, - [ - @16-21 Str( - PlainLine( - "Bar", - ), - ), - ], - Space, - ), - ), - Body( - @26-27 Identifier { - ident: "x", - }, - @29-39 Apply( - @29-32 TrySuffix { - target: Task, - expr: Var { - module_name: "B", - ident: "b", - }, - }, - [ - @34-39 Str( - PlainLine( - "Foo", - ), - ), - ], - Space, - ), - ), - ], - }, - @45-49 SpaceBefore( - Apply( - @45-46 TrySuffix { - target: Task, - expr: Var { - module_name: "", - ident: "c", - }, - }, - [ - @48-49 Var { - module_name: "", - ident: "x", - }, - ], - Space, - ), - [ - Newline, - Newline, - ], - ), - ), - [ - Newline, - ], - ), - ), - ], -} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc deleted file mode 100644 index 7c5720c765c..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc +++ /dev/null @@ -1,5 +0,0 @@ -main = - a! "Bar" - x= B.b! "Foo" - - c! x \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc deleted file mode 100644 index 8b65cc6d433..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -foo! (bar! baz) (blah stuff) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast deleted file mode 100644 index e62e74bd7fe..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast +++ /dev/null @@ -1,45 +0,0 @@ -Apply( - @0-3 TrySuffix { - target: Task, - expr: Var { - module_name: "", - ident: "foo", - }, - }, - [ - @9-17 ParensAround( - Apply( - @9-12 TrySuffix { - target: Task, - expr: Var { - module_name: "", - ident: "bar", - }, - }, - [ - @14-17 Var { - module_name: "", - ident: "baz", - }, - ], - Space, - ), - ), - @22-32 ParensAround( - Apply( - @22-26 Var { - module_name: "", - ident: "blah", - }, - [ - @27-32 Var { - module_name: "", - ident: "stuff", - }, - ], - Space, - ), - ), - ], - Space, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc deleted file mode 100644 index 45287194b9c..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc +++ /dev/null @@ -1 +0,0 @@ -foo! ( bar! baz) ( blah stuff) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.formatted.roc deleted file mode 100644 index 831a23396ae..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.formatted.roc +++ /dev/null @@ -1,17 +0,0 @@ -app [main] { - cli: "../basic-cli/platform/main.roc", -} - -import cli.Stdout - -main = - # is this a valid statement? - "Foo" |> A.x! - - # what about this? - "Bar" - |> B.y! - { config: "config" } - - C.z "Bar" - diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast deleted file mode 100644 index 6248fa191a9..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast +++ /dev/null @@ -1,213 +0,0 @@ -Full( - FullAst { - header: SpacesBefore { - before: [], - item: App( - AppHeader { - before_provides: [], - provides: [ - @5-9 ExposedName( - "main", - ), - ], - before_packages: [], - packages: @11-55 Collection { - items: [ - @15-52 SpaceBefore( - PackageEntry { - shorthand: "cli", - spaces_after_shorthand: [], - platform_marker: None, - package_name: @20-52 PackageName( - "../basic-cli/platform/main.roc", - ), - }, - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, - old_imports: None, - old_provides_to_new_package: None, - }, - ), - }, - defs: Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - ], - regions: [ - @57-74, - @76-220, - ], - space_before: [ - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, - ], - space_after: [ - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 2 }, - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - ModuleImport( - ModuleImport { - before_name: [], - name: @64-74 ImportedModuleName { - package: Some( - "cli", - ), - name: ModuleName( - "Stdout", - ), - }, - params: None, - alias: None, - exposed: None, - }, - ), - Body( - @76-80 Identifier { - ident: "main", - }, - @120-220 SpaceBefore( - Defs( - Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - ], - regions: [ - @120-133, - @162-205, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 3 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 3, length: 0 }, - ], - spaces: [ - Newline, - Newline, - LineComment( - " what about this?", - ), - ], - type_defs: [], - value_defs: [ - Stmt( - @120-133 BinOps( - [ - ( - @120-125 Str( - PlainLine( - "Foo", - ), - ), - @126-128 Pizza, - ), - ], - @129-132 TrySuffix { - target: Task, - expr: Var { - module_name: "A", - ident: "x", - }, - }, - ), - ), - Stmt( - @162-205 BinOps( - [ - ( - @162-167 Str( - PlainLine( - "Bar", - ), - ), - @168-170 Pizza, - ), - ], - @171-205 Apply( - @171-174 TrySuffix { - target: Task, - expr: Var { - module_name: "B", - ident: "y", - }, - }, - [ - @185-205 SpaceBefore( - Record( - [ - @187-203 RequiredValue( - @187-193 "config", - [], - @195-203 Str( - PlainLine( - "config", - ), - ), - ), - ], - ), - [ - Newline, - ], - ), - ], - Space, - ), - ), - ), - ], - }, - @211-220 SpaceBefore( - Apply( - @211-214 Var { - module_name: "C", - ident: "z", - }, - [ - @215-220 Str( - PlainLine( - "Bar", - ), - ), - ], - Space, - ), - [ - Newline, - Newline, - ], - ), - ), - [ - Newline, - LineComment( - " is this a valid statement?", - ), - ], - ), - ), - ], - }, - }, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc deleted file mode 100644 index 7cd91a21ffe..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc +++ /dev/null @@ -1,16 +0,0 @@ -app [main] { - cli: "../basic-cli/platform/main.roc", -} - -import cli.Stdout - -main = - # is this a valid statement? - "Foo" |> A.x! - - # what about this? - "Bar" |> B.y! - { config: "config" } - - C.z "Bar" - diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc deleted file mode 100644 index 3c148a87750..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc +++ /dev/null @@ -1,9 +0,0 @@ -app [main] { - cli: platform "", -} - -main = - "jq --version" - |> Cmd.new - |> Cmd.status - |> Task.mapErr! UnableToCheckJQVersion diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast deleted file mode 100644 index 37736fb0814..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast +++ /dev/null @@ -1,134 +0,0 @@ -Full( - FullAst { - header: SpacesBefore { - before: [], - item: App( - AppHeader { - before_provides: [ - Newline, - ], - provides: [ - @74-78 ExposedName( - "main", - ), - ], - before_packages: [ - Newline, - ], - packages: @6-44 Collection { - items: [ - @30-37 SpaceBefore( - PackageEntry { - shorthand: "cli", - spaces_after_shorthand: [], - platform_marker: Some( - [], - ), - package_name: @35-37 PackageName( - "", - ), - }, - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, - old_imports: None, - old_provides_to_new_package: None, - }, - ), - }, - defs: Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @88-202, - ], - space_before: [ - Slice { start: 0, length: 2 }, - ], - space_after: [ - Slice { start: 2, length: 1 }, - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @88-92 Identifier { - ident: "main", - }, - @100-202 SpaceBefore( - BinOps( - [ - ( - @100-114 SpaceAfter( - Str( - PlainLine( - "jq --version", - ), - ), - [ - Newline, - ], - ), - @123-125 Pizza, - ), - ( - @126-133 SpaceAfter( - Var { - module_name: "Cmd", - ident: "new", - }, - [ - Newline, - ], - ), - @142-144 Pizza, - ), - ( - @145-155 SpaceAfter( - Var { - module_name: "Cmd", - ident: "status", - }, - [ - Newline, - ], - ), - @164-166 Pizza, - ), - ], - @167-202 Apply( - @167-178 TrySuffix { - target: Task, - expr: Var { - module_name: "Task", - ident: "mapErr", - }, - }, - [ - @180-202 Tag( - "UnableToCheckJQVersion", - ), - ], - Space, - ), - ), - [ - Newline, - ], - ), - ), - ], - }, - }, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc deleted file mode 100644 index f9605f9d622..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc +++ /dev/null @@ -1,12 +0,0 @@ -app "" - packages { - cli: "", - } - imports [] - provides [main] to cli - -main = - "jq --version" - |> Cmd.new - |> Cmd.status - |> Task.mapErr! UnableToCheckJQVersion diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.formatted.roc new file mode 100644 index 00000000000..d580b0e18de --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.formatted.roc @@ -0,0 +1,3 @@ +(Config launchTheNukes! code) = cfg + +launchTheNukes! code \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast new file mode 100644 index 00000000000..c544346d5d5 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast @@ -0,0 +1,63 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @0-33, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-27 Apply( + @0-6 Tag( + "Config", + ), + [ + @7-22 Identifier { + ident: "launchTheNukes!", + }, + @23-27 Identifier { + ident: "code", + }, + ], + ), + @30-33 Var { + module_name: "", + ident: "cfg", + }, + ), + ], + }, + @35-55 SpaceBefore( + Apply( + @35-50 Var { + module_name: "", + ident: "launchTheNukes!", + }, + [ + @51-55 Var { + module_name: "", + ident: "code", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.roc new file mode 100644 index 00000000000..4e33e8de2d7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.roc @@ -0,0 +1,3 @@ +Config launchTheNukes! code = cfg + +launchTheNukes! code diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.formatted.roc new file mode 100644 index 00000000000..d580b0e18de --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.formatted.roc @@ -0,0 +1,3 @@ +(Config launchTheNukes! code) = cfg + +launchTheNukes! code \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast new file mode 100644 index 00000000000..303e59b8ad0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast @@ -0,0 +1,63 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @0-32, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-26 Apply( + @0-6 Tag( + "Config", + ), + [ + @7-22 Identifier { + ident: "launchTheNukes!", + }, + @22-26 Identifier { + ident: "code", + }, + ], + ), + @29-32 Var { + module_name: "", + ident: "cfg", + }, + ), + ], + }, + @34-54 SpaceBefore( + Apply( + @34-49 Var { + module_name: "", + ident: "launchTheNukes!", + }, + [ + @50-54 Var { + module_name: "", + ident: "code", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.roc new file mode 100644 index 00000000000..8fbcd60302a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.roc @@ -0,0 +1,3 @@ +Config launchTheNukes!code = cfg + +launchTheNukes! code diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.formatted.roc new file mode 100644 index 00000000000..8d08b0fe774 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.formatted.roc @@ -0,0 +1,3 @@ +(launchTheNukes!, code) = config + +launchTheNukes! code \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast new file mode 100644 index 00000000000..f98f394b2d3 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast @@ -0,0 +1,60 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @0-32, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-23 Tuple( + [ + @1-16 Identifier { + ident: "launchTheNukes!", + }, + @18-22 Identifier { + ident: "code", + }, + ], + ), + @26-32 Var { + module_name: "", + ident: "config", + }, + ), + ], + }, + @34-54 SpaceBefore( + Apply( + @34-49 Var { + module_name: "", + ident: "launchTheNukes!", + }, + [ + @50-54 Var { + module_name: "", + ident: "code", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.roc new file mode 100644 index 00000000000..dd7438380d2 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.roc @@ -0,0 +1,3 @@ +(launchTheNukes!, code) = config + +launchTheNukes! code diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index ac2325f72a8..34940febb87 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -200,6 +200,7 @@ mod test_snapshots { fail/error_inline_alias_argument_uppercase.expr, fail/error_inline_alias_not_an_alias.expr, fail/error_inline_alias_qualified.expr, + fail/exposed_type_bang.header, fail/expr_to_pattern_fail.expr, fail/expression_indentation_end.expr, fail/if_guard_without_condition.expr, @@ -228,6 +229,7 @@ mod test_snapshots { fail/pattern_in_parens_end_comma.expr, fail/pattern_in_parens_indent_open.expr, fail/pattern_in_parens_open.expr, + fail/record_destructure_field_bang_no_space.expr, fail/record_type_end.expr, fail/record_type_keyword_field_name.expr, fail/record_type_missing_comma.expr, @@ -293,6 +295,8 @@ mod test_snapshots { pass/basic_tag.expr, pass/basic_tuple.expr, pass/basic_var.expr, + pass/call_bang.expr, + pass/call_bang_no_space.expr, pass/closure_in_binop_with_spaces.expr, pass/closure_with_underscores.expr, pass/comma_prefixed_indented_record.expr, @@ -310,6 +314,7 @@ mod test_snapshots { pass/dbg.expr, pass/dbg_stmt.expr, pass/dbg_stmt_multiline.expr, + pass/def_bang.expr, pass/defs_suffixed_middle_extra_indents.moduledefs, pass/destructure_tag_assignment.expr, pass/docs.expr, @@ -446,8 +451,8 @@ mod test_snapshots { pass/pattern_as_list_rest.expr, pass/pattern_as_spaces.expr, pass/pattern_with_space_in_parens.expr, // https://github.com/roc-lang/roc/issues/929 - pass/pizza_bang.moduledefs, pass/pizza_dbg.expr, + pass/pizza_question.moduledefs, pass/plus_if.expr, pass/plus_when.expr, pass/pos_inf_float.expr, @@ -460,7 +465,9 @@ mod test_snapshots { pass/record_builder.expr, pass/record_builder_ignored_fields.expr, pass/record_destructure_def.expr, + pass/record_destructure_field_bang.expr, pass/record_func_type_decl.expr, + pass/record_literal_field_bang.expr, pass/record_type_with_function.expr, pass/record_update.expr, pass/record_updater_literal_apply.expr, @@ -484,16 +491,13 @@ mod test_snapshots { pass/string_without_escape.expr, pass/sub_var_with_spaces.expr, pass/sub_with_spaces.expr, - pass/suffixed_bang.expr, - pass/suffixed_bang_multiple_defs.moduledefs, - pass/suffixed_bang_nested.expr, - pass/suffixed_bang_one_def.full, - pass/suffixed_bang_optional_last.full, pass/suffixed_question.expr, pass/suffixed_question_multiple_defs.moduledefs, pass/suffixed_question_nested.expr, pass/suffixed_question_one_def.full, pass/suffixed_question_optional_last.full, + pass/tag_destructure_bang.expr, + pass/tag_destructure_bang_no_space.expr, pass/tag_pattern.expr, pass/ten_times_eleven.expr, pass/three_arg_closure.expr, @@ -503,6 +507,7 @@ mod test_snapshots { pass/tuple_access_after_ident.expr, pass/tuple_access_after_record.expr, pass/tuple_accessor_function.expr, + pass/tuple_destructure_bang.expr, pass/tuple_type.expr, pass/tuple_type_ext.expr, pass/two_arg_closure.expr, diff --git a/examples/cli/effects-platform/Effect.roc b/examples/cli/effects-platform/Effect.roc index ba79d4fce54..8b1ab6d69fa 100644 --- a/examples/cli/effects-platform/Effect.roc +++ b/examples/cli/effects-platform/Effect.roc @@ -1,7 +1,7 @@ hosted Effect - exposes [putLine, getLine] + exposes [putLine!, getLine!] imports [] -putLine : Str => {} +putLine! : Str => {} -getLine : {} => Str +getLine! : {} => Str diff --git a/examples/cli/effects.roc b/examples/cli/effects.roc index 0a05c04055e..c792cee9a92 100644 --- a/examples/cli/effects.roc +++ b/examples/cli/effects.roc @@ -4,6 +4,6 @@ import pf.Effect main : {} => {} main = \{} -> - line = Effect.getLine {} - _ = Effect.putLine "You entered: $(line)" - Effect.putLine "It is known" + line = Effect.getLine! {} + _ = Effect.putLine! "You entered: $(line)" + Effect.putLine! "It is known" diff --git a/examples/inspect-logging.roc b/examples/inspect-logging.roc index 0b86c1028f5..0d5832b4581 100644 --- a/examples/inspect-logging.roc +++ b/examples/inspect-logging.roc @@ -8,28 +8,28 @@ import Community main = Community.empty - |> Community.addPerson { - firstName: "John", - lastName: "Smith", - age: 27, - hasBeard: Bool.true, - favoriteColor: Blue, - } - |> Community.addPerson { - firstName: "Debby", - lastName: "Johnson", - age: 47, - hasBeard: Bool.false, - favoriteColor: Green, - } - |> Community.addPerson { - firstName: "Jane", - lastName: "Doe", - age: 33, - hasBeard: Bool.false, - favoriteColor: RGB (255, 255, 0), - } - |> Community.addFriend 0 2 - |> Community.addFriend 1 2 - |> Inspect.toStr - |> Stdout.line! + |> Community.addPerson { + firstName: "John", + lastName: "Smith", + age: 27, + hasBeard: Bool.true, + favoriteColor: Blue, + } + |> Community.addPerson { + firstName: "Debby", + lastName: "Johnson", + age: 47, + hasBeard: Bool.false, + favoriteColor: Green, + } + |> Community.addPerson { + firstName: "Jane", + lastName: "Doe", + age: 33, + hasBeard: Bool.false, + favoriteColor: RGB (255, 255, 0), + } + |> Community.addFriend 0 2 + |> Community.addFriend 1 2 + |> Inspect.toStr + |> Stdout.line! From 56cdc749af98e8deaeca3b723031e98bd477c724 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 14 Oct 2024 21:18:32 -0300 Subject: [PATCH 16/73] Do not attempt to parse `!` suffix --- crates/compiler/parse/src/expr.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index d656bf09bfa..d5ca5698357 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -169,9 +169,6 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr ) ) ), - map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix( - TryTarget::Task - )), map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix( TryTarget::Result )), From aeeaab4b99db9604d5d1c3a1af6aeacb4b361901 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 14 Oct 2024 21:39:45 -0300 Subject: [PATCH 17/73] Desugar idents ending in `!` to TrySuffix --- crates/compiler/can/src/desugar.rs | 20 ++++++++++++++++++- ...ffixed_tests__apply_argument_multiple.snap | 12 +++++------ ...suffixed_tests__apply_argument_single.snap | 6 +++--- ...ed__suffixed_tests__bang_in_pipe_root.snap | 6 +++--- ...fixed__suffixed_tests__closure_simple.snap | 16 +++++++-------- ...sts__last_stmt_not_top_level_suffixed.snap | 6 +++--- ...xed__suffixed_tests__multi_defs_stmts.snap | 10 +++++----- ...uffixed__suffixed_tests__simple_pizza.snap | 16 +++++++-------- crates/compiler/parse/src/ast.rs | 8 +++++++- 9 files changed, 62 insertions(+), 38 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index bd9dfc485f4..108cbe5b5dd 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -396,7 +396,6 @@ pub fn desugar_expr<'a>( | NonBase10Int { .. } | SingleQuote(_) | AccessorFunction(_) - | Var { .. } | Underscore { .. } | MalformedIdent(_, _) | MalformedClosure @@ -410,6 +409,25 @@ pub fn desugar_expr<'a>( | Crash | Try => loc_expr, + Var { module_name, ident } => { + // TODO remove when purity inference fully replaces Task + // [purity-inference] TODO: only in Task mode + if ident.ends_with('!') { + env.arena.alloc(Loc::at( + loc_expr.region, + TrySuffix { + expr: env.arena.alloc(Var { + module_name, + ident: ident.trim_end_matches('!'), + }), + target: roc_parse::ast::TryTarget::Task, + }, + )) + } else { + loc_expr + } + } + Str(str_literal) => match str_literal { StrLiteral::PlainLine(_) => loc_expr, StrLiteral::Line(segments) => { diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap index 29b6f33f3ae..fa9b21ad4b7 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap @@ -30,13 +30,13 @@ Defs { ident: "await", }, [ - @17-18 Var { + @17-19 Var { module_name: "", ident: "a", }, @15-22 Closure( [ - @17-18 Identifier { + @17-19 Identifier { ident: "#!0_arg", }, ], @@ -46,13 +46,13 @@ Defs { ident: "await", }, [ - @20-21 Var { + @20-22 Var { module_name: "", ident: "x", }, @15-22 Closure( [ - @20-21 Identifier { + @20-22 Identifier { ident: "#!1_arg", }, ], @@ -83,11 +83,11 @@ Defs { ident: "b", }, [ - @17-18 Var { + @17-19 Var { module_name: "", ident: "#!0_arg", }, - @20-21 Var { + @20-22 Var { module_name: "", ident: "#!1_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap index 63b7cf74b30..144d413c178 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap @@ -30,13 +30,13 @@ Defs { ident: "await", }, [ - @17-18 Var { + @17-19 Var { module_name: "", ident: "a", }, @15-19 Closure( [ - @17-18 Identifier { + @17-19 Identifier { ident: "#!0_arg", }, ], @@ -67,7 +67,7 @@ Defs { ident: "b", }, [ - @17-18 Var { + @17-19 Var { module_name: "", ident: "#!0_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap index 5f52ead8655..38ecebcfd4d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap @@ -30,13 +30,13 @@ Defs { ident: "await", }, [ - @15-16 Var { + @15-17 Var { module_name: "", ident: "a", }, @15-22 Closure( [ - @15-16 Identifier { + @15-17 Identifier { ident: "#!0_arg", }, ], @@ -67,7 +67,7 @@ Defs { ident: "b", }, [ - @15-16 Var { + @15-17 Var { module_name: "", ident: "#!0_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index dce9c0f3f12..32090d76640 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -51,19 +51,19 @@ Defs { ident: "msg", }, ], - @31-42 Apply( - @31-42 Var { + @31-43 Apply( + @31-43 Var { module_name: "Task", ident: "await", }, [ - @31-42 Defs( + @31-43 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @31-42, + @31-43, ], space_before: [ Slice { start: 0, length: 0 }, @@ -93,8 +93,8 @@ Defs { body_pattern: @31-43 Identifier { ident: "#!0_stmt", }, - body_expr: @31-42 Apply( - @31-42 Var { + body_expr: @31-43 Apply( + @31-43 Var { module_name: "", ident: "line", }, @@ -111,12 +111,12 @@ Defs { }, ], }, - @31-42 Var { + @31-43 Var { module_name: "", ident: "#!0_stmt", }, ), - @31-42 Closure( + @31-43 Closure( [ @31-43 Underscore( "#!stmt", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap index aa775291e93..5f4bd8be7f5 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap @@ -57,13 +57,13 @@ Defs { ident: "await", }, [ - @24-25 Var { + @24-26 Var { module_name: "", ident: "b", }, @11-26 Closure( [ - @24-25 Identifier { + @24-26 Identifier { ident: "#!0_arg", }, ], @@ -73,7 +73,7 @@ Defs { ident: "a", }, [ - @24-25 Var { + @24-26 Var { module_name: "", ident: "#!0_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index 8e98fb84a8d..dd46fbbf6c4 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -94,14 +94,14 @@ Defs { "#!stmt", ), ], - @33-55 Apply( - @33-55 Var { + @33-56 Apply( + @33-56 Var { module_name: "Task", ident: "await", }, [ - @33-55 Apply( - @33-55 Var { + @33-56 Apply( + @33-56 Var { module_name: "Stdout", ident: "line", }, @@ -116,7 +116,7 @@ Defs { Pizza, ), ), - @33-55 Closure( + @33-56 Closure( [ @28-30 RecordDestructure( [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 1d5573d9e00..3a33e1d5d6a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -24,19 +24,19 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-56 Apply( - @11-56 Var { + @11-57 Apply( + @11-57 Var { module_name: "Task", ident: "await", }, [ - @11-56 Defs( + @11-57 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @11-56, + @11-57, ], space_before: [ Slice { start: 0, length: 0 }, @@ -66,8 +66,8 @@ Defs { body_pattern: @11-57 Identifier { ident: "#!0_stmt", }, - body_expr: @11-56 Apply( - @11-56 Var { + body_expr: @11-57 Apply( + @11-57 Var { module_name: "", ident: "line", }, @@ -101,12 +101,12 @@ Defs { }, ], }, - @11-56 Var { + @11-57 Var { module_name: "", ident: "#!0_stmt", }, ), - @11-56 Closure( + @11-57 Closure( [ @11-57 Underscore( "#!stmt", diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 37818fadcb6..8635eb95585 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -591,7 +591,13 @@ pub fn is_top_level_suffixed(expr: &Expr) -> bool { pub fn is_expr_suffixed(expr: &Expr) -> bool { match expr { // expression without arguments, `read!` - Expr::Var { .. } => false, + Expr::Var { + module_name: _, + ident, + } => { + // TODO remove when purity inference fully replaces Task + ident.ends_with('!') + } Expr::TrySuffix { .. } => true, From 8bde68cc5ef21d88b8082326598e1d4a09ebbd6c Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 14 Oct 2024 21:39:59 -0300 Subject: [PATCH 18/73] Restore parsing `!` suffix This reverts commit 52896d9fa65141df832989b326f526cbedf67341. We actually still need this for when `!` follows a non-ident expr --- crates/compiler/parse/src/expr.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index d5ca5698357..d656bf09bfa 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -169,6 +169,9 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr ) ) ), + map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix( + TryTarget::Task + )), map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix( TryTarget::Result )), From fd3fb16f7a459d390a38d2f93313a6689748b344 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 14 Oct 2024 21:45:01 -0300 Subject: [PATCH 19/73] Add TODO to remove TryTarget::Task --- crates/compiler/can/src/desugar.rs | 1 - crates/compiler/parse/src/ast.rs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 108cbe5b5dd..b37a1bd4a94 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -410,7 +410,6 @@ pub fn desugar_expr<'a>( | Try => loc_expr, Var { module_name, ident } => { - // TODO remove when purity inference fully replaces Task // [purity-inference] TODO: only in Task mode if ident.ends_with('!') { env.arena.alloc(Loc::at( diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 8635eb95585..e6b82bfcb96 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -394,6 +394,7 @@ pub enum StrLiteral<'a> { /// Values that can be tried, extracting success values or "returning early" on failure #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TryTarget { + // TODO: Remove when purity inference replaces Task fully /// Tasks suffixed with ! are `Task.await`ed Task, /// Results suffixed with ? are `Result.try`ed @@ -594,10 +595,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool { Expr::Var { module_name: _, ident, - } => { - // TODO remove when purity inference fully replaces Task - ident.ends_with('!') - } + } => ident.ends_with('!'), Expr::TrySuffix { .. } => true, From 01c94050c81da1e28ac2d2442664aab48b3a9d7a Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 14 Oct 2024 22:16:04 -0300 Subject: [PATCH 20/73] Detect fx mode based on hosted module --- crates/compiler/can/src/desugar.rs | 8 +++--- crates/compiler/can/src/env.rs | 10 ++++++++ crates/compiler/can/src/module.rs | 4 ++- crates/compiler/can/tests/helpers/mod.rs | 1 + crates/compiler/can/tests/test_suffixed.rs | 3 ++- crates/compiler/load/tests/helpers/mod.rs | 1 + crates/compiler/load_internal/src/file.rs | 29 +++++++++++++++++++++- crates/compiler/parse/src/header.rs | 6 ++++- examples/cli/effects.roc | 2 +- 9 files changed, 54 insertions(+), 10 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index b37a1bd4a94..06696a6b921 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -1,6 +1,6 @@ #![allow(clippy::manual_map)] -use crate::env::Env; +use crate::env::{Env, FxMode}; use crate::scope::Scope; use crate::suffixed::{apply_try_function, unwrap_suffixed_expression, EUnwrapped}; use bumpalo::collections::Vec; @@ -207,8 +207,7 @@ fn desugar_value_def<'a>( // _ : {} // _ = stmt_expr! - if !is_expr_suffixed(&stmt_expr.value) { - // [purity-inference] TODO: this is ok in purity inference mode + if env.fx_mode == FxMode::Task && !is_expr_suffixed(&stmt_expr.value) { env.problems.push(Problem::StmtAfterExpr(stmt_expr.region)); return ValueDef::StmtAfterExpr; @@ -410,8 +409,7 @@ pub fn desugar_expr<'a>( | Try => loc_expr, Var { module_name, ident } => { - // [purity-inference] TODO: only in Task mode - if ident.ends_with('!') { + if env.fx_mode == FxMode::Task && ident.ends_with('!') { env.arena.alloc(Loc::at( loc_expr.region, TrySuffix { diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index 3b996648c9d..664991ce3dd 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -45,6 +45,8 @@ pub struct Env<'a> { pub opt_shorthand: Option<&'a str>, + pub fx_mode: FxMode, + pub src: &'a str, /// Lazily calculated line info. This data is only needed if the code contains calls to `dbg`, @@ -62,6 +64,7 @@ impl<'a> Env<'a> { dep_idents: &'a IdentIdsByModule, qualified_module_ids: &'a PackageModuleIds<'a>, opt_shorthand: Option<&'a str>, + fx_mode: FxMode, ) -> Env<'a> { Env { arena, @@ -79,6 +82,7 @@ impl<'a> Env<'a> { home_params_record: None, opt_shorthand, line_info: arena.alloc(None), + fx_mode, } } @@ -237,3 +241,9 @@ impl<'a> Env<'a> { self.line_info.as_ref().unwrap() } } + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum FxMode { + PurityInference, + Task, +} diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index c2fc06aaa26..610f13192c7 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -4,7 +4,7 @@ use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedI use crate::annotation::{canonicalize_annotation, AnnotationFor}; use crate::def::{canonicalize_defs, report_unused_imports, Def}; use crate::desugar::desugar_record_destructures; -use crate::env::Env; +use crate::env::{Env, FxMode}; use crate::expr::{ ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives, }; @@ -226,6 +226,7 @@ pub fn canonicalize_module_defs<'a>( symbols_from_requires: &[(Loc, Loc>)], var_store: &mut VarStore, opt_shorthand: Option<&'a str>, + fx_mode: FxMode, ) -> ModuleOutput { let mut can_exposed_imports = MutMap::default(); @@ -247,6 +248,7 @@ pub fn canonicalize_module_defs<'a>( dep_idents, qualified_module_ids, opt_shorthand, + fx_mode, ); for (name, alias) in aliases.into_iter() { diff --git a/crates/compiler/can/tests/helpers/mod.rs b/crates/compiler/can/tests/helpers/mod.rs index 4fe40c935f7..7942c864a69 100644 --- a/crates/compiler/can/tests/helpers/mod.rs +++ b/crates/compiler/can/tests/helpers/mod.rs @@ -62,6 +62,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut &dep_idents, &qualified_module_ids, None, + roc_can::env::FxMode::PurityInference, ); // Desugar operators (convert them to Apply calls, taking into account diff --git a/crates/compiler/can/tests/test_suffixed.rs b/crates/compiler/can/tests/test_suffixed.rs index c8db51da67c..2a30c718d08 100644 --- a/crates/compiler/can/tests/test_suffixed.rs +++ b/crates/compiler/can/tests/test_suffixed.rs @@ -6,7 +6,7 @@ mod suffixed_tests { use bumpalo::Bump; use insta::assert_snapshot; use roc_can::desugar::desugar_defs_node_values; - use roc_can::env::Env; + use roc_can::env::{Env, FxMode}; use roc_can::scope::Scope; use roc_module::symbol::{IdentIds, ModuleIds, PackageModuleIds}; use roc_parse::test_helpers::parse_defs_with; @@ -34,6 +34,7 @@ mod suffixed_tests { &dep_idents, &qualified_module_ids, None, + FxMode::Task, ); let mut defs = parse_defs_with(arena, indoc!($src)).unwrap(); diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index 9a701774ab8..a55a4bd5362 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -177,6 +177,7 @@ pub fn can_expr_with<'a>( &dep_idents, &module_ids, None, + roc_can::env::FxMode::PurityInference, ); // Desugar operators (convert them to Apply calls, taking into account diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index ab52bbb55aa..775be12ad60 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -15,6 +15,7 @@ use parking_lot::Mutex; use roc_builtins::roc::module_source; use roc_can::abilities::{AbilitiesStore, PendingAbilitiesStore, ResolvedImpl}; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints, TypeOrVar}; +use roc_can::env::FxMode; use roc_can::expr::{DbgLookup, Declarations, ExpectLookup, PendingDerives}; use roc_can::module::{ canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module, @@ -318,6 +319,7 @@ fn start_phase<'a>( exposed_module_ids: state.exposed_modules, exec_mode: state.exec_mode, imported_module_params, + fx_mode: state.fx_mode, } } @@ -710,6 +712,7 @@ struct State<'a> { pub platform_path: PlatformPath<'a>, pub target: Target, pub(self) function_kind: FunctionKind, + pub fx_mode: FxMode, /// Note: only packages and platforms actually expose any modules; /// for all others, this will be empty. @@ -797,6 +800,7 @@ impl<'a> State<'a> { cache_dir, target, function_kind, + fx_mode: FxMode::Task, platform_data: None, platform_path: PlatformPath::NotSpecified, module_cache: ModuleCache::default(), @@ -900,6 +904,7 @@ enum BuildTask<'a> { skip_constraint_gen: bool, exec_mode: ExecutionMode, imported_module_params: VecMap, + fx_mode: FxMode, }, Solve { module: Module, @@ -2271,11 +2276,29 @@ fn update<'a>( state.platform_path = PlatformPath::RootIsModule; } } - Hosted { .. } => { + Hosted { exposes, .. } => { if header.is_root_module { debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified)); state.platform_path = PlatformPath::RootIsHosted; } + + if exposes + .iter() + .any(|exposed| exposed.value.is_effectful_fn()) + { + if exposes + .iter() + .any(|exposed| !exposed.value.is_effectful_fn()) + { + // Temporary error message while we transition platforms + return Err(LoadingProblem::FormattedReport( + "Hosted module must not mix effectful and Task functions" + .to_string(), + )); + } + + state.fx_mode = FxMode::PurityInference; + } } } @@ -5050,6 +5073,7 @@ fn canonicalize_and_constrain<'a>( exposed_module_ids: &[ModuleId], exec_mode: ExecutionMode, imported_module_params: VecMap, + fx_mode: FxMode, ) -> CanAndCon { let canonicalize_start = Instant::now(); @@ -5093,6 +5117,7 @@ fn canonicalize_and_constrain<'a>( &symbols_from_requires, &mut var_store, opt_shorthand, + fx_mode, ); let mut types = Types::new(); @@ -6276,6 +6301,7 @@ fn run_task<'a>( exposed_module_ids, exec_mode, imported_module_params, + fx_mode, } => { let can_and_con = canonicalize_and_constrain( arena, @@ -6289,6 +6315,7 @@ fn run_task<'a>( exposed_module_ids, exec_mode, imported_module_params, + fx_mode, ); Ok(Msg::CanonicalizedAndConstrained(can_and_con)) diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 34be62a6431..f7bb0ee428e 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -1132,6 +1132,10 @@ impl<'a> ExposedName<'a> { pub fn as_str(&'a self) -> &'a str { self.0 } + + pub fn is_effectful_fn(&self) -> bool { + self.0.ends_with('!') + } } pub trait Keyword: Copy + Clone + Debug { @@ -1255,7 +1259,7 @@ pub struct PlatformHeader<'a> { #[derive(Copy, Clone, Debug, PartialEq)] pub enum ImportsEntry<'a> { - /// e.g. `Hello` or `Hello exposing [hello]` see roc-lang.org/examples/MultipleRocFiles/README.html + /// e.g. `Hello` or `Hello exposing [hello]` see roc-lang.org/examples/MultipleRocFiles/README.html Module( ModuleName<'a>, Collection<'a, Loc>>>, diff --git a/examples/cli/effects.roc b/examples/cli/effects.roc index c792cee9a92..4fabe4d57c0 100644 --- a/examples/cli/effects.roc +++ b/examples/cli/effects.roc @@ -5,5 +5,5 @@ import pf.Effect main : {} => {} main = \{} -> line = Effect.getLine! {} - _ = Effect.putLine! "You entered: $(line)" + Effect.putLine! "You entered: $(line)" Effect.putLine! "It is known" From 460fa693fddb19eb8ae390bdc94de9b338d7ad1f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 14 Oct 2024 23:01:26 -0300 Subject: [PATCH 21/73] Desugar stmt expr before checking whether it's suffixed --- crates/compiler/can/src/desugar.rs | 6 ++++-- crates/compiler/parse/src/ast.rs | 5 +---- examples/cli/effects.roc | 7 +++++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 06696a6b921..20b1afaa9c1 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -207,7 +207,9 @@ fn desugar_value_def<'a>( // _ : {} // _ = stmt_expr! - if env.fx_mode == FxMode::Task && !is_expr_suffixed(&stmt_expr.value) { + let desugared_expr = desugar_expr(env, scope, stmt_expr); + + if env.fx_mode == FxMode::Task && !is_expr_suffixed(&desugared_expr.value) { env.problems.push(Problem::StmtAfterExpr(stmt_expr.region)); return ValueDef::StmtAfterExpr; @@ -229,7 +231,7 @@ fn desugar_value_def<'a>( )), lines_between: &[], body_pattern: new_pat, - body_expr: desugar_expr(env, scope, stmt_expr), + body_expr: desugared_expr, } } } diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index e6b82bfcb96..34d83e84b71 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -592,10 +592,7 @@ pub fn is_top_level_suffixed(expr: &Expr) -> bool { pub fn is_expr_suffixed(expr: &Expr) -> bool { match expr { // expression without arguments, `read!` - Expr::Var { - module_name: _, - ident, - } => ident.ends_with('!'), + Expr::Var { .. } => false, Expr::TrySuffix { .. } => true, diff --git a/examples/cli/effects.roc b/examples/cli/effects.roc index 4fabe4d57c0..9d8c026cf6a 100644 --- a/examples/cli/effects.roc +++ b/examples/cli/effects.roc @@ -5,5 +5,12 @@ import pf.Effect main : {} => {} main = \{} -> line = Effect.getLine! {} + + if line == "secret" then + Effect.putLine! "You found the secret" + Effect.putLine! "Congratulations!" + else + {} + Effect.putLine! "You entered: $(line)" Effect.putLine! "It is known" From 6e6382ab23f2aa27da3f527532ca53a64792e579 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 30 Oct 2024 19:26:11 -0300 Subject: [PATCH 22/73] Canonicalize and constrain statement expr in purity inference mode --- crates/compiler/can/src/builtins.rs | 2 + crates/compiler/can/src/copy.rs | 9 +- crates/compiler/can/src/debug/pretty_print.rs | 8 +- crates/compiler/can/src/def.rs | 159 ++++++++++++------ crates/compiler/can/src/desugar.rs | 8 +- crates/compiler/can/src/effect_module.rs | 3 +- crates/compiler/can/src/expr.rs | 5 +- crates/compiler/can/src/module.rs | 3 +- crates/compiler/constrain/src/expr.rs | 51 +++++- crates/compiler/derive/src/lib.rs | 2 + .../compiler/lower_params/src/type_error.rs | 1 + crates/compiler/mono/src/ir.rs | 5 + crates/compiler/types/src/types.rs | 1 + crates/reporting/src/error/type.rs | 1 + 14 files changed, 197 insertions(+), 61 deletions(-) diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index ae6b41cfdbe..abc4977925a 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -418,6 +418,7 @@ fn defn( expr_var: var_store.fresh(), pattern_vars: SendMap::default(), annotation: None, + kind: crate::def::DefKind::Let, } } @@ -548,6 +549,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) expr_var: record_var, pattern_vars: SendMap::default(), annotation: None, + kind: crate::def::DefKind::Let, }; let body = LetNonRec(Box::new(def), Box::new(no_region(cont))); diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 8fd743c0a76..3e1214ee283 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -1,5 +1,5 @@ use crate::{ - def::Def, + def::{Def, DefKind}, expr::{ ClosureData, Expr, Field, OpaqueWrapFunctionData, StructAccessorData, WhenBranchPattern, }, @@ -378,6 +378,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr expr_var, pattern_vars, annotation, + kind, }| Def { loc_pattern: loc_pattern.map(|p| deep_copy_pattern_help(env, copied, p)), loc_expr: loc_expr.map(|e| go_help!(e)), @@ -386,6 +387,10 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr // Annotation should only be used in constraining, don't clone before // constraining :) annotation: annotation.clone(), + kind: match kind { + DefKind::Let => DefKind::Let, + DefKind::Stmt(v) => DefKind::Stmt(sub!(*v)), + }, }, ) .collect(), @@ -399,6 +404,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr expr_var, pattern_vars, annotation, + kind, } = &**def; let def = Def { loc_pattern: loc_pattern.map(|p| deep_copy_pattern_help(env, copied, p)), @@ -408,6 +414,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr // Annotation should only be used in constraining, don't clone before // constraining :) annotation: annotation.clone(), + kind: *kind, }; LetNonRec(Box::new(def), Box::new(body.map(|e| go_help!(e)))) } diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index 00ea23b07e4..77088f3e261 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -1,6 +1,6 @@ //! Pretty-prints the canonical AST back to check our work - do things look reasonable? -use crate::def::Def; +use crate::def::{Def, DefKind}; use crate::expr::Expr::{self, *}; use crate::expr::{ ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, @@ -107,9 +107,13 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> { expr_var: _, pattern_vars: _, annotation: _, + kind, } = d; - def_help(c, f, &loc_pattern.value, &loc_expr.value) + match kind { + DefKind::Let => def_help(c, f, &loc_pattern.value, &loc_expr.value), + DefKind::Stmt(_) => expr(c, EPrec::Free, f, &loc_expr.value), + } } fn def_symbol_help<'a>( diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index f07585e63e4..1de569a62e8 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -10,6 +10,7 @@ use crate::annotation::IntroducedVariables; use crate::annotation::OwnedNamedOrAble; use crate::derive; use crate::env::Env; +use crate::env::FxMode; use crate::expr::canonicalize_record; use crate::expr::get_lookup_symbols; use crate::expr::AnnotatedMark; @@ -69,6 +70,7 @@ pub struct Def { pub expr_var: Variable, pub pattern_vars: SendMap, pub annotation: Option, + pub kind: DefKind, } impl Def { @@ -89,6 +91,23 @@ impl Def { } } +#[derive(Clone, Copy, Debug)] +pub enum DefKind { + /// A def that introduces identifiers + Let, + /// A standalone statement with an fx variable + Stmt(Variable), +} + +impl DefKind { + pub fn map_var Variable>(self, f: F) -> Self { + match self { + DefKind::Let => DefKind::Let, + DefKind::Stmt(v) => DefKind::Stmt(f(v)), + } + } +} + #[derive(Clone, Debug)] pub struct Annotation { pub signature: Type, @@ -195,22 +214,25 @@ enum PendingValueDef<'a> { Option>>, Loc>, ), + /// A standalone statement + Stmt(&'a Loc>), } impl PendingValueDef<'_> { - fn loc_pattern(&self) -> &Loc { + fn loc_pattern(&self) -> Option<&Loc> { match self { - PendingValueDef::AnnotationOnly(loc_pattern, _) => loc_pattern, - PendingValueDef::Body(loc_pattern, _) => loc_pattern, - PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern, + PendingValueDef::AnnotationOnly(loc_pattern, _) => Some(loc_pattern), + PendingValueDef::Body(loc_pattern, _) => Some(loc_pattern), + PendingValueDef::TypedBody(_, loc_pattern, _, _) => Some(loc_pattern), PendingValueDef::ImportParams { loc_pattern, symbol: _, variable: _, module_id: _, opt_provided: _, - } => loc_pattern, - PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern, + } => Some(loc_pattern), + PendingValueDef::IngestedFile(loc_pattern, _, _) => Some(loc_pattern), + PendingValueDef::Stmt(_) => None, } } } @@ -1189,29 +1211,29 @@ fn canonicalize_value_defs<'a>( let mut symbol_to_index: Vec<(IdentId, u32)> = Vec::with_capacity(pending_value_defs.len()); for (def_index, pending_def) in pending_value_defs.iter().enumerate() { - let mut new_bindings = BindingsFromPattern::new(pending_def.loc_pattern()).peekable(); - - if new_bindings.peek().is_none() { - env.problem(Problem::NoIdentifiersIntroduced( - pending_def.loc_pattern().region, - )); - } + if let Some(loc_pattern) = pending_def.loc_pattern() { + let mut new_bindings = BindingsFromPattern::new(loc_pattern).peekable(); - for (s, r) in new_bindings { - // store the top-level defs, used to ensure that closures won't capture them - if let PatternType::TopLevelDef = pattern_type { - env.top_level_symbols.insert(s); + if new_bindings.peek().is_none() { + env.problem(Problem::NoIdentifiersIntroduced(loc_pattern.region)); } - symbols_introduced.insert(s, r); + for (s, r) in new_bindings { + // store the top-level defs, used to ensure that closures won't capture them + if let PatternType::TopLevelDef = pattern_type { + env.top_level_symbols.insert(s); + } - debug_assert_eq!(env.home, s.module_id()); - debug_assert!( - !symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()), - "{s:?}" - ); + symbols_introduced.insert(s, r); - symbol_to_index.push((s.ident_id(), def_index as u32)); + debug_assert_eq!(env.home, s.module_id()); + debug_assert!( + !symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()), + "{s:?}" + ); + + symbol_to_index.push((s.ident_id(), def_index as u32)); + } } } @@ -2217,6 +2239,7 @@ fn single_can_def( expr_var: Variable, opt_loc_annotation: Option>, pattern_vars: SendMap, + kind: DefKind, ) -> Def { let def_annotation = opt_loc_annotation.map(|loc_annotation| Annotation { signature: loc_annotation.value.typ, @@ -2234,6 +2257,7 @@ fn single_can_def( }, pattern_vars, annotation: def_annotation, + kind, } } @@ -2374,6 +2398,7 @@ fn canonicalize_pending_value_def<'a>( expr_var, Some(Loc::at(loc_ann.region, type_annotation)), vars_by_symbol.clone(), + DefKind::Let, ); DefOutput { @@ -2409,6 +2434,7 @@ fn canonicalize_pending_value_def<'a>( loc_can_pattern, loc_expr, Some(Loc::at(loc_ann.region, type_annotation)), + DefKind::Let, ) } Body(loc_can_pattern, loc_expr) => { @@ -2421,6 +2447,20 @@ fn canonicalize_pending_value_def<'a>( loc_can_pattern, loc_expr, None, + DefKind::Let, + ) + } + Stmt(loc_expr) => { + let fx_var = var_store.fresh(); + canonicalize_pending_body( + env, + output, + scope, + var_store, + Loc::at(loc_expr.region, Pattern::Underscore), + loc_expr, + None, + DefKind::Stmt(fx_var), ) } ImportParams { @@ -2461,6 +2501,7 @@ fn canonicalize_pending_value_def<'a>( var_store.fresh(), None, SendMap::default(), + DefKind::Let, ); DefOutput { @@ -2530,6 +2571,7 @@ fn canonicalize_pending_value_def<'a>( var_store.fresh(), opt_loc_can_ann, SendMap::default(), + DefKind::Let, ); DefOutput { @@ -2568,6 +2610,7 @@ fn canonicalize_pending_body<'a>( loc_expr: &'a Loc, opt_loc_annotation: Option>, + kind: DefKind, ) -> DefOutput { let mut loc_value = &loc_expr.value; @@ -2686,6 +2729,7 @@ fn canonicalize_pending_body<'a>( expr_var, opt_loc_annotation, vars_by_symbol, + kind, ); DefOutput { @@ -3075,10 +3119,7 @@ fn to_pending_value_def<'a>( loc_pattern.region, ); - PendingValue::Def(PendingValueDef::AnnotationOnly( - loc_can_pattern, - loc_ann, - )) + PendingValue::Def(PendingValueDef::AnnotationOnly(loc_can_pattern, loc_ann)) } Body(loc_pattern, loc_expr) => { // This takes care of checking for shadowing and adding idents to scope. @@ -3184,15 +3225,17 @@ fn to_pending_value_def<'a>( // Generate a symbol for the module params def // We do this even if params weren't provided so that solve can report if they are missing let params_sym = scope.gen_unique_symbol(); - let params_region = module_import.params.map(|p| p.params.region).unwrap_or(region); + let params_region = module_import + .params + .map(|p| p.params.region) + .unwrap_or(region); let params_var = var_store.fresh(); - let params = - PendingModuleImportParams { - symbol: params_sym, - variable: params_var, - loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)), - opt_provided: module_import.params.map(|p| p.params.value), - }; + let params = PendingModuleImportParams { + symbol: params_sym, + variable: params_var, + loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)), + opt_provided: module_import.params.map(|p| p.params.value), + }; let provided_params = if module_import.params.is_some() { // Only add params to scope if they are provided Some((params_var, params_sym)) @@ -3221,8 +3264,12 @@ fn to_pending_value_def<'a>( .map(|kw| kw.item.items) .unwrap_or_default(); - if exposed_names.is_empty() && !env.home.is_builtin() && module_id.is_automatically_imported() { - env.problems.push(Problem::ExplicitBuiltinImport(module_id, region)); + if exposed_names.is_empty() + && !env.home.is_builtin() + && module_id.is_automatically_imported() + { + env.problems + .push(Problem::ExplicitBuiltinImport(module_id, region)); } let exposed_ids = env @@ -3242,7 +3289,9 @@ fn to_pending_value_def<'a>( let symbol = Symbol::new(module_id, ident_id); exposed_symbols.push((symbol, loc_name.region)); - if let Err((_shadowed_symbol, existing_symbol_region)) = scope.import_symbol(ident, symbol, loc_name.region) { + if let Err((_shadowed_symbol, existing_symbol_region)) = + scope.import_symbol(ident, symbol, loc_name.region) + { if symbol.is_automatically_imported() { env.problem(Problem::ExplicitBuiltinTypeImport( symbol, @@ -3257,14 +3306,12 @@ fn to_pending_value_def<'a>( } } } - None => { - env.problem(Problem::RuntimeError(RuntimeError::ValueNotExposed { - module_name: module_name.clone(), - ident, - region: loc_name.region, - exposed_values: exposed_ids.exposed_values(), - })) - } + None => env.problem(Problem::RuntimeError(RuntimeError::ValueNotExposed { + module_name: module_name.clone(), + ident, + region: loc_name.region, + exposed_values: exposed_ids.exposed_values(), + })), } } @@ -3279,12 +3326,12 @@ fn to_pending_value_def<'a>( let loc_name = ingested_file.name.item; let symbol = match scope.introduce(loc_name.value.into(), loc_name.region) { - Ok(symbol ) => symbol, + Ok(symbol) => symbol, Err((original, shadow, _)) => { env.problem(Problem::Shadowing { original_region: original.region, shadow, - kind: ShadowKind::Variable + kind: ShadowKind::Variable, }); return PendingValue::InvalidIngestedFile; @@ -3293,10 +3340,20 @@ fn to_pending_value_def<'a>( let loc_pattern = Loc::at(loc_name.region, Pattern::Identifier(symbol)); - PendingValue::Def(PendingValueDef::IngestedFile(loc_pattern, ingested_file.annotation.map(|ann| ann.annotation), ingested_file.path)) + PendingValue::Def(PendingValueDef::IngestedFile( + loc_pattern, + ingested_file.annotation.map(|ann| ann.annotation), + ingested_file.path, + )) } StmtAfterExpr => PendingValue::StmtAfterExpr, - Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"), + Stmt(expr) => { + if env.fx_mode == FxMode::Task { + internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar") + } + + PendingValue::Def(PendingValueDef::Stmt(expr)) + } } } diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 20b1afaa9c1..d872e496358 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -203,13 +203,19 @@ fn desugar_value_def<'a>( StmtAfterExpr => internal_error!("unexpected StmtAfterExpr"), Stmt(stmt_expr) => { + if env.fx_mode == FxMode::PurityInference { + // In purity inference mode, statements aren't fully desugared here + // so we can provide better errors + return Stmt(desugar_expr(env, scope, stmt_expr)); + } + // desugar `stmt_expr!` to // _ : {} // _ = stmt_expr! let desugared_expr = desugar_expr(env, scope, stmt_expr); - if env.fx_mode == FxMode::Task && !is_expr_suffixed(&desugared_expr.value) { + if !is_expr_suffixed(&desugared_expr.value) { env.problems.push(Problem::StmtAfterExpr(stmt_expr.region)); return ValueDef::StmtAfterExpr; diff --git a/crates/compiler/can/src/effect_module.rs b/crates/compiler/can/src/effect_module.rs index 394de4b4a3c..1bea1c781c9 100644 --- a/crates/compiler/can/src/effect_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -1,4 +1,4 @@ -use crate::def::Def; +use crate::def::{Def, DefKind}; use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive}; use crate::pattern::Pattern; use crate::scope::Scope; @@ -211,6 +211,7 @@ pub fn build_host_exposed_def( expr_var, pattern_vars, annotation: Some(def_annotation), + kind: DefKind::Let, } } diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index aea53a55812..3debaeb0862 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1,7 +1,7 @@ use crate::abilities::SpecializationId; use crate::annotation::{freshen_opaque_def, IntroducedVariables}; use crate::builtins::builtin_defs_map; -use crate::def::{can_defs_with_return, Annotation, Def}; +use crate::def::{can_defs_with_return, Annotation, Def, DefKind}; use crate::env::Env; use crate::num::{ finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result, @@ -2288,6 +2288,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { expr_var: def.expr_var, pattern_vars: def.pattern_vars, annotation: def.annotation, + kind: def.kind, }); } @@ -2309,6 +2310,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { expr_var: def.expr_var, pattern_vars: def.pattern_vars, annotation: def.annotation, + kind: def.kind, }; let loc_expr = Loc { @@ -2498,6 +2500,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { expr_var, pattern_vars, annotation: None, + kind: DefKind::Let, }; loc_answer = Loc { diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 610f13192c7..76552de3256 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -2,7 +2,7 @@ use std::path::Path; use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl}; use crate::annotation::{canonicalize_annotation, AnnotationFor}; -use crate::def::{canonicalize_defs, report_unused_imports, Def}; +use crate::def::{canonicalize_defs, report_unused_imports, Def, DefKind}; use crate::desugar::desugar_record_destructures; use crate::env::{Env, FxMode}; use crate::expr::{ @@ -657,6 +657,7 @@ pub fn canonicalize_module_defs<'a>( expr_var: var_store.fresh(), pattern_vars, annotation: None, + kind: DefKind::Let, }; declarations.push_def(def); diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index faa25a4c472..aed4faa92e8 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -10,7 +10,7 @@ use roc_can::annotation::IntroducedVariables; use roc_can::constraint::{ Constraint, Constraints, ExpectedTypeIndex, Generalizable, OpportunisticResolve, TypeOrVar, }; -use roc_can::def::Def; +use roc_can::def::{Def, DefKind}; use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext}; use roc_can::expected::Expected::{self, *}; use roc_can::expected::PExpected; @@ -1469,7 +1469,12 @@ pub fn constrain_expr( ); while let Some(def) = stack.pop() { - body_con = constrain_def(types, constraints, env, def, body_con) + body_con = match def.kind { + DefKind::Let => constrain_let_def(types, constraints, env, def, body_con), + DefKind::Stmt(fx_var) => { + constrain_stmt_def(types, constraints, env, def, body_con, fx_var) + } + }; } body_con @@ -3423,7 +3428,7 @@ fn attach_resolution_constraints( constraints.and_constraint([constraint, resolution_constrs]) } -fn constrain_def( +fn constrain_let_def( types: &mut Types, constraints: &mut Constraints, env: &mut Env, @@ -3468,6 +3473,46 @@ fn constrain_def( } } +fn constrain_stmt_def( + types: &mut Types, + constraints: &mut Constraints, + env: &mut Env, + def: &Def, + body_con: Constraint, + fx_var: Variable, +) -> Constraint { + // [purity-inference] TODO: Require fx is effectful + + // Statement expressions must return an empty record + let empty_record_index = constraints.push_type(types, Types::EMPTY_RECORD); + let expected = constraints.push_expected_type(ForReason( + Reason::Stmt, + empty_record_index, + def.loc_expr.region, + )); + + let expr_con = constrain_expr( + types, + constraints, + env, + def.loc_expr.region, + &def.loc_expr.value, + expected, + ); + let expr_con = attach_resolution_constraints(constraints, env, expr_con); + + let generalizable = Generalizable(is_generalizable_expr(&def.loc_expr.value)); + + constraints.let_constraint( + std::iter::empty(), + std::iter::empty(), + std::iter::empty(), + expr_con, + body_con, + generalizable, + ) +} + /// Create a let-constraint for a non-recursive def. /// Recursive defs should always use `constrain_recursive_defs`. pub(crate) fn constrain_def_make_constraint( diff --git a/crates/compiler/derive/src/lib.rs b/crates/compiler/derive/src/lib.rs index fdb4679d6e7..13d5fa1d973 100644 --- a/crates/compiler/derive/src/lib.rs +++ b/crates/compiler/derive/src/lib.rs @@ -4,6 +4,7 @@ use std::iter::once; use std::sync::{Arc, Mutex}; use roc_can::abilities::SpecializationLambdaSets; +use roc_can::def::DefKind; use roc_can::expr::Expr; use roc_can::pattern::Pattern; use roc_can::{def::Def, module::ExposedByModule}; @@ -90,6 +91,7 @@ fn build_derived_body( expr_var: body_type, pattern_vars: once((derived_symbol, body_type)).collect(), annotation: None, + kind: DefKind::Let, }; (def, specialization_lambda_sets) diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index da6dd6995a0..20c4f3a76a7 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -184,6 +184,7 @@ fn remove_for_reason( | Reason::ImportParams(_) | Reason::CallInFunction(_) | Reason::CallInTopLevelDef + | Reason::Stmt | Reason::FunctionOutput => {} } } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 128f8ecf8f7..b4da4740b97 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2620,6 +2620,7 @@ fn from_can_let<'a>( expr_var: def.expr_var, pattern_vars: std::iter::once((anon_name, def.expr_var)).collect(), annotation: None, + kind: def.kind, }); // f = #lam @@ -2629,6 +2630,7 @@ fn from_can_let<'a>( expr_var: def.expr_var, pattern_vars: def.pattern_vars, annotation: def.annotation, + kind: def.kind, }); let new_inner = LetNonRec(new_def, cont); @@ -2648,6 +2650,7 @@ fn from_can_let<'a>( pattern_vars: def.pattern_vars, annotation: def.annotation, expr_var: def.expr_var, + kind: def.kind, }; let new_inner = LetNonRec(Box::new(new_def), cont); @@ -2687,6 +2690,7 @@ fn from_can_let<'a>( pattern_vars: def.pattern_vars, annotation: def.annotation, expr_var: def.expr_var, + kind: def.kind, }; let new_inner = LetNonRec(Box::new(new_def), cont); @@ -7292,6 +7296,7 @@ fn to_opt_branches<'a>( roc_can::pattern::Pattern::Identifier(symbol), ), pattern_vars: std::iter::once((symbol, variable)).collect(), + kind: roc_can::def::DefKind::Let, }; let new_expr = roc_can::expr::Expr::LetNonRec(Box::new(def), Box::new(loc_expr)); diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 7839ba0b4a0..e8b229fa371 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3448,6 +3448,7 @@ pub enum Reason { foreign_symbol: ForeignSymbol, arg_index: HumanIndex, }, + Stmt, CallInFunction(Option), CallInTopLevelDef, FloatLiteral, diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 2e4573d6f2f..f05a78bc271 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1681,6 +1681,7 @@ fn to_expr_report<'b>( } Reason::CallInFunction(_) => todo!("[purity-inference] CallInFunction"), Reason::CallInTopLevelDef => todo!("[purity-inference] CallInTopLevelDef"), + Reason::Stmt => todo!("[purity-inference] Stmt"), }, } } From 69e026f8bb8728cb1c29136fd6f94f4f941d96fc Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 15 Oct 2024 22:33:07 -0300 Subject: [PATCH 23/73] Leftover statement warning for pure statements --- crates/compiler/can/src/constraint.rs | 6 ++ crates/compiler/constrain/src/expr.rs | 55 ++++++++--- crates/compiler/load/tests/test_reporting.rs | 99 +++++++++---------- .../compiler/lower_params/src/type_error.rs | 3 +- crates/compiler/solve/src/solve.rs | 25 +++++ crates/compiler/solve_problem/src/lib.rs | 5 +- crates/reporting/src/error/type.rs | 16 +++ 7 files changed, 140 insertions(+), 69 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index d15f88280e8..46e93b8ebfc 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -598,6 +598,7 @@ impl Constraints { | Constraint::Store(..) | Constraint::Lookup(..) | Constraint::Pattern(..) + | Constraint::EffectfulStmt(..) | Constraint::True | Constraint::IsOpenType(_) | Constraint::IncludesTag(_) @@ -770,6 +771,8 @@ pub enum Constraint { Index, Region, ), + /// Expect statement to be effectful + EffectfulStmt(Variable, Region), /// Used for things that always unify, e.g. blanks and runtime errors True, SaveTheEnvironment, @@ -858,6 +861,9 @@ impl std::fmt::Debug for Constraint { Self::Pattern(arg0, arg1, arg2, arg3) => { write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } + Self::EffectfulStmt(arg0, arg1) => { + write!(f, "EffectfulStmt({arg0:?}, {arg1:?})") + } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(), diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index aed4faa92e8..ff3a124ad47 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -3481,36 +3481,59 @@ fn constrain_stmt_def( body_con: Constraint, fx_var: Variable, ) -> Constraint { - // [purity-inference] TODO: Require fx is effectful + let region = def.loc_expr.region; // Statement expressions must return an empty record let empty_record_index = constraints.push_type(types, Types::EMPTY_RECORD); - let expected = constraints.push_expected_type(ForReason( - Reason::Stmt, - empty_record_index, - def.loc_expr.region, - )); + let expect_empty_record = + constraints.push_expected_type(ForReason(Reason::Stmt, empty_record_index, region)); + + let expr_con = env.with_enclosing_fx(fx_var, None, |env| { + constrain_expr( + types, + constraints, + env, + region, + &def.loc_expr.value, + expect_empty_record, + ) + }); - let expr_con = constrain_expr( - types, - constraints, - env, - def.loc_expr.region, - &def.loc_expr.value, - expected, - ); let expr_con = attach_resolution_constraints(constraints, env, expr_con); let generalizable = Generalizable(is_generalizable_expr(&def.loc_expr.value)); - constraints.let_constraint( + let body_con = constraints.let_constraint( std::iter::empty(), std::iter::empty(), std::iter::empty(), expr_con, body_con, generalizable, - ) + ); + + // Stmt expr must be effectful, otherwise it's dead code + let effectful_constraint = Constraint::EffectfulStmt(fx_var, region); + + // We have to unify the stmt fx with the enclosing fx + // since we used the former to constrain the expr. + let enclosing_fx_index = match env.enclosing_fx { + Some(enclosing_fn) => { + let enclosing_fx_index = constraints.push_variable(enclosing_fn.fx_var); + + constraints.push_expected_type(ForReason(Reason::Stmt, enclosing_fx_index, region)) + } + None => constraints.push_expected_type(ForReason( + // Statements are not allowed in top-level defs + Reason::Stmt, + constraints.push_variable(Variable::PURE), + region, + )), + }; + let enclosing_fx_constraint = + constraints.equal_types_var(fx_var, enclosing_fx_index, Category::Unknown, region); + + constraints.and_constraint([body_con, effectful_constraint, enclosing_fx_constraint]) } /// Create a let-constraint for a non-recursive def. diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 55c5abfaa1b..e98eae796ea 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14471,40 +14471,6 @@ All branches in an `if` must have the same type! "# ); - // TODO: add the following tests after built-in Tasks are added - // https://github.com/roc-lang/roc/pull/6836 - - // test_report!( - // suffixed_stmt_invalid_type, - // indoc!( - // r###" - // app "test" provides [main] to "./platform" - - // main : Task U64 _ -> _ - // main = \task -> - // task! - // 42 - // "### - // ), - // @r"" - // ); - - // test_report!( - // suffixed_expr_invalid_type, - // indoc!( - // r###" - // app "test" provides [main] to "./platform" - - // main : Task U64 _ -> _ - // main = \task -> - // result : U32 - // result = task! - // result - // "### - // ), - // @r"" - // ); - test_report!( issue_6240_1, indoc!( @@ -14528,38 +14494,38 @@ All branches in an `if` must have the same type! issue_6240_2, indoc!( r#" - ("", "").abcde - "# + ("", "").abcde + "# ), @r###" - ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── + ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── - This expression is used in an unexpected way: + This expression is used in an unexpected way: - 4│ ("", "").abcde - ^^^^^^^^^^^^^^ + 4│ ("", "").abcde + ^^^^^^^^^^^^^^ - It is a tuple of type: + It is a tuple of type: - ( - Str, - Str, - )a + ( + Str, + Str, + )a - But you are trying to use it as: + But you are trying to use it as: - { abcde : * }b - "### + { abcde : * }b + "### ); test_report!( issue_6240_3, indoc!( r" - {}.0 - " + {}.0 + " ), - @r###" + @r" ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── This expression is used in an unexpected way: @@ -14574,6 +14540,37 @@ All branches in an `if` must have the same type! But you are trying to use it as: (*)b + " + ); + + test_report!( + leftover_statement, + indoc!( + r#" + app [main] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main = \{} -> + identity {} + + Effect.putLine! "hello" + + identity = \x -> x + "# + ), + @r###" + ── LEFTOVER STATEMENT in /code/proj/Main.roc ─────────────────────────────────── + + This statement does not produce any effects: + + 6│ identity {} + ^^^^^^^^^^^ + + Standalone statements are only useful if they call effectful + functions. + + Did you forget to use its result? If not, feel free to remove it. "### ); diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 20c4f3a76a7..e0d2aea405b 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -99,7 +99,8 @@ pub fn remove_module_param_arguments( | TypeError::IngestedFileUnsupportedType(_, _) | TypeError::UnexpectedModuleParams(_, _) | TypeError::MissingModuleParams(_, _, _) - | TypeError::ModuleParamsMismatch(_, _, _, _) => {} + | TypeError::ModuleParamsMismatch(_, _, _, _) + | TypeError::PureStmt(_) => {} } } } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 08df3add1c3..05152c5cf43 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -777,6 +777,31 @@ fn solve( } } } + EffectfulStmt(variable, region) => { + let content = env.subs.get_content_without_compacting(*variable); + + match content { + Content::Pure | Content::FlexVar(_) => { + let problem = TypeError::PureStmt(*region); + problems.push(problem); + + state + } + Content::Effectful => state, + Content::RigidVar(_) + | Content::FlexAbleVar(_, _) + | Content::RigidAbleVar(_, _) + | Content::RecursionVar { .. } + | Content::LambdaSet(_) + | Content::ErasedLambda + | Content::Structure(_) + | Content::Alias(_, _, _, _) + | Content::RangedNumber(_) + | Content::Error => { + internal_error!("ExpectEffectful: unexpected content: {:?}", content) + } + } + } Let(index, pool_slice) => { let let_con = &env.constraints.let_constraints[index.index()]; diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 9eabe601ba1..4e49041a1d4 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -39,6 +39,7 @@ pub enum TypeError { UnexpectedModuleParams(Region, ModuleId), MissingModuleParams(Region, ModuleId, ErrorType), ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType), + PureStmt(Region), } impl TypeError { @@ -63,6 +64,7 @@ impl TypeError { TypeError::ModuleParamsMismatch(..) => RuntimeError, TypeError::IngestedFileBadUtf8(..) => Fatal, TypeError::IngestedFileUnsupportedType(..) => Fatal, + TypeError::PureStmt(..) => Warning, } } @@ -78,7 +80,8 @@ impl TypeError { | TypeError::BadPatternMissingAbility(region, ..) | TypeError::UnexpectedModuleParams(region, ..) | TypeError::MissingModuleParams(region, ..) - | TypeError::ModuleParamsMismatch(region, ..) => Some(*region), + | TypeError::ModuleParamsMismatch(region, ..) + | TypeError::PureStmt(region) => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), TypeError::Exhaustive(e) => Some(e.region()), TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index f05a78bc271..09bf62bc18f 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -314,6 +314,22 @@ pub fn type_problem<'b>( severity, }) } + PureStmt(region) => { + let stack = [ + alloc.reflow("This statement does not produce any effects:"), + alloc.region(lines.convert_region(region), severity), + alloc.reflow( + "Standalone statements are only useful if they call effectful functions.", + ), + alloc.reflow("Did you forget to use its result? If not, feel free to remove it."), + ]; + Some(Report { + title: "LEFTOVER STATEMENT".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } } } From 2c8571537e7dd4ee8df8d6b1320db4324353f024 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 10:08:11 -0300 Subject: [PATCH 24/73] Get suffix from IdentId or Symbol IdentId will now reserve the MSB for flagging whether the ident is suffixed with a `!`. We will use this later to constrain identifiers to be effectful or pure. --- crates/compiler/module/src/ident.rs | 57 +++++++++++++ crates/compiler/module/src/symbol.rs | 116 +++++++++++++++++---------- 2 files changed, 131 insertions(+), 42 deletions(-) diff --git a/crates/compiler/module/src/ident.rs b/crates/compiler/module/src/ident.rs index 0e955b2a3e7..74ea9cd201d 100644 --- a/crates/compiler/module/src/ident.rs +++ b/crates/compiler/module/src/ident.rs @@ -362,3 +362,60 @@ impl fmt::Display for Uppercase { fmt::Display::fmt(&self.0, f) } } + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum IdentSuffix { + None, + Bang, +} + +impl IdentSuffix { + #[inline(always)] + pub const fn from_name(name: &str) -> Self { + // Checking bytes directly so it can be const. + // This should be fine since the suffix is ASCII. + let bytes = name.as_bytes(); + let len = bytes.len(); + + debug_assert!(len > 0, "Ident name must not be empty"); + + if bytes[len - 1] == ('!' as u8) { + IdentSuffix::Bang + } else { + IdentSuffix::None + } + } + + pub fn is_suffixed(&self) -> bool { + match self { + IdentSuffix::None => false, + IdentSuffix::Bang => true, + } + } +} + +#[cfg(test)] +mod suffix_test { + use crate::ident::IdentSuffix; + + #[test] + fn ends_with_bang() { + assert_eq!(IdentSuffix::from_name("foo!"), IdentSuffix::Bang) + } + + #[test] + fn ends_without_bang() { + assert_eq!(IdentSuffix::from_name("foo"), IdentSuffix::None) + } + + #[test] + fn invalid() { + assert_eq!(IdentSuffix::from_name("foo!bar"), IdentSuffix::None) + } + + #[test] + #[should_panic] + fn empty_panics() { + IdentSuffix::from_name(""); + } +} diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index f99901b2c65..a408faf4edc 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1,4 +1,4 @@ -use crate::ident::{Ident, Lowercase, ModuleName}; +use crate::ident::{Ident, IdentSuffix, Lowercase, ModuleName}; use crate::module_err::{ModuleError, ModuleResult}; use roc_collections::{SmallStringInterner, VecMap}; use roc_error_macros::internal_error; @@ -79,7 +79,7 @@ impl Symbol { Self { module_id: module_id.0, - ident_id: ident_id.0, + ident_id: ident_id.raw(), } } @@ -88,13 +88,17 @@ impl Symbol { } pub const fn ident_id(self) -> IdentId { - IdentId(self.ident_id) + IdentId::from_raw(self.ident_id) } pub const fn is_builtin(self) -> bool { self.module_id().is_builtin() } + pub const fn suffix(self) -> IdentSuffix { + self.ident_id().suffix() + } + pub fn is_derivable_ability(self) -> bool { self.derivable_ability().is_some() } @@ -146,7 +150,7 @@ impl Symbol { .unwrap_or_else(|| { internal_error!( "ident_string's IdentIds did not contain an entry for {} in module {:?}", - self.ident_id().0, + self.ident_id().index(), self.module_id() ) }) @@ -246,11 +250,9 @@ impl fmt::Debug for Symbol { impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let module_id = self.module_id(); - let ident_id = self.ident_id(); + let ident_id = self.ident_id().index(); - match ident_id { - IdentId(value) => write!(f, "{module_id:?}.{value:?}"), - } + write!(f, "{module_id:?}.{ident_id:?}") } } @@ -319,10 +321,6 @@ impl Interns { } } } - - pub fn from_index(module_id: ModuleId, ident_id: u32) -> Symbol { - Symbol::new(module_id, IdentId(ident_id)) - } } pub fn get_module_ident_ids<'a>( @@ -637,28 +635,59 @@ impl ModuleIds { } } -/// An ID that is assigned to interned string identifiers within a module. -/// By turning these strings into numbers, post-canonicalization processes -/// like unification and optimization can run a lot faster. -/// -/// This ID is unique within a given module, not globally - so to turn this back into -/// a string, you would need a ModuleId, an IdentId, and a Map>. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct IdentId(u32); +mod ident_id { + use crate::ident::IdentSuffix; -impl IdentId { - pub const fn index(self) -> usize { - self.0 as usize - } - - /// # Safety + /// An ID that is assigned to interned string identifiers within a module. + /// By turning these strings into numbers, post-canonicalization processes + /// like unification and optimization can run a lot faster. /// - /// The index is not guaranteed to know to exist. - pub unsafe fn from_index(index: u32) -> Self { - Self(index) + /// This ID is unique within a given module, not globally - so to turn this back into + /// a string, you would need a ModuleId, an IdentId, and a Map>. + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + pub struct IdentId(u32); + + const BANG_FLAG: u32 = 1u32 << 31; + const UNSUFFIXED: u32 = !BANG_FLAG; + + impl IdentId { + pub const fn index(self) -> usize { + (self.0 & UNSUFFIXED) as usize + } + + pub const fn suffix(self) -> IdentSuffix { + if self.0 & BANG_FLAG > 0 { + IdentSuffix::Bang + } else { + IdentSuffix::None + } + } + + pub(super) const fn raw(self) -> u32 { + self.0 + } + + pub(super) const fn from_raw(raw: u32) -> Self { + Self(raw) + } + + pub(super) const fn from_index(index: usize, suffix: IdentSuffix) -> Self { + assert!(index as u32 <= UNSUFFIXED, "IdentId index too large"); + + match suffix { + IdentSuffix::None => Self(index as u32), + IdentSuffix::Bang => Self((index as u32) | BANG_FLAG), + } + } + + pub(super) const fn from_index_named(index: usize, name: &str) -> Self { + Self::from_index(index, IdentSuffix::from_name(name)) + } } } +pub use ident_id::IdentId; + /// Stores a mapping between Ident and IdentId. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct IdentIds { @@ -670,15 +699,15 @@ impl IdentIds { self.interner .iter() .enumerate() - .map(|(index, ident)| (IdentId(index as u32), ident)) + .map(|(index, ident)| (IdentId::from_index_named(index, ident), ident)) } pub fn add_str(&mut self, ident_name: &str) -> IdentId { - IdentId(self.interner.insert(ident_name) as u32) + IdentId::from_index_named(self.interner.insert(ident_name), ident_name) } pub fn duplicate_ident(&mut self, ident_id: IdentId) -> IdentId { - IdentId(self.interner.duplicate(ident_id.0 as usize) as u32) + IdentId::from_index(self.interner.duplicate(ident_id.index()), ident_id.suffix()) } pub fn get_or_insert(&mut self, name: &str) -> IdentId { @@ -692,7 +721,7 @@ impl IdentIds { // TODO fix when same ident_name is present multiple times, see issue #2548 pub fn update_key(&mut self, old_name: &str, new_name: &str) -> Result { match self.interner.find_and_update(old_name, new_name) { - Some(index) => Ok(IdentId(index as u32)), + Some(index) => Ok(IdentId::from_index_named(index, new_name)), None => Err(format!("The identifier {old_name:?} is not in IdentIds")), } } @@ -705,12 +734,12 @@ impl IdentIds { /// This is used, for example, during canonicalization of an Expr::Closure /// to generate a unique symbol to refer to that closure. pub fn gen_unique(&mut self) -> IdentId { - IdentId(self.interner.insert_index_str() as u32) + IdentId::from_index(self.interner.insert_index_str(), IdentSuffix::None) } pub fn is_generated_id(&self, id: IdentId) -> bool { self.interner - .try_get(id.0 as usize) + .try_get(id.index()) .map_or(false, |str| str.starts_with(|c: char| c.is_ascii_digit())) } @@ -718,18 +747,18 @@ impl IdentIds { pub fn get_id(&self, ident_name: &str) -> Option { self.interner .find_index(ident_name) - .map(|i| IdentId(i as u32)) + .map(|i| IdentId::from_index_named(i, ident_name)) } #[inline(always)] pub fn get_id_many<'a>(&'a self, ident_name: &'a str) -> impl Iterator + 'a { self.interner .find_indices(ident_name) - .map(|i| IdentId(i as u32)) + .map(|i| IdentId::from_index_named(i, ident_name)) } pub fn get_name(&self, id: IdentId) -> Option<&str> { - self.interner.try_get(id.0 as usize) + self.interner.try_get(id.index()) } pub fn get_name_str_res(&self, ident_id: IdentId) -> ModuleResult<&str> { @@ -1013,10 +1042,10 @@ macro_rules! define_builtins { $( $( $(#[$ident_meta])* - pub const $ident_const: Symbol = Symbol::new(ModuleId::$module_const, IdentId($ident_id)); + pub const $ident_const: Symbol = Symbol::new(ModuleId::$module_const, IdentId::from_index_named($ident_id, $ident_name)); )* $( - pub const $u_ident_const: Symbol = Symbol::new(ModuleId::$module_const, IdentId($u_ident_id)); + pub const $u_ident_const: Symbol = Symbol::new(ModuleId::$module_const, IdentId::from_index_named($u_ident_id, $u_ident_name)); )* )+ @@ -1038,9 +1067,11 @@ macro_rules! define_builtins { // release builds, this condition is either `if true` // or `if false` and will get optimized out. debug_assert_eq!($exposed_apply_type, $ident_name.chars().next().unwrap().is_uppercase()); + // Types should not be suffixed + debug_assert!(!IdentSuffix::from_name($ident_name).is_suffixed()); if $exposed_apply_type { - scope.insert($ident_name.into(), (Symbol::new(ModuleId::$module_const, IdentId($ident_id)), Region::zero())); + scope.insert($ident_name.into(), (Symbol::new(ModuleId::$module_const, IdentId::from_index($ident_id, IdentSuffix::None)), Region::zero())); } )? )* @@ -1059,7 +1090,7 @@ macro_rules! define_builtins { $( $( if $exposed_type { - ($ident_name, (Symbol::new(ModuleId::$module_const, IdentId($ident_id)), Region::zero())) + ($ident_name, (Symbol::new(ModuleId::$module_const, IdentId::from_raw($ident_id)), Region::zero())) } else { unreachable!() }, @@ -1474,6 +1505,7 @@ define_builtins! { 87 LIST_CLONE: "clone" 88 LIST_LEN_USIZE: "lenUsize" 89 LIST_CONCAT_UTF8: "concatUtf8" + 90 LIST_WALK_FX: "walk!" } 7 RESULT: "Result" => { 0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias From b80f44738fae2713f1e04c08002bf970a9986d0f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 15:30:49 -0300 Subject: [PATCH 25/73] Switch fx mode based on platform main too --- crates/compiler/load_internal/src/file.rs | 20 ++++++++------------ crates/compiler/module/src/ident.rs | 9 ++++++++- crates/compiler/module/src/symbol.rs | 2 +- crates/compiler/parse/src/header.rs | 3 ++- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 775be12ad60..83f0d5720ae 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -34,7 +34,7 @@ use roc_debug_flags::{ use roc_derive::SharedDerivedModule; use roc_error_macros::internal_error; use roc_late_solve::{AbilitiesView, WorldAbilities}; -use roc_module::ident::{Ident, ModuleName, QualifiedModuleName}; +use roc_module::ident::{Ident, IdentSuffix, ModuleName, QualifiedModuleName}; use roc_module::symbol::{ IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified, Symbol, @@ -2235,6 +2235,7 @@ fn update<'a>( config_shorthand, provides, exposes_ids, + requires, .. } => { work.extend(state.dependencies.notify_package(config_shorthand)); @@ -2269,6 +2270,12 @@ fn update<'a>( if header.is_root_module { state.exposed_modules = exposes_ids; } + + if requires.iter().any(|requires| { + IdentSuffix::from_name(requires.value.ident.value).is_bang() + }) { + state.fx_mode = FxMode::PurityInference; + } } Builtin { .. } | Module { .. } => { if header.is_root_module { @@ -2286,17 +2293,6 @@ fn update<'a>( .iter() .any(|exposed| exposed.value.is_effectful_fn()) { - if exposes - .iter() - .any(|exposed| !exposed.value.is_effectful_fn()) - { - // Temporary error message while we transition platforms - return Err(LoadingProblem::FormattedReport( - "Hosted module must not mix effectful and Task functions" - .to_string(), - )); - } - state.fx_mode = FxMode::PurityInference; } } diff --git a/crates/compiler/module/src/ident.rs b/crates/compiler/module/src/ident.rs index 74ea9cd201d..a0f979d0dbe 100644 --- a/crates/compiler/module/src/ident.rs +++ b/crates/compiler/module/src/ident.rs @@ -386,7 +386,14 @@ impl IdentSuffix { } } - pub fn is_suffixed(&self) -> bool { + pub fn is_none(&self) -> bool { + match self { + IdentSuffix::None => true, + IdentSuffix::Bang => false, + } + } + + pub fn is_bang(&self) -> bool { match self { IdentSuffix::None => false, IdentSuffix::Bang => true, diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index a408faf4edc..d14e549109f 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1068,7 +1068,7 @@ macro_rules! define_builtins { // or `if false` and will get optimized out. debug_assert_eq!($exposed_apply_type, $ident_name.chars().next().unwrap().is_uppercase()); // Types should not be suffixed - debug_assert!(!IdentSuffix::from_name($ident_name).is_suffixed()); + debug_assert!(IdentSuffix::from_name($ident_name).is_none()); if $exposed_apply_type { scope.insert($ident_name.into(), (Symbol::new(ModuleId::$module_const, IdentId::from_index($ident_id, IdentSuffix::None)), Region::zero())); diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index f7bb0ee428e..6098505570d 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -19,6 +19,7 @@ use crate::pattern::record_pattern_fields; use crate::state::State; use crate::string_literal::{self, parse_str_literal}; use crate::type_annotation; +use roc_module::ident::IdentSuffix; use roc_module::symbol::ModuleId; use roc_region::all::{Loc, Position, Region}; @@ -1134,7 +1135,7 @@ impl<'a> ExposedName<'a> { } pub fn is_effectful_fn(&self) -> bool { - self.0.ends_with('!') + IdentSuffix::from_name(self.0).is_bang() } } From 75856ae804c4d7acbd5128c955cca6dc0c5c429b Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 15:34:35 -0300 Subject: [PATCH 26/73] Support `!` in symbols provided to host --- crates/compiler/mono/src/layout.rs | 6 +++--- examples/cli/effects-platform/main.roc | 8 ++++---- examples/cli/effects.roc | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index ed65781b4ce..b45a69d77f5 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -4646,7 +4646,7 @@ impl LayoutId { // Returns something like "#UserApp_foo_1" when given a symbol that interns to "foo" // and a LayoutId of 1. pub fn to_symbol_string(self, symbol: Symbol, interns: &Interns) -> String { - let ident_string = symbol.as_str(interns); + let ident_string = symbol.as_str(interns).trim_end_matches('!'); let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap(); format!("{}_{}_{}", module_string, ident_string, self.0) } @@ -4654,12 +4654,12 @@ impl LayoutId { // Returns something like "roc__foo_1_exposed" when given a symbol that interns to "foo" // and a LayoutId of 1. pub fn to_exposed_symbol_string(self, symbol: Symbol, interns: &Interns) -> String { - let ident_string = symbol.as_str(interns); + let ident_string = symbol.as_str(interns).trim_end_matches('!'); format!("roc__{}_{}_exposed", ident_string, self.0) } pub fn to_exposed_generic_symbol_string(self, symbol: Symbol, interns: &Interns) -> String { - let ident_string = symbol.as_str(interns); + let ident_string = symbol.as_str(interns).trim_end_matches('!'); format!("roc__{}_{}_exposed_generic", ident_string, self.0) } } diff --git a/examples/cli/effects-platform/main.roc b/examples/cli/effects-platform/main.roc index 6e9934684f9..bcaccafe040 100644 --- a/examples/cli/effects-platform/main.roc +++ b/examples/cli/effects-platform/main.roc @@ -1,9 +1,9 @@ platform "effects" - requires {} { main : {} => {} } + requires {} { main! : {} => {} } exposes [] packages {} imports [] - provides [mainForHost] + provides [mainForHost!] -mainForHost : {} => {} -mainForHost = \{} -> main {} +mainForHost! : {} => {} +mainForHost! = \{} -> main! {} diff --git a/examples/cli/effects.roc b/examples/cli/effects.roc index 9d8c026cf6a..fd1b4a58a2c 100644 --- a/examples/cli/effects.roc +++ b/examples/cli/effects.roc @@ -1,9 +1,9 @@ -app [main] { pf: platform "effects-platform/main.roc" } +app [main!] { pf: platform "effects-platform/main.roc" } import pf.Effect -main : {} => {} -main = \{} -> +main! : {} => {} +main! = \{} -> line = Effect.getLine! {} if line == "secret" then From 1da8af390bd355b7cc84980d49713d54d39d9c8a Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 16:08:33 -0300 Subject: [PATCH 27/73] Unsuffixed effectul function warning --- crates/compiler/load/tests/test_reporting.rs | 66 ++++++++++++++++++- .../compiler/lower_params/src/type_error.rs | 3 +- crates/compiler/solve/src/solve.rs | 28 ++++++++ crates/compiler/solve_problem/src/lib.rs | 5 +- crates/reporting/src/error/type.rs | 17 +++++ 5 files changed, 115 insertions(+), 4 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index e98eae796ea..a349f25252f 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14547,11 +14547,11 @@ All branches in an `if` must have the same type! leftover_statement, indoc!( r#" - app [main] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } import pf.Effect - main = \{} -> + main! = \{} -> identity {} Effect.putLine! "hello" @@ -14689,4 +14689,66 @@ All branches in an `if` must have the same type! Num * "### ); + + test_report!( + function_def_fx_no_bang, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + printHello {} + + printHello = \{} -> + Effect.putLine! "hello" + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 8│ printHello = \{} -> + ^^^^^^^^^^ + + Add an exclamation mark at the end of its name, like: + + printHello! + + This will help readers identify it as a source of effects. + "### + ); + + test_report!( + nested_function_def_fx_no_bang, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + printHello = \{} -> + Effect.putLine! "hello" + + printHello {} + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 6│ printHello = \{} -> + ^^^^^^^^^^ + + Add an exclamation mark at the end of its name, like: + + printHello! + + This will help readers identify it as a source of effects. + "### + ); } diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index e0d2aea405b..18ddc62be2e 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -100,7 +100,8 @@ pub fn remove_module_param_arguments( | TypeError::UnexpectedModuleParams(_, _) | TypeError::MissingModuleParams(_, _, _) | TypeError::ModuleParamsMismatch(_, _, _, _) - | TypeError::PureStmt(_) => {} + | TypeError::PureStmt(_) + | TypeError::UnsuffixedEffectfulFunction(_, _) => {} } } } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 05152c5cf43..95cdc9fd1a7 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -22,6 +22,7 @@ use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED; use roc_error_macros::internal_error; +use roc_module::ident::IdentSuffix; use roc_module::symbol::{ModuleId, Symbol}; use roc_problem::can::CycleEntry; use roc_region::all::Loc; @@ -448,6 +449,8 @@ fn solve( ); new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value); + + check_symbol_suffix(env, problems, *symbol, *loc_var); } // Note that this vars_by_symbol is the one returned by the @@ -1520,6 +1523,31 @@ fn solve( state } +fn check_symbol_suffix( + env: &mut InferenceEnv<'_>, + problems: &mut Vec, + symbol: Symbol, + loc_var: Loc, +) { + match symbol.suffix() { + IdentSuffix::None => { + if let Content::Structure(FlatType::Func(_, _, _, fx)) = + env.subs.get_content_without_compacting(loc_var.value) + { + if let Content::Effectful = env.subs.get_content_without_compacting(*fx) { + problems.push(TypeError::UnsuffixedEffectfulFunction( + loc_var.region, + symbol, + )); + } + } + } + IdentSuffix::Bang => { + // [purity-inference] TODO + } + } +} + fn chase_alias_content(subs: &Subs, mut var: Variable) -> (Variable, &Content) { loop { match subs.get_content_without_compacting(var) { diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 4e49041a1d4..49c6bcb389f 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -40,6 +40,7 @@ pub enum TypeError { MissingModuleParams(Region, ModuleId, ErrorType), ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType), PureStmt(Region), + UnsuffixedEffectfulFunction(Region, Symbol), } impl TypeError { @@ -65,6 +66,7 @@ impl TypeError { TypeError::IngestedFileBadUtf8(..) => Fatal, TypeError::IngestedFileUnsupportedType(..) => Fatal, TypeError::PureStmt(..) => Warning, + TypeError::UnsuffixedEffectfulFunction(_, _) => Warning, } } @@ -81,7 +83,8 @@ impl TypeError { | TypeError::UnexpectedModuleParams(region, ..) | TypeError::MissingModuleParams(region, ..) | TypeError::ModuleParamsMismatch(region, ..) - | TypeError::PureStmt(region) => Some(*region), + | TypeError::PureStmt(region) + | TypeError::UnsuffixedEffectfulFunction(region, _) => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), TypeError::Exhaustive(e) => Some(e.region()), TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 09bf62bc18f..e1584e459bf 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -330,6 +330,23 @@ pub fn type_problem<'b>( severity, }) } + UnsuffixedEffectfulFunction(region, symbol) => { + let stack = [ + alloc.reflow("This function is effectful, but its name does not indicate so:"), + alloc.region(lines.convert_region(region), severity), + alloc.reflow("Add an exclamation mark at the end of its name, like:"), + alloc + .string(format!("{}!", symbol.as_str(alloc.interns))) + .indent(4), + alloc.reflow("This will help readers identify it as a source of effects."), + ]; + Some(Report { + title: "MISSING EXCLAMATION".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } } } From d22b2a79f5b292fd56cf624a878bf0782293de81 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 16:20:29 -0300 Subject: [PATCH 28/73] Suffixed pure function warning --- crates/compiler/load/tests/test_reporting.rs | 28 +++++++++++++++++++ .../compiler/lower_params/src/type_error.rs | 3 +- crates/compiler/solve/src/solve.rs | 12 +++++++- crates/compiler/solve_problem/src/lib.rs | 5 +++- crates/reporting/src/error/type.rs | 14 ++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index a349f25252f..43f8fef3cb3 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14751,4 +14751,32 @@ All branches in an `if` must have the same type! This will help readers identify it as a source of effects. "### ); + + test_report!( + function_def_leftover_bang, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + Effect.putLine! (hello! {}) + + hello! = \{} -> + "hello" + "# + ), + @r###" + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This function is pure, but its name suggests otherwise: + + 8│ hello! = \{} -> + ^^^^^^ + + Remove the exclamation mark to give an accurate impression of its + behavior. + "### + ); } diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 18ddc62be2e..6cb79ad4c8d 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -101,7 +101,8 @@ pub fn remove_module_param_arguments( | TypeError::MissingModuleParams(_, _, _) | TypeError::ModuleParamsMismatch(_, _, _, _) | TypeError::PureStmt(_) - | TypeError::UnsuffixedEffectfulFunction(_, _) => {} + | TypeError::UnsuffixedEffectfulFunction(_, _) + | TypeError::SuffixedPureFunction(_, _) => {} } } } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 95cdc9fd1a7..5838e3a0e02 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1543,7 +1543,17 @@ fn check_symbol_suffix( } } IdentSuffix::Bang => { - // [purity-inference] TODO + if let Content::Structure(FlatType::Func(_, _, _, fx)) = + env.subs.get_content_without_compacting(loc_var.value) + { + match env.subs.get_content_without_compacting(*fx) { + // [purity-inference] TODO: Should FlexVar actually be a case? + Content::Pure | Content::FlexVar(_) => { + problems.push(TypeError::SuffixedPureFunction(loc_var.region, symbol)); + } + _ => {} + } + } } } } diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 49c6bcb389f..99cfbe8d717 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -41,6 +41,7 @@ pub enum TypeError { ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType), PureStmt(Region), UnsuffixedEffectfulFunction(Region, Symbol), + SuffixedPureFunction(Region, Symbol), } impl TypeError { @@ -67,6 +68,7 @@ impl TypeError { TypeError::IngestedFileUnsupportedType(..) => Fatal, TypeError::PureStmt(..) => Warning, TypeError::UnsuffixedEffectfulFunction(_, _) => Warning, + TypeError::SuffixedPureFunction(_, _) => Warning, } } @@ -84,7 +86,8 @@ impl TypeError { | TypeError::MissingModuleParams(region, ..) | TypeError::ModuleParamsMismatch(region, ..) | TypeError::PureStmt(region) - | TypeError::UnsuffixedEffectfulFunction(region, _) => Some(*region), + | TypeError::UnsuffixedEffectfulFunction(region, _) + | TypeError::SuffixedPureFunction(region, _) => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), TypeError::Exhaustive(e) => Some(e.region()), TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index e1584e459bf..a2876167185 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -347,6 +347,20 @@ pub fn type_problem<'b>( severity, }) } + SuffixedPureFunction(region, _symbol) => { + let stack = [ + alloc.reflow("This function is pure, but its name suggests otherwise:"), + alloc.region(lines.convert_region(region), severity), + alloc.reflow("Remove the exclamation mark to give an accurate impression of its behavior."), + ]; + + Some(Report { + title: "UNNECESSARY EXCLAMATION".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } } } From 839078b5d1261bbff7303b2ce49298ce0262bb58 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 16:26:40 -0300 Subject: [PATCH 29/73] Test aliased unsuffixed effectful function --- crates/compiler/load/tests/test_reporting.rs | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 43f8fef3cb3..5c53c6d9503 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14779,4 +14779,34 @@ All branches in an `if` must have the same type! behavior. "### ); + + test_report!( + aliased_fx_fn, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + printLn "Hello!" + + printLn = Effect.putLine! + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 8│ printLn = Effect.putLine! + ^^^^^^^ + + Add an exclamation mark at the end of its name, like: + + printLn! + + This will help readers identify it as a source of effects. + "### + ); } From fd2493ee5170f0b30f50c7e078b1eb977cc96b27 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 30 Oct 2024 19:36:19 -0300 Subject: [PATCH 30/73] Report effect call in pure function --- crates/compiler/load/tests/test_reporting.rs | 91 +++++++++++--------- crates/reporting/src/error/type.rs | 40 ++++++++- 2 files changed, 88 insertions(+), 43 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 5c53c6d9503..19ee90de450 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14543,37 +14543,6 @@ All branches in an `if` must have the same type! " ); - test_report!( - leftover_statement, - indoc!( - r#" - app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } - - import pf.Effect - - main! = \{} -> - identity {} - - Effect.putLine! "hello" - - identity = \x -> x - "# - ), - @r###" - ── LEFTOVER STATEMENT in /code/proj/Main.roc ─────────────────────────────────── - - This statement does not produce any effects: - - 6│ identity {} - ^^^^^^^^^^^ - - Standalone statements are only useful if they call effectful - functions. - - Did you forget to use its result? If not, feel free to remove it. - "### - ); - test_report!( return_outside_of_function, indoc!( @@ -14691,7 +14660,7 @@ All branches in an `if` must have the same type! ); test_report!( - function_def_fx_no_bang, + leftover_statement, indoc!( r#" app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } @@ -14699,25 +14668,63 @@ All branches in an `if` must have the same type! import pf.Effect main! = \{} -> - printHello {} + identity {} - printHello = \{} -> Effect.putLine! "hello" + + identity = \x -> x "# ), @r###" - ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + ── LEFTOVER STATEMENT in /code/proj/Main.roc ─────────────────────────────────── - This function is effectful, but its name does not indicate so: + This statement does not produce any effects: - 8│ printHello = \{} -> - ^^^^^^^^^^ + 6│ identity {} + ^^^^^^^^^^^ - Add an exclamation mark at the end of its name, like: + Standalone statements are only useful if they call effectful + functions. - printHello! + Did you forget to use its result? If not, feel free to remove it. + "### + ); - This will help readers identify it as a source of effects. + test_report!( + fx_fn_annotated_as_pure, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + Effect.putLine! (getCheer "hello") + + getCheer : Str -> Str + getCheer = \msg -> + name = Effect.getLine! {} + + "$(msg), $(name)!" + "# + ), + @r###" + ── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ────────────────────────────── + + This expression calls an effectful function: + + 10│ name = Effect.getLine! {} + ^^^^^^^^^^^^^^^^^^ + + However, the type of the enclosing function indicates it must be pure: + + 8│ getCheer : Str -> Str + ^^^^^^^^^^ + + Tip: Replace `->` with `=>` to annotate it as effectful. + + You can still run the program with this error, which can be helpful + when you're debugging. "### ); @@ -14809,4 +14816,6 @@ All branches in an `if` must have the same type! This will help readers identify it as a source of effects. "### ); + + // [purity-inference] TODO: check ! in records, tuples, tags, opaques, and arguments } diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index a2876167185..5e979c905d4 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -351,7 +351,9 @@ pub fn type_problem<'b>( let stack = [ alloc.reflow("This function is pure, but its name suggests otherwise:"), alloc.region(lines.convert_region(region), severity), - alloc.reflow("Remove the exclamation mark to give an accurate impression of its behavior."), + alloc.reflow( + "Remove the exclamation mark to give an accurate impression of its behavior.", + ), ]; Some(Report { @@ -1692,6 +1694,7 @@ fn to_expr_report<'b>( unimplemented!("record default field is not implemented yet") } Reason::ImportParams(_) => unreachable!(), + Reason::FunctionOutput => { let problem = alloc.concat([ alloc.text("This "), @@ -1726,7 +1729,40 @@ fn to_expr_report<'b>( severity, } } - Reason::CallInFunction(_) => todo!("[purity-inference] CallInFunction"), + + Reason::CallInFunction(ann_region) => { + let lines = [ + alloc.reflow("This expression calls an effectful function:"), + alloc.region(lines.convert_region(region), severity), + match ann_region { + Some(ann_region) => alloc.stack([ + alloc.reflow( + "However, the type of the enclosing function indicates it must be pure:", + ), + alloc.region(lines.convert_region(ann_region), Severity::Warning), + alloc.concat([ + alloc.tip(), + alloc.text("Replace "), + alloc.keyword("->"), + alloc.text(" with "), + alloc.keyword("=>"), + alloc.text(" to annotate it as effectful."), + ]), + ]), + None => { + alloc.reflow("However, the enclosing function is required to be pure.") + } + }, + alloc.reflow("You can still run the program with this error, which can be helpful when you're debugging."), + ]; + + Report { + filename, + title: "EFFECT IN PURE FUNCTION".to_string(), + doc: alloc.stack(lines), + severity, + } + } Reason::CallInTopLevelDef => todo!("[purity-inference] CallInTopLevelDef"), Reason::Stmt => todo!("[purity-inference] Stmt"), }, From b62665e49e859fa4004caadcd2818987e9dfd0f3 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 19:05:26 -0300 Subject: [PATCH 31/73] Report effectful statement in pure function --- crates/compiler/constrain/src/expr.rs | 63 ++++++++++---------- crates/compiler/load/tests/test_reporting.rs | 39 +++++++++++- crates/reporting/src/error/type.rs | 2 +- 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index ff3a124ad47..02c09deb8ae 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -601,24 +601,6 @@ pub fn constrain_expr( let category = Category::CallResult(opt_symbol, *called_via); - let fx_expected_type = match env.enclosing_fx { - Some(enclosing_fn) => { - let enclosing_fx_index = constraints.push_variable(enclosing_fn.fx_var); - - constraints.push_expected_type(ForReason( - Reason::CallInFunction(enclosing_fn.ann_region), - enclosing_fx_index, - region, - )) - } - None => constraints.push_expected_type(ForReason( - Reason::CallInTopLevelDef, - // top-level defs are only allowed to call pure functions - constraints.push_variable(Variable::PURE), - region, - )), - }; - let and_cons = [ fn_con, constraints.equal_types_var(*fn_var, expected_fn_type, category.clone(), fn_region), @@ -629,7 +611,7 @@ pub fn constrain_expr( category.clone(), region, ), - constraints.equal_types_var(*fx_var, fx_expected_type, category, region), + constrain_call_fx(env, constraints, region, *fx_var, category), ]; let and_constraint = constraints.and_constraint(and_cons); @@ -1891,6 +1873,34 @@ pub fn constrain_expr( } } +fn constrain_call_fx( + env: &mut Env, + constraints: &mut Constraints, + region: Region, + fx_var: Variable, + category: Category, +) -> Constraint { + let fx_expected_type = match env.enclosing_fx { + Some(enclosing_fn) => { + let enclosing_fx_index = constraints.push_variable(enclosing_fn.fx_var); + + constraints.push_expected_type(ForReason( + Reason::CallInFunction(enclosing_fn.ann_region), + enclosing_fx_index, + region, + )) + } + None => constraints.push_expected_type(ForReason( + Reason::CallInTopLevelDef, + // top-level defs are only allowed to call pure functions + constraints.push_variable(Variable::PURE), + region, + )), + }; + + constraints.equal_types_var(fx_var, fx_expected_type, category, region) +} + fn constrain_function_def( types: &mut Types, constraints: &mut Constraints, @@ -3517,21 +3527,8 @@ fn constrain_stmt_def( // We have to unify the stmt fx with the enclosing fx // since we used the former to constrain the expr. - let enclosing_fx_index = match env.enclosing_fx { - Some(enclosing_fn) => { - let enclosing_fx_index = constraints.push_variable(enclosing_fn.fx_var); - - constraints.push_expected_type(ForReason(Reason::Stmt, enclosing_fx_index, region)) - } - None => constraints.push_expected_type(ForReason( - // Statements are not allowed in top-level defs - Reason::Stmt, - constraints.push_variable(Variable::PURE), - region, - )), - }; let enclosing_fx_constraint = - constraints.equal_types_var(fx_var, enclosing_fx_index, Category::Unknown, region); + constrain_call_fx(env, constraints, region, fx_var, Category::Unknown); constraints.and_constraint([body_con, effectful_constraint, enclosing_fx_constraint]) } diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 19ee90de450..0fd7523edf2 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14716,7 +14716,7 @@ All branches in an `if` must have the same type! 10│ name = Effect.getLine! {} ^^^^^^^^^^^^^^^^^^ - However, the type of the enclosing function indicates it must be pure: + However, the type of the enclosing function requires that it's pure: 8│ getCheer : Str -> Str ^^^^^^^^^^ @@ -14728,6 +14728,43 @@ All branches in an `if` must have the same type! "### ); + test_report!( + fx_fn_annotated_as_pure_stmt, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + trim "hello " + + trim : Str -> Str + trim = \msg -> + Effect.putLine! "Trimming $(msg)" + Str.trim msg + "# + ), + @r###" + ── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ────────────────────────────── + + This expression calls an effectful function: + + 10│ Effect.putLine! "Trimming $(msg)" + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + However, the type of the enclosing function requires that it's pure: + + 8│ trim : Str -> Str + ^^^^^^^^^^ + + Tip: Replace `->` with `=>` to annotate it as effectful. + + You can still run the program with this error, which can be helpful + when you're debugging. + "### + ); + test_report!( nested_function_def_fx_no_bang, indoc!( diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 5e979c905d4..412b14bc4d3 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1737,7 +1737,7 @@ fn to_expr_report<'b>( match ann_region { Some(ann_region) => alloc.stack([ alloc.reflow( - "However, the type of the enclosing function indicates it must be pure:", + "However, the type of the enclosing function requires that it's pure:", ), alloc.region(lines.convert_region(ann_region), Severity::Warning), alloc.concat([ From f666dba67d363f3c0a91d6e8dc17e4fcca31471a Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 19:29:08 -0300 Subject: [PATCH 32/73] Report effectful top-level exprs --- crates/compiler/constrain/src/expr.rs | 2 +- crates/compiler/load/tests/test_reporting.rs | 38 +++++++++++++++++++ .../compiler/lower_params/src/type_error.rs | 2 +- crates/compiler/types/src/types.rs | 2 +- crates/reporting/src/error/type.rs | 21 +++++++++- 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 02c09deb8ae..a83750ebe61 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -1891,7 +1891,7 @@ fn constrain_call_fx( )) } None => constraints.push_expected_type(ForReason( - Reason::CallInTopLevelDef, + Reason::CallInTopLevel, // top-level defs are only allowed to call pure functions constraints.push_variable(Variable::PURE), region, diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 0fd7523edf2..889f9f9c85e 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14824,6 +14824,44 @@ All branches in an `if` must have the same type! "### ); + test_report!( + effect_in_top_level_value_def, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + hello = + Effect.putLine! "calling hello!" + "hello" + + main! = \{} -> + Effect.putLine! hello + "# + ), + @r###" + ── EFFECT IN TOP-LEVEL in /code/proj/Main.roc ────────────────────────────────── + + This top-level expression calls an effectful function: + + 6│ Effect.putLine! "calling hello!" + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + However, only functions are allowed to be effectful. This limitation + ensures that importing a module never produces a side effect. + + Tip: If you don't need any arguments, use an empty record: + + askName! : {} => Str + askName! = \{} -> + Stdout.line! "What's your name?" + Stdin.line! {} + + This will allow the caller to control when the effect runs. + "### + ); + test_report!( aliased_fx_fn, indoc!( diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 6cb79ad4c8d..534ddf8b6d8 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -186,7 +186,7 @@ fn remove_for_reason( | Reason::CrashArg | Reason::ImportParams(_) | Reason::CallInFunction(_) - | Reason::CallInTopLevelDef + | Reason::CallInTopLevel | Reason::Stmt | Reason::FunctionOutput => {} } diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index e8b229fa371..1cd2e1689df 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3450,7 +3450,7 @@ pub enum Reason { }, Stmt, CallInFunction(Option), - CallInTopLevelDef, + CallInTopLevel, FloatLiteral, IntLiteral, NumLiteral, diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 412b14bc4d3..62073a32b59 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1763,7 +1763,26 @@ fn to_expr_report<'b>( severity, } } - Reason::CallInTopLevelDef => todo!("[purity-inference] CallInTopLevelDef"), + Reason::CallInTopLevel => { + let lines = [ + alloc.reflow("This top-level expression calls an effectful function:"), + alloc.region(lines.convert_region(region), severity), + alloc.reflow("However, only functions are allowed to be effectful. This limitation ensures that importing a module never produces a side effect."), + alloc.concat([ + alloc.tip(), + alloc.reflow("If you don't need any arguments, use an empty record:"), + ]), + alloc.parser_suggestion(" askName! : {} => Str\n askName! = \\{} ->\n Stdout.line! \"What's your name?\"\n Stdin.line! {}"), + alloc.reflow("This will allow the caller to control when the effect runs."), + ]; + + Report { + filename, + title: "EFFECT IN TOP-LEVEL".to_string(), + doc: alloc.stack(lines), + severity, + } + } Reason::Stmt => todo!("[purity-inference] Stmt"), }, } From b01771c5ae804edc69d09c03ba39a7851e8ec547 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 19:57:47 -0300 Subject: [PATCH 33/73] Show effectful function name in mismatches --- crates/compiler/constrain/src/expr.rs | 42 +++++++++++++++---- crates/compiler/load/tests/test_reporting.rs | 14 +++---- .../compiler/lower_params/src/type_error.rs | 4 +- crates/compiler/types/src/types.rs | 10 ++++- crates/reporting/src/error/type.rs | 27 ++++++++---- 5 files changed, 72 insertions(+), 25 deletions(-) diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index a83750ebe61..92a3d0e79f6 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -29,8 +29,8 @@ use roc_region::all::{Loc, Region}; use roc_types::subs::{IllegalCycleMark, Variable}; use roc_types::types::Type::{self, *}; use roc_types::types::{ - AliasKind, AnnotationSource, Category, IndexOrField, OptAbleType, PReason, Reason, RecordField, - TypeExtension, TypeTag, Types, + AliasKind, AnnotationSource, Category, FxReason, IndexOrField, OptAbleType, PReason, Reason, + RecordField, TypeExtension, TypeTag, Types, }; use soa::{Index, Slice}; @@ -611,7 +611,14 @@ pub fn constrain_expr( category.clone(), region, ), - constrain_call_fx(env, constraints, region, *fx_var, category), + constrain_call_fx( + env, + constraints, + region, + *fx_var, + category, + FxReason::Call(opt_symbol), + ), ]; let and_constraint = constraints.and_constraint(and_cons); @@ -1879,19 +1886,20 @@ fn constrain_call_fx( region: Region, fx_var: Variable, category: Category, + fx_reason: FxReason, ) -> Constraint { let fx_expected_type = match env.enclosing_fx { Some(enclosing_fn) => { let enclosing_fx_index = constraints.push_variable(enclosing_fn.fx_var); constraints.push_expected_type(ForReason( - Reason::CallInFunction(enclosing_fn.ann_region), + Reason::FxInFunction(enclosing_fn.ann_region, fx_reason), enclosing_fx_index, region, )) } None => constraints.push_expected_type(ForReason( - Reason::CallInTopLevel, + Reason::FxInTopLevel(fx_reason), // top-level defs are only allowed to call pure functions constraints.push_variable(Variable::PURE), region, @@ -3525,10 +3533,30 @@ fn constrain_stmt_def( // Stmt expr must be effectful, otherwise it's dead code let effectful_constraint = Constraint::EffectfulStmt(fx_var, region); + // Try to extract the fn name and region if the stmt is a direct call + let (fx_reason, call_region) = if let Expr::Call(boxed, _, _) = &def.loc_expr.value { + let loc_fn_expr = &boxed.1; + + match loc_fn_expr.value { + Var(symbol, _) | ParamsVar { symbol, .. } => { + (FxReason::Call(Some(symbol)), loc_fn_expr.region) + } + _ => (FxReason::Stmt, def.loc_expr.region), + } + } else { + (FxReason::Stmt, def.loc_expr.region) + }; + // We have to unify the stmt fx with the enclosing fx // since we used the former to constrain the expr. - let enclosing_fx_constraint = - constrain_call_fx(env, constraints, region, fx_var, Category::Unknown); + let enclosing_fx_constraint = constrain_call_fx( + env, + constraints, + call_region, + fx_var, + Category::Unknown, + fx_reason, + ); constraints.and_constraint([body_con, effectful_constraint, enclosing_fx_constraint]) } diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 889f9f9c85e..c482439e0ca 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14711,7 +14711,7 @@ All branches in an `if` must have the same type! @r###" ── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ────────────────────────────── - This expression calls an effectful function: + This call to `Effect.getLine!` might produce an effect: 10│ name = Effect.getLine! {} ^^^^^^^^^^^^^^^^^^ @@ -14748,10 +14748,10 @@ All branches in an `if` must have the same type! @r###" ── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ────────────────────────────── - This expression calls an effectful function: + This call to `Effect.putLine!` might produce an effect: 10│ Effect.putLine! "Trimming $(msg)" - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ^^^^^^^^^^^^^^^ However, the type of the enclosing function requires that it's pure: @@ -14843,13 +14843,13 @@ All branches in an `if` must have the same type! @r###" ── EFFECT IN TOP-LEVEL in /code/proj/Main.roc ────────────────────────────────── - This top-level expression calls an effectful function: + This call to `Effect.putLine!` might produce an effect: 6│ Effect.putLine! "calling hello!" - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ^^^^^^^^^^^^^^^ - However, only functions are allowed to be effectful. This limitation - ensures that importing a module never produces a side effect. + However, it appears in a top-level def instead of a function. If we + allowed this, importing this module would produce a side effect. Tip: If you don't need any arguments, use an empty record: diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 534ddf8b6d8..44bbadc5913 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -185,8 +185,8 @@ fn remove_for_reason( } | Reason::CrashArg | Reason::ImportParams(_) - | Reason::CallInFunction(_) - | Reason::CallInTopLevel + | Reason::FxInFunction(_, _) + | Reason::FxInTopLevel(_) | Reason::Stmt | Reason::FunctionOutput => {} } diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 1cd2e1689df..cd1533cb1c7 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3449,8 +3449,8 @@ pub enum Reason { arg_index: HumanIndex, }, Stmt, - CallInFunction(Option), - CallInTopLevel, + FxInFunction(Option, FxReason), + FxInTopLevel(FxReason), FloatLiteral, IntLiteral, NumLiteral, @@ -3487,6 +3487,12 @@ pub enum Reason { FunctionOutput, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FxReason { + Call(Option), + Stmt, +} + #[derive(PartialEq, Eq, Debug, Clone)] pub enum Category { Lookup(Symbol), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 62073a32b59..c61c989fde7 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -20,8 +20,8 @@ use roc_solve_problem::{ use roc_std::RocDec; use roc_types::pretty_print::{Parens, WILDCARD}; use roc_types::types::{ - AbilitySet, AliasKind, Category, ErrorType, IndexOrField, PatternCategory, Polarity, Reason, - RecordField, TypeExt, + AbilitySet, AliasKind, Category, ErrorType, FxReason, IndexOrField, PatternCategory, Polarity, + Reason, RecordField, TypeExt, }; use std::path::PathBuf; use ven_pretty::{text, DocAllocator}; @@ -1730,9 +1730,9 @@ fn to_expr_report<'b>( } } - Reason::CallInFunction(ann_region) => { + Reason::FxInFunction(ann_region, fx_reason) => { let lines = [ - alloc.reflow("This expression calls an effectful function:"), + describe_fx_reason(alloc, fx_reason), alloc.region(lines.convert_region(region), severity), match ann_region { Some(ann_region) => alloc.stack([ @@ -1763,11 +1763,12 @@ fn to_expr_report<'b>( severity, } } - Reason::CallInTopLevel => { + + Reason::FxInTopLevel(fx_reason) => { let lines = [ - alloc.reflow("This top-level expression calls an effectful function:"), + describe_fx_reason(alloc, fx_reason), alloc.region(lines.convert_region(region), severity), - alloc.reflow("However, only functions are allowed to be effectful. This limitation ensures that importing a module never produces a side effect."), + alloc.reflow("However, it appears in a top-level def instead of a function. If we allowed this, importing this module would produce a side effect."), alloc.concat([ alloc.tip(), alloc.reflow("If you don't need any arguments, use an empty record:"), @@ -5418,3 +5419,15 @@ fn pattern_to_doc_help<'b>( } } } + +fn describe_fx_reason<'b>(alloc: &'b RocDocAllocator<'b>, reason: FxReason) -> RocDocBuilder<'b> { + match reason { + FxReason::Call(Some(name)) => alloc.concat([ + alloc.reflow("This call to "), + alloc.symbol_qualified(name), + alloc.reflow(" might produce an effect:"), + ]), + FxReason::Call(None) => alloc.reflow("This expression calls an effectful function:"), + FxReason::Stmt => alloc.reflow("This statement calls an effectful function:"), + } +} From 6adc6d9168927b08ad134a7e3da1b6ab232ccb86 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 20:27:37 -0300 Subject: [PATCH 34/73] Report ignored statement results --- crates/compiler/constrain/src/expr.rs | 36 +++++++++++-------- crates/compiler/load/tests/test_reporting.rs | 32 +++++++++++++++++ .../compiler/lower_params/src/type_error.rs | 2 +- crates/compiler/types/src/types.rs | 2 +- crates/reporting/src/error/type.rs | 33 ++++++++++++++++- 5 files changed, 87 insertions(+), 18 deletions(-) diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 92a3d0e79f6..538203d8ede 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -3501,10 +3501,25 @@ fn constrain_stmt_def( ) -> Constraint { let region = def.loc_expr.region; + // Try to extract the fn name and region if the stmt is a call to a named function + let (fn_name, error_region) = if let Expr::Call(boxed, _, _) = &def.loc_expr.value { + let loc_fn_expr = &boxed.1; + + match loc_fn_expr.value { + Var(symbol, _) | ParamsVar { symbol, .. } => (Some(symbol), loc_fn_expr.region), + _ => (None, def.loc_expr.region), + } + } else { + (None, def.loc_expr.region) + }; + // Statement expressions must return an empty record let empty_record_index = constraints.push_type(types, Types::EMPTY_RECORD); - let expect_empty_record = - constraints.push_expected_type(ForReason(Reason::Stmt, empty_record_index, region)); + let expect_empty_record = constraints.push_expected_type(ForReason( + Reason::Stmt(fn_name), + empty_record_index, + error_region, + )); let expr_con = env.with_enclosing_fx(fx_var, None, |env| { constrain_expr( @@ -3533,18 +3548,9 @@ fn constrain_stmt_def( // Stmt expr must be effectful, otherwise it's dead code let effectful_constraint = Constraint::EffectfulStmt(fx_var, region); - // Try to extract the fn name and region if the stmt is a direct call - let (fx_reason, call_region) = if let Expr::Call(boxed, _, _) = &def.loc_expr.value { - let loc_fn_expr = &boxed.1; - - match loc_fn_expr.value { - Var(symbol, _) | ParamsVar { symbol, .. } => { - (FxReason::Call(Some(symbol)), loc_fn_expr.region) - } - _ => (FxReason::Stmt, def.loc_expr.region), - } - } else { - (FxReason::Stmt, def.loc_expr.region) + let fx_reason = match fn_name { + None => FxReason::Stmt, + Some(name) => FxReason::Call(Some(name)), }; // We have to unify the stmt fx with the enclosing fx @@ -3552,7 +3558,7 @@ fn constrain_stmt_def( let enclosing_fx_constraint = constrain_call_fx( env, constraints, - call_region, + error_region, fx_var, Category::Unknown, fx_reason, diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index c482439e0ca..1ffd0db3e09 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14796,6 +14796,38 @@ All branches in an `if` must have the same type! "### ); + test_report!( + ignored_result_stmt, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + Effect.getLine! {} + {} + "# + ), + @r###" + ── IGNORED RESULT in /code/proj/Main.roc ─────────────────────────────────────── + + The result of this call to `Effect.getLine!` is ignored: + + 6│ Effect.getLine! {} + ^^^^^^^^^^^^^^^ + + Standalone statements are required to produce an empty record, but the + type of this one is: + + Str + + If you still want to ignore it, assign it to `_`, like this: + + _ = File.delete! "data.json" + "### + ); + test_report!( function_def_leftover_bang, indoc!( diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 44bbadc5913..42181085bc6 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -187,7 +187,7 @@ fn remove_for_reason( | Reason::ImportParams(_) | Reason::FxInFunction(_, _) | Reason::FxInTopLevel(_) - | Reason::Stmt + | Reason::Stmt(_) | Reason::FunctionOutput => {} } } diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index cd1533cb1c7..b9c0892a9dd 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3448,7 +3448,7 @@ pub enum Reason { foreign_symbol: ForeignSymbol, arg_index: HumanIndex, }, - Stmt, + Stmt(Option), FxInFunction(Option, FxReason), FxInTopLevel(FxReason), FloatLiteral, diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index c61c989fde7..46dbbe0f053 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1784,7 +1784,38 @@ fn to_expr_report<'b>( severity, } } - Reason::Stmt => todo!("[purity-inference] Stmt"), + Reason::Stmt(opt_name) => { + let diff = to_diff(alloc, Parens::Unnecessary, found, expected_type); + + let lines = [ + match opt_name { + None => alloc.reflow("The result of this expression is ignored:"), + Some(fn_name) => alloc.concat([ + alloc.reflow("The result of this call to "), + alloc.symbol_qualified(fn_name), + alloc.reflow(" is ignored:"), + ]), + }, + alloc.region(lines.convert_region(region), severity), + alloc.reflow("Standalone statements are required to produce an empty record, but the type of this one is:"), + alloc.type_block(type_with_able_vars(alloc, diff.left, diff.left_able)), + alloc.concat([ + alloc.reflow("If you still want to ignore it, assign it to "), + alloc.keyword("_"), + alloc.reflow(", like this:"), + ]), + alloc + .parser_suggestion("_ = File.delete! \"data.json\"") + .indent(4), + ]; + + Report { + filename, + title: "IGNORED RESULT".to_string(), + doc: alloc.stack(lines), + severity, + } + } }, } } From 9a5a5c3462ed8e83bb7cf5188e702cc4ca64069b Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 20:27:59 -0300 Subject: [PATCH 35/73] Remove irrelevant todos --- crates/compiler/types/src/subs.rs | 5 +---- crates/glue/src/types.rs | 1 - crates/language_server/src/analysis/completion.rs | 1 - crates/reporting/src/error/canonicalize.rs | 1 - 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index 871b2cef1b3..a3bcd63a549 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -5323,10 +5323,7 @@ fn is_registered(content: &Content) -> bool { | Content::RigidAbleVar(..) => false, Content::Structure(FlatType::EmptyRecord | FlatType::EmptyTagUnion) => false, Content::ErasedLambda => false, - Content::Pure | Content::Effectful => { - // [purity-inference] TODO - false - } + Content::Pure | Content::Effectful => false, Content::Structure(_) | Content::RecursionVar { .. } diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index c7fdcb9befe..521df30814f 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -1237,7 +1237,6 @@ impl<'a> Env<'a> { match self.subs.get_content_without_compacting(var) { Content::Structure(FlatType::Func(args, closure_var, ret_var, _fx_var)) => { - // [purity-inference] TODO: fx var // this is a toplevel type, so the closure must be empty let is_toplevel = true; add_function_type( diff --git a/crates/language_server/src/analysis/completion.rs b/crates/language_server/src/analysis/completion.rs index 6b61670c113..bf01e955a26 100644 --- a/crates/language_server/src/analysis/completion.rs +++ b/crates/language_server/src/analysis/completion.rs @@ -239,7 +239,6 @@ fn make_completion_item( let typ = match subs.get(var).content { roc_types::subs::Content::Structure(var) => match var { roc_types::subs::FlatType::Apply(_, _) => CompletionItemKind::FUNCTION, - // [purity-inference] TODO: Categorize by purity? roc_types::subs::FlatType::Func(_, _, _, _) => CompletionItemKind::FUNCTION, roc_types::subs::FlatType::EmptyTagUnion | roc_types::subs::FlatType::TagUnion(_, _) => CompletionItemKind::ENUM, diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 73a86295776..3a848332c8e 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1403,7 +1403,6 @@ pub fn can_problem<'b>( } Problem::StmtAfterExpr(region) => { - // TODO: Update when [purity-inference] is fully implemented doc = alloc.stack([ alloc .reflow(r"I just finished parsing an expression with a series of definitions,"), From 31bc3670df9946c8d73e4382a45a112cbdf5e4d8 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 21:14:21 -0300 Subject: [PATCH 36/73] Ignore errors in statement checks --- crates/compiler/solve/src/solve.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 5838e3a0e02..5611fcfc6e8 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -790,7 +790,7 @@ fn solve( state } - Content::Effectful => state, + Content::Effectful | Content::Error => state, Content::RigidVar(_) | Content::FlexAbleVar(_, _) | Content::RigidAbleVar(_, _) @@ -799,8 +799,7 @@ fn solve( | Content::ErasedLambda | Content::Structure(_) | Content::Alias(_, _, _, _) - | Content::RangedNumber(_) - | Content::Error => { + | Content::RangedNumber(_) => { internal_error!("ExpectEffectful: unexpected content: {:?}", content) } } From 025600c6a78ab56cd6a7e9a87dda2e9adb3bf9e7 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 16 Oct 2024 22:12:22 -0300 Subject: [PATCH 37/73] Fix non-sensical error message --- crates/compiler/mono/src/layout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index b45a69d77f5..7c64dcf801a 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -515,7 +515,7 @@ impl<'a> RawFunctionLayout<'a> { internal_error!("lambda set should only appear under a function, where it's handled independently."); } ErasedLambda => internal_error!("erased lambda type should only appear under a function, where it's handled independently"), - Pure|Effectful => internal_error!("fx vars should not be pure or effectful"), + Pure | Effectful => internal_error!("fx vars should only appear under a function"), Structure(flat_type) => Self::layout_from_flat_type(env, flat_type), RangedNumber(..) => Layout::new_help(env, var, content).then(Self::ZeroArgumentThunk), From 28f35edb2c91da4be34456d2bec060bcd6c03588 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 17 Oct 2024 22:30:40 -0300 Subject: [PATCH 38/73] Add Pure/Effectful content to checkmate --- crates/compiler/checkmate/schema.json | 32 +++++++++++++++++++++ crates/compiler/checkmate/src/convert.rs | 8 +++--- crates/compiler/checkmate_schema/src/lib.rs | 3 ++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/crates/compiler/checkmate/schema.json b/crates/compiler/checkmate/schema.json index 874d79a828f..2944d379f78 100644 --- a/crates/compiler/checkmate/schema.json +++ b/crates/compiler/checkmate/schema.json @@ -315,6 +315,7 @@ "type": "object", "required": [ "arguments", + "fx", "lambda_type", "ret", "type" @@ -326,6 +327,9 @@ "$ref": "#/definitions/Variable" } }, + "fx": { + "$ref": "#/definitions/Variable" + }, "lambda_type": { "$ref": "#/definitions/Variable" }, @@ -528,6 +532,34 @@ } } }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Pure" + ] + } + } + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Effectful" + ] + } + } + }, { "type": "object", "required": [ diff --git a/crates/compiler/checkmate/src/convert.rs b/crates/compiler/checkmate/src/convert.rs index 854613dfdd1..2e1dcd261e7 100644 --- a/crates/compiler/checkmate/src/convert.rs +++ b/crates/compiler/checkmate/src/convert.rs @@ -77,8 +77,8 @@ impl AsSchema for subs::Content { } => B::Recursive(opt_name.as_schema(subs), structure.as_schema(subs)), A::LambdaSet(lambda_set) => lambda_set.as_schema(subs), A::ErasedLambda => B::ErasedLambda(), - A::Pure => todo!("[purity-inference] checkmate"), - A::Effectful => todo!("[purity-inference] checkmate"), + A::Pure => B::Pure(), + A::Effectful => B::Effectful(), A::Structure(flat_type) => flat_type.as_schema(subs), A::Alias(name, type_vars, real_var, kind) => B::Alias( name.as_schema(subs), @@ -98,11 +98,11 @@ impl AsSchema for subs::FlatType { subs::FlatType::Apply(symbol, variables) => { Content::Apply(symbol.as_schema(subs), variables.as_schema(subs)) } - subs::FlatType::Func(arguments, closure, ret, _fx) => Content::Function( + subs::FlatType::Func(arguments, closure, ret, fx) => Content::Function( arguments.as_schema(subs), closure.as_schema(subs), ret.as_schema(subs), - // [purity-inference] TODO: checkmate + fx.as_schema(subs), ), subs::FlatType::Record(fields, ext) => { Content::Record(fields.as_schema(subs), ext.as_schema(subs)) diff --git a/crates/compiler/checkmate_schema/src/lib.rs b/crates/compiler/checkmate_schema/src/lib.rs index c34aa01ac81..f1d5ff3ce43 100644 --- a/crates/compiler/checkmate_schema/src/lib.rs +++ b/crates/compiler/checkmate_schema/src/lib.rs @@ -73,6 +73,7 @@ impl_content! { arguments: Vec, lambda_type: Variable, ret: Variable, + fx: Variable, }, Record { fields: HashMap, @@ -101,6 +102,8 @@ impl_content! { RangedNumber { range: NumericRange, }, + Pure {}, + Effectful {}, Error {}, } From 8a6561770423bb724980a439c4afac0d58e71092 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 17 Oct 2024 22:38:23 -0300 Subject: [PATCH 39/73] Effectful function in docs --- crates/compiler/load_internal/src/docs.rs | 7 ++++--- crates/docs/src/lib.rs | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/compiler/load_internal/src/docs.rs b/crates/compiler/load_internal/src/docs.rs index f9b78d7cd86..a2dd011baab 100644 --- a/crates/compiler/load_internal/src/docs.rs +++ b/crates/compiler/load_internal/src/docs.rs @@ -4,8 +4,8 @@ use roc_can::scope::Scope; use roc_collections::VecSet; use roc_module::ident::ModuleName; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; -use roc_parse::ast::AssignedField; use roc_parse::ast::{self, ExtractSpaces, TypeHeader}; +use roc_parse::ast::{AssignedField, FunctionArrow}; use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef}; // Documentation generation requirements @@ -53,6 +53,7 @@ pub enum TypeAnnotation { }, Function { args: Vec, + arrow: FunctionArrow, output: Box, }, ObscuredTagUnion, @@ -615,8 +616,7 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> ast::TypeAnnotation::SpaceAfter(&sub_type_ann, _) => { type_to_docs(in_func_type_ann, sub_type_ann) } - ast::TypeAnnotation::Function(ast_arg_anns, _arrow, output_ann) => { - // [purity-inference] TODO: arrow + ast::TypeAnnotation::Function(ast_arg_anns, arrow, output_ann) => { let mut doc_arg_anns = Vec::new(); for ast_arg_ann in ast_arg_anns { @@ -625,6 +625,7 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> Function { args: doc_arg_anns, + arrow, output: Box::new(type_to_docs(true, output_ann.value)), } } diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs index e9d70a46df1..ab0d39a5f1c 100644 --- a/crates/docs/src/lib.rs +++ b/crates/docs/src/lib.rs @@ -10,6 +10,7 @@ use roc_load::docs::{ModuleDocumentation, RecordField}; use roc_load::{ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading}; use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_packaging::cache::{self, RocCacheDir}; +use roc_parse::ast::FunctionArrow; use roc_parse::ident::{parse_ident, Accessor, Ident}; use roc_parse::keyword; use roc_parse::state::State; @@ -827,7 +828,11 @@ fn type_annotation_to_html( type_annotation_to_html(indent_level, buf, extension, true); } - TypeAnnotation::Function { args, output } => { + TypeAnnotation::Function { + args, + arrow, + output, + } => { let mut paren_is_open = false; let mut peekable_args = args.iter().peekable(); @@ -858,7 +863,10 @@ fn type_annotation_to_html( buf.push(' '); } - buf.push_str("-> "); + match arrow { + FunctionArrow::Effectful => buf.push_str("=> "), + FunctionArrow::Pure => buf.push_str("-> "), + } let mut next_indent_level = indent_level; @@ -1029,9 +1037,11 @@ fn should_be_multiline(type_ann: &TypeAnnotation) -> bool { .iter() .any(|tag| tag.values.iter().any(should_be_multiline)) } - TypeAnnotation::Function { args, output } => { - args.len() > 2 || should_be_multiline(output) || args.iter().any(should_be_multiline) - } + TypeAnnotation::Function { + args, + arrow: _, + output, + } => args.len() > 2 || should_be_multiline(output) || args.iter().any(should_be_multiline), TypeAnnotation::ObscuredTagUnion => false, TypeAnnotation::ObscuredRecord => false, TypeAnnotation::BoundVariable(_) => false, From 6533e9084d8b32fda83be53e23f13957ffbe4313 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 17 Oct 2024 22:59:44 -0300 Subject: [PATCH 40/73] Add fx to ErrorType --- crates/compiler/load/tests/test_reporting.rs | 4 +-- .../compiler/lower_params/src/type_error.rs | 6 ++-- crates/compiler/types/src/subs.rs | 24 +++++++++++--- crates/compiler/types/src/types.rs | 29 +++++++++++++---- crates/glue/src/types.rs | 1 - crates/reporting/src/error/type.rs | 32 +++++++++++-------- 6 files changed, 66 insertions(+), 30 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 1ffd0db3e09..fae7d96c0d2 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -5419,9 +5419,9 @@ mod test_reporting { 6│ 2 -> 2 ^^ - Looks like you are trying to define a function. + Looks like you are trying to define a function. - In Roc, functions are always written as a lambda, like + In Roc, functions are always written as a lambda, like increment = \n -> n + 1 "# diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 42181085bc6..730f3b1fdab 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -57,8 +57,8 @@ pub fn remove_module_param_arguments( drop_last_argument(expected); if let ( - ErrorType::Function(found_args, _, _), - ErrorType::Function(expected_args, _, _), + ErrorType::Function(found_args, _, _, _), + ErrorType::Function(expected_args, _, _, _), ) = (found, expected) { if found_args.len() > expected_args.len() { @@ -194,7 +194,7 @@ fn remove_for_reason( fn drop_last_argument(err_type: &mut ErrorType) { match err_type { - ErrorType::Function(arguments, _, _) => { + ErrorType::Function(arguments, _, _, _) => { arguments.pop(); } // Irrelevant diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index a3bcd63a549..587e545898d 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -1,7 +1,7 @@ #![deny(unsafe_op_in_unsafe_fn)] use crate::types::{ - name_type_var, AbilitySet, AliasKind, ErrorType, ExtImplicitOpenness, Polarity, RecordField, - RecordFieldsError, TupleElemsError, TypeExt, Uls, + name_type_var, AbilitySet, AliasKind, ErrorFunctionFx, ErrorType, ExtImplicitOpenness, + Polarity, RecordField, RecordFieldsError, TupleElemsError, TypeExt, Uls, }; use crate::unification_table::{self, UnificationTable}; use roc_collections::all::{FnvMap, ImMap, ImSet, MutSet, SendMap}; @@ -4206,7 +4206,7 @@ fn flat_type_to_err_type( ErrorType::Type(symbol, arg_types) } - Func(arg_vars, closure_var, ret_var, _fx_var) => { + Func(arg_vars, closure_var, ret_var, fx_var) => { let args = arg_vars .into_iter() .map(|index| { @@ -4217,9 +4217,23 @@ fn flat_type_to_err_type( let ret = var_to_err_type(subs, state, ret_var, Polarity::Pos); let closure = var_to_err_type(subs, state, closure_var, pol); + let fx = match subs.get_content_without_compacting(fx_var) { + Content::Pure | Content::FlexVar(_) | Content::Error => ErrorFunctionFx::Pure, + Content::Effectful => ErrorFunctionFx::Effectful, + Content::RigidVar(_) + | Content::FlexAbleVar(_, _) + | Content::RigidAbleVar(_, _) + | Content::RecursionVar { .. } + | Content::LambdaSet(_) + | Content::ErasedLambda + | Content::Structure(_) + | Content::Alias(_, _, _, _) + | Content::RangedNumber(_) => { + internal_error!("Unexpected content in fx var") + } + }; - // [purity-inference] TODO: add fx var to the error type - ErrorType::Function(args, Box::new(closure), Box::new(ret)) + ErrorType::Function(args, Box::new(closure), fx, Box::new(ret)) } EmptyRecord => ErrorType::Record(SendMap::default(), TypeExt::Closed), diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index b9c0892a9dd..95a38232848 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3680,12 +3680,23 @@ pub enum ErrorType { TypeExt, Polarity, ), - Function(Vec, Box, Box), + Function( + Vec, + Box, + ErrorFunctionFx, + Box, + ), Alias(Symbol, Vec, Box, AliasKind), Range(Vec), Error, } +#[derive(PartialEq, Eq, Clone, Hash)] +pub enum ErrorFunctionFx { + Pure, + Effectful, +} + impl std::fmt::Debug for ErrorType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // TODO remove clone @@ -3731,7 +3742,7 @@ impl ErrorType { .for_each(|(_, ts)| ts.iter().for_each(|t| t.add_names(taken))); ext.add_names(taken); } - Function(args, capt, ret) => { + Function(args, capt, _fx, ret) => { args.iter().for_each(|t| t.add_names(taken)); capt.add_names(taken); ret.add_names(taken); @@ -3817,7 +3828,7 @@ fn write_error_type_help( } } } - Function(arguments, _closure, result) => { + Function(arguments, _closure, fx, result) => { let write_parens = parens != Parens::Unnecessary; if write_parens { @@ -3833,7 +3844,10 @@ fn write_error_type_help( } } - buf.push_str(" -> "); + match fx { + ErrorFunctionFx::Pure => buf.push_str(" -> "), + ErrorFunctionFx::Effectful => buf.push_str(" => "), + } write_error_type_help(interns, *result, buf, Parens::InFn); @@ -3967,7 +3981,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: buf.push(')'); } } - Function(arguments, _closure, result) => { + Function(arguments, _closure, fx, result) => { let write_parens = parens != Parens::Unnecessary; if write_parens { @@ -3983,7 +3997,10 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: } } - buf.push_str(" -> "); + match fx { + ErrorFunctionFx::Pure => buf.push_str(" -> "), + ErrorFunctionFx::Effectful => buf.push_str(" => "), + } write_debug_error_type_help(*result, buf, Parens::InFn); diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index 521df30814f..b8ec3334f91 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -1406,7 +1406,6 @@ fn add_type_help<'a>( } }, Content::Structure(FlatType::Func(args, closure_var, ret_var, _fx_var)) => { - // [purity-inference] TODO: fx var let is_toplevel = false; // or in any case, we cannot assume that we are add_function_type( diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 46dbbe0f053..10dbca38bec 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1846,7 +1846,7 @@ fn describe_wanted_function(tipe: &ErrorType) -> DescribedFunction { use ErrorType::*; match tipe { - Function(args, _, _) => DescribedFunction::Arguments(args.len()), + Function(args, _, _, _) => DescribedFunction::Arguments(args.len()), Alias(_, _, actual, AliasKind::Structural) => describe_wanted_function(actual), Alias(_, _, actual, AliasKind::Opaque) => { let tag = if matches!( @@ -2698,12 +2698,13 @@ fn to_doc_help<'b>( use ErrorType::*; match tipe { - Function(args, _, ret) => report_text::function( + Function(args, _, fx, ret) => report_text::function( alloc, parens, args.into_iter() .map(|arg| to_doc_help(ctx, gen_usages, alloc, Parens::InFn, arg)) .collect(), + fx, to_doc_help(ctx, gen_usages, alloc, Parens::InFn, *ret), ), Infinite => alloc.text("∞"), @@ -2940,7 +2941,7 @@ fn count_generated_name_usages<'a>( stack.extend(tags.values().flatten().map(|t| (t, only_unseen))); ext_stack.push((ext, only_unseen)); } - Function(args, _lset, ret) => { + Function(args, _lset, _fx, ret) => { stack.extend(args.iter().map(|t| (t, only_unseen))); stack.push((ret, only_unseen)); } @@ -3115,7 +3116,7 @@ fn to_diff<'b>( } } - (Function(args1, _, ret1), Function(args2, _, ret2)) => { + (Function(args1, _, fx1, ret1), Function(args2, _, fx2, ret2)) => { if args1.len() == args2.len() { let mut status = Status::Similar; let arg_diff = diff_args(alloc, Parens::InFn, args1, args2); @@ -3123,8 +3124,8 @@ fn to_diff<'b>( status.merge(arg_diff.status); status.merge(ret_diff.status); - let left = report_text::function(alloc, parens, arg_diff.left, ret_diff.left); - let right = report_text::function(alloc, parens, arg_diff.right, ret_diff.right); + let left = report_text::function(alloc, parens, arg_diff.left, fx1, ret_diff.left); + let right = report_text::function(alloc, parens, arg_diff.right, fx2, ret_diff.right); let mut left_able = arg_diff.left_able; left_able.extend(ret_diff.left_able); let mut right_able = arg_diff.right_able; @@ -3713,9 +3714,10 @@ fn should_show_diff(t1: &ErrorType, t2: &ErrorType) -> bool { .any(|(p1, p2)| should_show_diff(p1, p2)) }) } - (Function(params1, ret1, l1), Function(params2, ret2, l2)) => { + (Function(params1, ret1, fx1, l1), Function(params2, ret2, fx2, l2)) => { if params1.len() != params2.len() || should_show_diff(ret1, ret2) + || fx1 != fx2 || should_show_diff(l1, l2) { return true; @@ -3777,8 +3779,8 @@ fn should_show_diff(t1: &ErrorType, t2: &ErrorType) -> bool { | (_, TagUnion(_, _, _)) | (RecursiveTagUnion(_, _, _, _), _) | (_, RecursiveTagUnion(_, _, _, _)) - | (Function(_, _, _), _) - | (_, Function(_, _, _)) => true, + | (Function(_, _, _, _), _) + | (_, Function(_, _, _, _)) => true, } } @@ -4236,7 +4238,7 @@ mod report_text { use crate::report::{Annotation, RocDocAllocator, RocDocBuilder}; use roc_module::ident::Lowercase; use roc_types::pretty_print::Parens; - use roc_types::types::{ErrorType, RecordField, TypeExt}; + use roc_types::types::{ErrorFunctionFx, ErrorType, RecordField, TypeExt}; use ven_pretty::DocAllocator; fn with_parens<'b>( @@ -4250,11 +4252,15 @@ mod report_text { alloc: &'b RocDocAllocator<'b>, parens: Parens, args: Vec>, + fx: ErrorFunctionFx, ret: RocDocBuilder<'b>, ) -> RocDocBuilder<'b> { let function_doc = alloc.concat([ alloc.intersperse(args, alloc.reflow(", ")), - alloc.reflow(" -> "), + match fx { + ErrorFunctionFx::Pure => alloc.text(" -> "), + ErrorFunctionFx::Effectful => alloc.text(" => "), + }, ret, ]); @@ -4820,7 +4826,7 @@ fn type_problem_to_pretty<'b>( rigid_able_vs_different_flex_able(x, abilities, other_abilities) } RigidVar(y) | RigidAbleVar(y, _) => bad_double_rigid(x, y), - Function(_, _, _) => rigid_able_vs_concrete(x, alloc.reflow("a function value")), + Function(_, _, _, _) => rigid_able_vs_concrete(x, alloc.reflow("a function value")), Record(_, _) => rigid_able_vs_concrete(x, alloc.reflow("a record value")), Tuple(_, _) => rigid_able_vs_concrete(x, alloc.reflow("a tuple value")), TagUnion(_, _, _) | RecursiveTagUnion(_, _, _, _) => { @@ -4909,7 +4915,7 @@ fn type_problem_to_pretty<'b>( bad_rigid_var(x, msg) } RigidVar(y) | RigidAbleVar(y, _) => bad_double_rigid(x, y), - Function(_, _, _) => bad_rigid_var(x, alloc.reflow("a function value")), + Function(_, _, _, _) => bad_rigid_var(x, alloc.reflow("a function value")), Record(_, _) => bad_rigid_var(x, alloc.reflow("a record value")), Tuple(_, _) => bad_rigid_var(x, alloc.reflow("a tuple value")), TagUnion(_, _, _) | RecursiveTagUnion(_, _, _, _) => { From 89a918cebe9f86e1d26e4d8d6c01b4f2af2d42b1 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 22 Oct 2024 00:07:45 -0300 Subject: [PATCH 41/73] Fix unifying pure with flex vars --- crates/compiler/can/src/constraint.rs | 49 +++++++ crates/compiler/constrain/src/expr.rs | 102 ++++---------- crates/compiler/constrain/src/module.rs | 6 +- crates/compiler/load/tests/helpers/mod.rs | 2 +- crates/compiler/load/tests/test_reporting.rs | 14 +- .../compiler/lower_params/src/type_error.rs | 4 +- crates/compiler/solve/src/solve.rs | 49 ++++++- crates/compiler/solve_problem/src/lib.rs | 11 +- crates/compiler/types/src/types.rs | 8 -- crates/compiler/unify/src/unify.rs | 3 +- crates/reporting/src/error/type.rs | 125 +++++++++--------- examples/cli/effects.roc | 11 ++ 12 files changed, 230 insertions(+), 154 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index 46e93b8ebfc..bd043fb3996 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -29,6 +29,7 @@ pub struct Constraints { pub eq: Vec, pub pattern_eq: Vec, pub cycles: Vec, + pub fx_call_constraints: Vec, } impl std::fmt::Debug for Constraints { @@ -50,6 +51,7 @@ impl std::fmt::Debug for Constraints { .field("eq", &self.eq) .field("pattern_eq", &self.pattern_eq) .field("cycles", &self.cycles) + .field("fx_call_constraints", &self.fx_call_constraints) .finish() } } @@ -81,6 +83,7 @@ impl Constraints { let eq = Vec::new(); let pattern_eq = Vec::new(); let cycles = Vec::new(); + let fx_call_constraints = Vec::with_capacity(16); categories.extend([ Category::Record, @@ -130,6 +133,7 @@ impl Constraints { eq, pattern_eq, cycles, + fx_call_constraints, } } @@ -574,6 +578,25 @@ impl Constraints { Constraint::Lookup(symbol, expected_index, region) } + pub fn fx_call( + &mut self, + call_fx_var: Variable, + call_kind: FxCallKind, + call_region: Region, + expectation: Option, + ) -> Constraint { + let constraint = FxCallConstraint { + call_fx_var, + call_kind, + call_region, + expectation, + }; + + let constraint_index = index_push_new(&mut self.fx_call_constraints, constraint); + + Constraint::FxCall(constraint_index) + } + pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool { match constraint { Constraint::SaveTheEnvironment => true, @@ -599,6 +622,7 @@ impl Constraints { | Constraint::Lookup(..) | Constraint::Pattern(..) | Constraint::EffectfulStmt(..) + | Constraint::FxCall(_) | Constraint::True | Constraint::IsOpenType(_) | Constraint::IncludesTag(_) @@ -771,6 +795,8 @@ pub enum Constraint { Index, Region, ), + /// Check call fx against enclosing function fx + FxCall(Index), /// Expect statement to be effectful EffectfulStmt(Variable, Region), /// Used for things that always unify, e.g. blanks and runtime errors @@ -845,6 +871,26 @@ pub struct Cycle { pub expr_regions: Slice, } +#[derive(Debug)] +pub struct FxCallConstraint { + pub call_fx_var: Variable, + pub call_kind: FxCallKind, + pub call_region: Region, + pub expectation: Option, +} + +#[derive(Debug, Clone, Copy)] +pub struct FxExpectation { + pub fx_var: Variable, + pub ann_region: Option, +} + +#[derive(Debug, Clone, Copy)] +pub enum FxCallKind { + Call(Option), + Stmt, +} + /// Custom impl to limit vertical space used by the debug output impl std::fmt::Debug for Constraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -861,6 +907,9 @@ impl std::fmt::Debug for Constraint { Self::Pattern(arg0, arg1, arg2, arg3) => { write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } + Self::FxCall(arg0) => { + write!(f, "CallFx({arg0:?})") + } Self::EffectfulStmt(arg0, arg1) => { write!(f, "EffectfulStmt({arg0:?}, {arg1:?})") } diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 538203d8ede..0cfe4d886e0 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -8,7 +8,8 @@ use crate::builtins::{ use crate::pattern::{constrain_pattern, PatternState}; use roc_can::annotation::IntroducedVariables; use roc_can::constraint::{ - Constraint, Constraints, ExpectedTypeIndex, Generalizable, OpportunisticResolve, TypeOrVar, + Constraint, Constraints, ExpectedTypeIndex, FxCallKind, FxExpectation, Generalizable, + OpportunisticResolve, TypeOrVar, }; use roc_can::def::{Def, DefKind}; use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext}; @@ -29,8 +30,8 @@ use roc_region::all::{Loc, Region}; use roc_types::subs::{IllegalCycleMark, Variable}; use roc_types::types::Type::{self, *}; use roc_types::types::{ - AliasKind, AnnotationSource, Category, FxReason, IndexOrField, OptAbleType, PReason, Reason, - RecordField, TypeExtension, TypeTag, Types, + AliasKind, AnnotationSource, Category, IndexOrField, OptAbleType, PReason, Reason, RecordField, + TypeExtension, TypeTag, Types, }; use soa::{Index, Slice}; @@ -59,17 +60,11 @@ pub struct Env { pub resolutions_to_make: Vec, pub home: ModuleId, /// The enclosing function's fx var to be unified with inner calls - pub enclosing_fx: Option, -} - -#[derive(Clone, Copy)] -pub struct EnclosingFx { - pub fx_var: Variable, - pub ann_region: Option, + pub fx_expectation: Option, } impl Env { - pub fn with_enclosing_fx( + pub fn with_fx_expectation( &mut self, fx_var: Variable, ann_region: Option, @@ -78,13 +73,13 @@ impl Env { where F: FnOnce(&mut Env) -> T, { - let prev = self.enclosing_fx.take(); + let prev = self.fx_expectation.take(); - self.enclosing_fx = Some(EnclosingFx { fx_var, ann_region }); + self.fx_expectation = Some(FxExpectation { fx_var, ann_region }); let result = f(self); - self.enclosing_fx = prev; + self.fx_expectation = prev; result } @@ -179,7 +174,7 @@ fn constrain_untyped_closure( loc_body_expr.region, )); - let ret_constraint = env.with_enclosing_fx(fx_var, None, |env| { + let ret_constraint = env.with_fx_expectation(fx_var, None, |env| { constrain_expr( types, constraints, @@ -611,13 +606,11 @@ pub fn constrain_expr( category.clone(), region, ), - constrain_call_fx( - env, - constraints, - region, + constraints.fx_call( *fx_var, - category, - FxReason::Call(opt_symbol), + FxCallKind::Call(opt_symbol), + region, + env.fx_expectation, ), ]; @@ -1880,35 +1873,6 @@ pub fn constrain_expr( } } -fn constrain_call_fx( - env: &mut Env, - constraints: &mut Constraints, - region: Region, - fx_var: Variable, - category: Category, - fx_reason: FxReason, -) -> Constraint { - let fx_expected_type = match env.enclosing_fx { - Some(enclosing_fn) => { - let enclosing_fx_index = constraints.push_variable(enclosing_fn.fx_var); - - constraints.push_expected_type(ForReason( - Reason::FxInFunction(enclosing_fn.ann_region, fx_reason), - enclosing_fx_index, - region, - )) - } - None => constraints.push_expected_type(ForReason( - Reason::FxInTopLevel(fx_reason), - // top-level defs are only allowed to call pure functions - constraints.push_variable(Variable::PURE), - region, - )), - }; - - constraints.equal_types_var(fx_var, fx_expected_type, category, region) -} - fn constrain_function_def( types: &mut Types, constraints: &mut Constraints, @@ -2047,7 +2011,7 @@ fn constrain_function_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], - enclosing_fx: Some(EnclosingFx { + fx_expectation: Some(FxExpectation { fx_var: function_def.fx_type, ann_region: Some(annotation.region), }), @@ -2303,7 +2267,7 @@ fn constrain_destructure_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], - enclosing_fx: env.enclosing_fx, + fx_expectation: env.fx_expectation, }; let signature_index = constraints.push_type(types, signature); @@ -2406,7 +2370,7 @@ fn constrain_value_def( home: env.home, rigids: ftv, resolutions_to_make: vec![], - enclosing_fx: env.enclosing_fx, + fx_expectation: env.fx_expectation, }; let loc_pattern = Loc::at(loc_symbol.region, Pattern::Identifier(loc_symbol.value)); @@ -2694,7 +2658,7 @@ pub fn constrain_decls( home, rigids: MutMap::default(), resolutions_to_make: vec![], - enclosing_fx: None, + fx_expectation: None, }; debug_assert_eq!(declarations.declarations.len(), declarations.symbols.len()); @@ -2926,7 +2890,7 @@ fn constrain_typed_def( home: env.home, resolutions_to_make: vec![], rigids: ftv, - enclosing_fx: env.enclosing_fx, + fx_expectation: env.fx_expectation, }; let signature_index = constraints.push_type(types, signature); @@ -3035,7 +2999,7 @@ fn constrain_typed_def( ret_type_index, )); - let ret_constraint = env.with_enclosing_fx(fx_var, Some(annotation.region), |env| { + let ret_constraint = env.with_fx_expectation(fx_var, Some(annotation.region), |env| { constrain_expr( types, constraints, @@ -3067,7 +3031,6 @@ fn constrain_typed_def( // when we check that the solved function type matches the annotation, we can // display the fully inferred return variable. constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), - constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), // Now, check the solved function type matches the annotation. constraints.equal_types( solved_fn_type, @@ -3521,7 +3484,7 @@ fn constrain_stmt_def( error_region, )); - let expr_con = env.with_enclosing_fx(fx_var, None, |env| { + let expr_con = env.with_fx_expectation(fx_var, None, |env| { constrain_expr( types, constraints, @@ -3548,21 +3511,15 @@ fn constrain_stmt_def( // Stmt expr must be effectful, otherwise it's dead code let effectful_constraint = Constraint::EffectfulStmt(fx_var, region); - let fx_reason = match fn_name { - None => FxReason::Stmt, - Some(name) => FxReason::Call(Some(name)), + let fx_call_kind = match fn_name { + None => FxCallKind::Stmt, + Some(name) => FxCallKind::Call(Some(name)), }; // We have to unify the stmt fx with the enclosing fx // since we used the former to constrain the expr. - let enclosing_fx_constraint = constrain_call_fx( - env, - constraints, - error_region, - fx_var, - Category::Unknown, - fx_reason, - ); + let enclosing_fx_constraint = + constraints.fx_call(fx_var, fx_call_kind, error_region, env.fx_expectation); constraints.and_constraint([body_con, effectful_constraint, enclosing_fx_constraint]) } @@ -4032,7 +3989,7 @@ fn constraint_recursive_function( constraints.push_type(types, typ) }; - let expr_con = env.with_enclosing_fx(fx_var, Some(annotation.region), |env| { + let expr_con = env.with_fx_expectation(fx_var, Some(annotation.region), |env| { let expected = constraints.push_expected_type(NoExpectation(ret_type_index)); constrain_expr( types, @@ -4049,6 +4006,7 @@ fn constraint_recursive_function( let state_constraints = constraints.and_constraint(argument_pattern_state.constraints); let cons = [ + constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), constraints.let_constraint( [], argument_pattern_state.vars, @@ -4063,7 +4021,6 @@ fn constraint_recursive_function( // Store type into AST vars. We use Store so errors aren't reported twice constraints.store(signature_index, expr_var, std::file!(), std::line!()), constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), - constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), closure_constraint, ]; @@ -4582,7 +4539,7 @@ fn rec_defs_help( constraints.push_type(types, typ) }; let expr_con = - env.with_enclosing_fx(fx_var, Some(annotation.region), |env| { + env.with_fx_expectation(fx_var, Some(annotation.region), |env| { let body_type = constraints.push_expected_type(NoExpectation(ret_type_index)); @@ -4630,7 +4587,6 @@ fn rec_defs_help( std::line!(), ), constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), - constraints.store(fx_type_index, fx_var, std::file!(), std::line!()), closure_constraint, ]; diff --git a/crates/compiler/constrain/src/module.rs b/crates/compiler/constrain/src/module.rs index e125fce918c..09e29b5211f 100644 --- a/crates/compiler/constrain/src/module.rs +++ b/crates/compiler/constrain/src/module.rs @@ -56,7 +56,7 @@ fn constrain_params( home, rigids: MutMap::default(), resolutions_to_make: vec![], - enclosing_fx: None, + fx_expectation: None, }; let index = constraints.push_variable(module_params.whole_var); @@ -115,7 +115,7 @@ fn constrain_symbols_from_requires( home, rigids, resolutions_to_make: vec![], - enclosing_fx: None, + fx_expectation: None, }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(loc_symbol.value)); @@ -183,7 +183,7 @@ pub fn frontload_ability_constraints( home, rigids, resolutions_to_make: vec![], - enclosing_fx: None, + fx_expectation: None, }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(*member_name)); diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index a55a4bd5362..4e3d5af447e 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -204,7 +204,7 @@ pub fn can_expr_with<'a>( rigids: MutMap::default(), home, resolutions_to_make: vec![], - enclosing_fx: None, + fx_expectation: None, }, loc_expr.region, &loc_expr.value, diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index fae7d96c0d2..85110b13fd5 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -5419,9 +5419,9 @@ mod test_reporting { 6│ 2 -> 2 ^^ - Looks like you are trying to define a function. + Looks like you are trying to define a function. - In Roc, functions are always written as a lambda, like + In Roc, functions are always written as a lambda, like increment = \n -> n + 1 "# @@ -14762,6 +14762,16 @@ All branches in an `if` must have the same type! You can still run the program with this error, which can be helpful when you're debugging. + + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This function is pure, but its name suggests otherwise: + + 5│ main! = \{} -> + ^^^^^ + + Remove the exclamation mark to give an accurate impression of its + behavior. "### ); diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 730f3b1fdab..a679a33c285 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -100,6 +100,8 @@ pub fn remove_module_param_arguments( | TypeError::UnexpectedModuleParams(_, _) | TypeError::MissingModuleParams(_, _, _) | TypeError::ModuleParamsMismatch(_, _, _, _) + | TypeError::FxInPureFunction(_, _, _) + | TypeError::FxInTopLevel(_, _) | TypeError::PureStmt(_) | TypeError::UnsuffixedEffectfulFunction(_, _) | TypeError::SuffixedPureFunction(_, _) => {} @@ -185,8 +187,6 @@ fn remove_for_reason( } | Reason::CrashArg | Reason::ImportParams(_) - | Reason::FxInFunction(_, _) - | Reason::FxInTopLevel(_) | Reason::Stmt(_) | Reason::FunctionOutput => {} } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 5611fcfc6e8..3ed8d9d566b 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -14,7 +14,7 @@ use crate::Aliases; use bumpalo::Bump; use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo}; use roc_can::constraint::Constraint::{self, *}; -use roc_can::constraint::{Cycle, LetConstraint, OpportunisticResolve}; +use roc_can::constraint::{Cycle, FxCallConstraint, LetConstraint, OpportunisticResolve}; use roc_can::expected::{Expected, PExpected}; use roc_can::module::ModuleParams; use roc_collections::VecMap; @@ -780,6 +780,53 @@ fn solve( } } } + FxCall(index) => { + let FxCallConstraint { + call_fx_var, + call_kind, + call_region, + expectation, + } = &env.constraints.fx_call_constraints[index.index()]; + + let actual_desc = env.subs.get(*call_fx_var); + + match (actual_desc.content, expectation) { + (Content::Pure, _) | (Content::FlexVar(_), _) | (Content::Error, _) => state, + (Content::Effectful, None) => { + let problem = TypeError::FxInTopLevel(*call_region, *call_kind); + problems.push(problem); + state + } + (Content::Effectful, Some(expectation)) => { + match env.subs.get_content_without_compacting(expectation.fx_var) { + Content::Effectful | Content::Error => state, + Content::FlexVar(_) => { + env.subs + .union(expectation.fx_var, *call_fx_var, actual_desc); + state + } + Content::Pure => { + let problem = TypeError::FxInPureFunction( + *call_region, + *call_kind, + expectation.ann_region, + ); + problems.push(problem); + state + } + expected_content => { + internal_error!( + "CallFx: unexpected content: {:?}", + expected_content + ) + } + } + } + actual_content => { + internal_error!("CallFx: unexpected content: {:?}", actual_content) + } + } + } EffectfulStmt(variable, region) => { let content = env.subs.get_content_without_compacting(*variable); diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 99cfbe8d717..3c46b208bcb 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -1,7 +1,10 @@ //! Provides types to describe problems that can occur during solving. use std::{path::PathBuf, str::Utf8Error}; -use roc_can::expected::{Expected, PExpected}; +use roc_can::{ + constraint::FxCallKind, + expected::{Expected, PExpected}, +}; use roc_module::{ ident::Lowercase, symbol::{ModuleId, Symbol}, @@ -39,6 +42,8 @@ pub enum TypeError { UnexpectedModuleParams(Region, ModuleId), MissingModuleParams(Region, ModuleId, ErrorType), ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType), + FxInPureFunction(Region, FxCallKind, Option), + FxInTopLevel(Region, FxCallKind), PureStmt(Region), UnsuffixedEffectfulFunction(Region, Symbol), SuffixedPureFunction(Region, Symbol), @@ -67,6 +72,8 @@ impl TypeError { TypeError::IngestedFileBadUtf8(..) => Fatal, TypeError::IngestedFileUnsupportedType(..) => Fatal, TypeError::PureStmt(..) => Warning, + TypeError::FxInPureFunction(_, _, _) => Warning, + TypeError::FxInTopLevel(_, _) => Warning, TypeError::UnsuffixedEffectfulFunction(_, _) => Warning, TypeError::SuffixedPureFunction(_, _) => Warning, } @@ -85,6 +92,8 @@ impl TypeError { | TypeError::UnexpectedModuleParams(region, ..) | TypeError::MissingModuleParams(region, ..) | TypeError::ModuleParamsMismatch(region, ..) + | TypeError::FxInPureFunction(region, _, _) + | TypeError::FxInTopLevel(region, _) | TypeError::PureStmt(region) | TypeError::UnsuffixedEffectfulFunction(region, _) | TypeError::SuffixedPureFunction(region, _) => Some(*region), diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 95a38232848..334e48b4e75 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3449,8 +3449,6 @@ pub enum Reason { arg_index: HumanIndex, }, Stmt(Option), - FxInFunction(Option, FxReason), - FxInTopLevel(FxReason), FloatLiteral, IntLiteral, NumLiteral, @@ -3487,12 +3485,6 @@ pub enum Reason { FunctionOutput, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum FxReason { - Call(Option), - Stmt, -} - #[derive(PartialEq, Eq, Debug, Clone)] pub enum Category { Lookup(Symbol), diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index f5936b4e88e..9ca4bd00555 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1149,9 +1149,8 @@ fn unify_erased_lambda( #[must_use] fn unify_pure(env: &mut Env, ctx: &Context, other: &Content) -> Outcome { match other { - Pure => merge(env, ctx, Pure), + Pure | FlexVar(_) => merge(env, ctx, Pure), Effectful => merge(env, ctx, Effectful), - FlexVar(_) => merge(env, ctx, *other), RigidVar(_) | FlexAbleVar(_, _) | RigidAbleVar(_, _) diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 10dbca38bec..7cb58fc902f 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -4,6 +4,7 @@ use crate::error::canonicalize::{to_circular_def_doc, CIRCULAR_DEF}; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder}; use itertools::EitherOrBoth; use itertools::Itertools; +use roc_can::constraint::FxCallKind; use roc_can::expected::{Expected, PExpected}; use roc_collections::all::{HumanIndex, MutSet, SendMap}; use roc_collections::VecMap; @@ -20,8 +21,8 @@ use roc_solve_problem::{ use roc_std::RocDec; use roc_types::pretty_print::{Parens, WILDCARD}; use roc_types::types::{ - AbilitySet, AliasKind, Category, ErrorType, FxReason, IndexOrField, PatternCategory, Polarity, - Reason, RecordField, TypeExt, + AbilitySet, AliasKind, Category, ErrorType, IndexOrField, PatternCategory, Polarity, Reason, + RecordField, TypeExt, }; use std::path::PathBuf; use ven_pretty::{text, DocAllocator}; @@ -314,6 +315,59 @@ pub fn type_problem<'b>( severity, }) } + FxInPureFunction(fx_call_region, fx_call_kind, ann_region) => { + let lines = [ + describe_fx_call_kind(alloc, fx_call_kind), + alloc.region(lines.convert_region(fx_call_region), severity), + match ann_region { + Some(ann_region) => alloc.stack([ + alloc.reflow( + "However, the type of the enclosing function requires that it's pure:", + ), + alloc.region(lines.convert_region(ann_region), Severity::Warning), + alloc.concat([ + alloc.tip(), + alloc.text("Replace "), + alloc.keyword("->"), + alloc.text(" with "), + alloc.keyword("=>"), + alloc.text(" to annotate it as effectful."), + ]), + ]), + None => { + alloc.reflow("However, the enclosing function is required to be pure.") + } + }, + alloc.reflow("You can still run the program with this error, which can be helpful when you're debugging."), + ]; + + Some(Report { + filename, + title: "EFFECT IN PURE FUNCTION".to_string(), + doc: alloc.stack(lines), + severity, + }) + } + FxInTopLevel(call_region, fx_call_kind) => { + let lines = [ + describe_fx_call_kind(alloc, fx_call_kind), + alloc.region(lines.convert_region(call_region), severity), + alloc.reflow("However, it appears in a top-level def instead of a function. If we allowed this, importing this module would produce a side effect."), + alloc.concat([ + alloc.tip(), + alloc.reflow("If you don't need any arguments, use an empty record:"), + ]), + alloc.parser_suggestion(" askName! : {} => Str\n askName! = \\{} ->\n Stdout.line! \"What's your name?\"\n Stdin.line! {}"), + alloc.reflow("This will allow the caller to control when the effect runs."), + ]; + + Some(Report { + filename, + title: "EFFECT IN TOP-LEVEL".to_string(), + doc: alloc.stack(lines), + severity, + }) + } PureStmt(region) => { let stack = [ alloc.reflow("This statement does not produce any effects:"), @@ -1730,60 +1784,6 @@ fn to_expr_report<'b>( } } - Reason::FxInFunction(ann_region, fx_reason) => { - let lines = [ - describe_fx_reason(alloc, fx_reason), - alloc.region(lines.convert_region(region), severity), - match ann_region { - Some(ann_region) => alloc.stack([ - alloc.reflow( - "However, the type of the enclosing function requires that it's pure:", - ), - alloc.region(lines.convert_region(ann_region), Severity::Warning), - alloc.concat([ - alloc.tip(), - alloc.text("Replace "), - alloc.keyword("->"), - alloc.text(" with "), - alloc.keyword("=>"), - alloc.text(" to annotate it as effectful."), - ]), - ]), - None => { - alloc.reflow("However, the enclosing function is required to be pure.") - } - }, - alloc.reflow("You can still run the program with this error, which can be helpful when you're debugging."), - ]; - - Report { - filename, - title: "EFFECT IN PURE FUNCTION".to_string(), - doc: alloc.stack(lines), - severity, - } - } - - Reason::FxInTopLevel(fx_reason) => { - let lines = [ - describe_fx_reason(alloc, fx_reason), - alloc.region(lines.convert_region(region), severity), - alloc.reflow("However, it appears in a top-level def instead of a function. If we allowed this, importing this module would produce a side effect."), - alloc.concat([ - alloc.tip(), - alloc.reflow("If you don't need any arguments, use an empty record:"), - ]), - alloc.parser_suggestion(" askName! : {} => Str\n askName! = \\{} ->\n Stdout.line! \"What's your name?\"\n Stdin.line! {}"), - alloc.reflow("This will allow the caller to control when the effect runs."), - ]; - - Report { - filename, - title: "EFFECT IN TOP-LEVEL".to_string(), - doc: alloc.stack(lines), - severity, - } - } Reason::Stmt(opt_name) => { let diff = to_diff(alloc, Parens::Unnecessary, found, expected_type); @@ -5457,14 +5457,17 @@ fn pattern_to_doc_help<'b>( } } -fn describe_fx_reason<'b>(alloc: &'b RocDocAllocator<'b>, reason: FxReason) -> RocDocBuilder<'b> { - match reason { - FxReason::Call(Some(name)) => alloc.concat([ +fn describe_fx_call_kind<'b>( + alloc: &'b RocDocAllocator<'b>, + kind: FxCallKind, +) -> RocDocBuilder<'b> { + match kind { + FxCallKind::Call(Some(name)) => alloc.concat([ alloc.reflow("This call to "), alloc.symbol_qualified(name), alloc.reflow(" might produce an effect:"), ]), - FxReason::Call(None) => alloc.reflow("This expression calls an effectful function:"), - FxReason::Stmt => alloc.reflow("This statement calls an effectful function:"), + FxCallKind::Call(None) => alloc.reflow("This expression calls an effectful function:"), + FxCallKind::Stmt => alloc.reflow("This statement calls an effectful function:"), } } diff --git a/examples/cli/effects.roc b/examples/cli/effects.roc index fd1b4a58a2c..01195ac59ae 100644 --- a/examples/cli/effects.roc +++ b/examples/cli/effects.roc @@ -4,6 +4,9 @@ import pf.Effect main! : {} => {} main! = \{} -> + ["Welcome!", "What's your name?"] + |> forEach! Effect.putLine! + line = Effect.getLine! {} if line == "secret" then @@ -14,3 +17,11 @@ main! = \{} -> Effect.putLine! "You entered: $(line)" Effect.putLine! "It is known" + +forEach! : List a, (a => {}) => {} +forEach! = \l, f! -> + when l is + [] -> {} + [x, .. as xs] -> + f! x + forEach! xs f! From 2859829ea844827c19b870a1393a19f8f3a9ebc8 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 22 Oct 2024 00:19:22 -0300 Subject: [PATCH 42/73] Mark flex fx vars as pure after solving body --- crates/compiler/can/src/constraint.rs | 6 ++++++ crates/compiler/constrain/src/expr.rs | 3 +++ crates/compiler/solve/src/solve.rs | 24 ++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index bd043fb3996..b39069737bc 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -623,6 +623,7 @@ impl Constraints { | Constraint::Pattern(..) | Constraint::EffectfulStmt(..) | Constraint::FxCall(_) + | Constraint::FlexToPure(_) | Constraint::True | Constraint::IsOpenType(_) | Constraint::IncludesTag(_) @@ -797,6 +798,8 @@ pub enum Constraint { ), /// Check call fx against enclosing function fx FxCall(Index), + /// Mark a function that doesn't call any effectful functions as pure + FlexToPure(Variable), /// Expect statement to be effectful EffectfulStmt(Variable, Region), /// Used for things that always unify, e.g. blanks and runtime errors @@ -913,6 +916,9 @@ impl std::fmt::Debug for Constraint { Self::EffectfulStmt(arg0, arg1) => { write!(f, "EffectfulStmt({arg0:?}, {arg1:?})") } + Self::FlexToPure(arg0) => { + write!(f, "FlexToPure({arg0:?})") + } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(), diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 0cfe4d886e0..82e6a43ee25 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -243,6 +243,7 @@ fn constrain_untyped_closure( ), early_returns_constraint, closure_constraint, + Constraint::FlexToPure(fx_var), ]; constraints.exists_many(vars, cons) @@ -3042,6 +3043,7 @@ fn constrain_typed_def( constraints.store(signature_index, *fn_var, std::file!(), std::line!()), constraints.store(signature_index, expr_var, std::file!(), std::line!()), closure_constraint, + Constraint::FlexToPure(fx_var), ]; let expr_con = constraints.exists_many(vars, cons); @@ -4022,6 +4024,7 @@ fn constraint_recursive_function( constraints.store(signature_index, expr_var, std::file!(), std::line!()), constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), closure_constraint, + Constraint::FlexToPure(fx_var), ]; let and_constraint = constraints.and_constraint(cons); diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 3ed8d9d566b..64e854c773b 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -851,6 +851,30 @@ fn solve( } } } + FlexToPure(variable) => { + let content = env.subs.get_content_without_compacting(*variable); + + match content { + Content::FlexVar(_) => { + let desc = env.subs.get(Variable::PURE); + env.subs.union(*variable, Variable::PURE, desc); + + state + } + Content::Pure | Content::Effectful | Content::Error => state, + Content::RigidVar(_) + | Content::FlexAbleVar(_, _) + | Content::RigidAbleVar(_, _) + | Content::RecursionVar { .. } + | Content::LambdaSet(_) + | Content::ErasedLambda + | Content::Structure(_) + | Content::Alias(_, _, _, _) + | Content::RangedNumber(_) => { + internal_error!("FlexToPure: unexpected content: {:?}", content) + } + } + } Let(index, pool_slice) => { let let_con = &env.constraints.let_constraints[index.index()]; From ea35094b28c57e3db6ea8c993970b8834cb95e56 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 22 Oct 2024 00:27:21 -0300 Subject: [PATCH 43/73] Remove flex var case when checking symbol suffix --- crates/compiler/load/tests/test_reporting.rs | 2 +- crates/compiler/solve/src/solve.rs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 85110b13fd5..319add6999f 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14913,7 +14913,7 @@ All branches in an `if` must have the same type! import pf.Effect main! = \{} -> - printLn "Hello!" + printLn "Hello" printLn = Effect.putLine! "# diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 64e854c773b..1932c1a824c 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1616,12 +1616,8 @@ fn check_symbol_suffix( if let Content::Structure(FlatType::Func(_, _, _, fx)) = env.subs.get_content_without_compacting(loc_var.value) { - match env.subs.get_content_without_compacting(*fx) { - // [purity-inference] TODO: Should FlexVar actually be a case? - Content::Pure | Content::FlexVar(_) => { - problems.push(TypeError::SuffixedPureFunction(loc_var.region, symbol)); - } - _ => {} + if let Content::Pure = env.subs.get_content_without_compacting(*fx) { + problems.push(TypeError::SuffixedPureFunction(loc_var.region, symbol)); } } } From 215de707fa0782367e4c2dba6c7f49fe53c7a020 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 22 Oct 2024 19:50:32 -0300 Subject: [PATCH 44/73] Report unsuffixed record literal field with effectful function --- crates/compiler/can/src/constraint.rs | 19 ++++++++- crates/compiler/constrain/src/expr.rs | 4 ++ crates/compiler/load/tests/test_reporting.rs | 32 ++++++++++++++ crates/compiler/module/src/ident.rs | 4 ++ crates/compiler/solve/src/solve.rs | 45 ++++++++++++++------ crates/compiler/solve_problem/src/lib.rs | 10 ++++- crates/reporting/src/error/type.rs | 28 ++++++++++-- 7 files changed, 121 insertions(+), 21 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index b39069737bc..d08a86f8d1c 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -6,7 +6,7 @@ use crate::abilities::SpecializationId; use crate::exhaustive::{ExhaustiveContext, SketchedRows}; use crate::expected::{Expected, PExpected}; use roc_collections::soa::{index_push_new, slice_extend_new}; -use roc_module::ident::TagName; +use roc_module::ident::{IdentSuffix, TagName}; use roc_module::symbol::{ModuleId, Symbol}; use roc_region::all::{Loc, Region}; use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, Variable}; @@ -597,6 +597,15 @@ impl Constraints { Constraint::FxCall(constraint_index) } + pub fn check_record_field_fx( + &self, + suffix: IdentSuffix, + variable: Variable, + region: Region, + ) -> Constraint { + Constraint::CheckRecordFieldFx(suffix, variable, region) + } + pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool { match constraint { Constraint::SaveTheEnvironment => true, @@ -623,6 +632,7 @@ impl Constraints { | Constraint::Pattern(..) | Constraint::EffectfulStmt(..) | Constraint::FxCall(_) + | Constraint::CheckRecordFieldFx(_, _, _) | Constraint::FlexToPure(_) | Constraint::True | Constraint::IsOpenType(_) @@ -798,10 +808,12 @@ pub enum Constraint { ), /// Check call fx against enclosing function fx FxCall(Index), - /// Mark a function that doesn't call any effectful functions as pure + /// Set an fx var as pure if flex (no effectful functions were called) FlexToPure(Variable), /// Expect statement to be effectful EffectfulStmt(Variable, Region), + /// Require field name to be accurately suffixed + CheckRecordFieldFx(IdentSuffix, Variable, Region), /// Used for things that always unify, e.g. blanks and runtime errors True, SaveTheEnvironment, @@ -919,6 +931,9 @@ impl std::fmt::Debug for Constraint { Self::FlexToPure(arg0) => { write!(f, "FlexToPure({arg0:?})") } + Self::CheckRecordFieldFx(arg0, arg1, arg2) => { + write!(f, "CheckRecordFieldFx({arg0:?}, {arg1:?}, {arg2:?})") + } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(), diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 82e6a43ee25..18ca1e0236b 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -283,6 +283,10 @@ pub fn constrain_expr( let (field_type, field_con) = constrain_field(types, constraints, env, field_var, loc_field_expr); + let check_field_con = + constraints.check_record_field_fx(label.suffix(), field_var, field.region); + let field_con = constraints.and_constraint([field_con, check_field_con]); + field_vars.push(field_var); field_types.insert(label.clone(), RecordField::Required(field_type)); diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 319add6999f..d18aa21e337 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14934,5 +14934,37 @@ All branches in an `if` must have the same type! "### ); + test_report!( + unsuffixed_fx_in_record, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + fx = { + putLine: Effect.putLine! + } + fx.putLine "hello world!" + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This field's value is an effectful function, but its name does not + indicate so: + + 7│ putLine: Effect.putLine! + ^^^^^^^^^^^^^^^^^^^^^^^^ + + Add an exclamation mark at the end of its name, like: + + { readFile! : File.read! } + + This will help readers identify it as a source of effects. + "### + ); + // [purity-inference] TODO: check ! in records, tuples, tags, opaques, and arguments } diff --git a/crates/compiler/module/src/ident.rs b/crates/compiler/module/src/ident.rs index a0f979d0dbe..1d97b402892 100644 --- a/crates/compiler/module/src/ident.rs +++ b/crates/compiler/module/src/ident.rs @@ -236,6 +236,10 @@ impl Lowercase { pub fn as_str(&self) -> &str { self.0.as_str() } + + pub fn suffix(&self) -> IdentSuffix { + IdentSuffix::from_name(self.0.as_str()) + } } impl From for String { diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 1932c1a824c..33ecef708ab 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -25,8 +25,8 @@ use roc_error_macros::internal_error; use roc_module::ident::IdentSuffix; use roc_module::symbol::{ModuleId, Symbol}; use roc_problem::can::CycleEntry; -use roc_region::all::Loc; -use roc_solve_problem::TypeError; +use roc_region::all::{Loc, Region}; +use roc_solve_problem::{SuffixErrorKind, TypeError}; use roc_solve_schema::UnificationMode; use roc_types::subs::{ self, Content, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt, UlsOfVar, @@ -450,7 +450,14 @@ fn solve( new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value); - check_symbol_suffix(env, problems, *symbol, *loc_var); + check_ident_suffix( + env, + problems, + symbol.suffix(), + loc_var.value, + &loc_var.region, + SuffixErrorKind::Let(*symbol), + ); } // Note that this vars_by_symbol is the one returned by the @@ -875,6 +882,17 @@ fn solve( } } } + CheckRecordFieldFx(suffix, field_var, region) => { + check_ident_suffix( + env, + problems, + *suffix, + *field_var, + region, + SuffixErrorKind::RecordField, + ); + state + } Let(index, pool_slice) => { let let_con = &env.constraints.let_constraints[index.index()]; @@ -1593,31 +1611,30 @@ fn solve( state } -fn check_symbol_suffix( +fn check_ident_suffix( env: &mut InferenceEnv<'_>, problems: &mut Vec, - symbol: Symbol, - loc_var: Loc, + suffix: IdentSuffix, + variable: Variable, + region: &Region, + kind: SuffixErrorKind, ) { - match symbol.suffix() { + match suffix { IdentSuffix::None => { if let Content::Structure(FlatType::Func(_, _, _, fx)) = - env.subs.get_content_without_compacting(loc_var.value) + env.subs.get_content_without_compacting(variable) { if let Content::Effectful = env.subs.get_content_without_compacting(*fx) { - problems.push(TypeError::UnsuffixedEffectfulFunction( - loc_var.region, - symbol, - )); + problems.push(TypeError::UnsuffixedEffectfulFunction(*region, kind)); } } } IdentSuffix::Bang => { if let Content::Structure(FlatType::Func(_, _, _, fx)) = - env.subs.get_content_without_compacting(loc_var.value) + env.subs.get_content_without_compacting(variable) { if let Content::Pure = env.subs.get_content_without_compacting(*fx) { - problems.push(TypeError::SuffixedPureFunction(loc_var.region, symbol)); + problems.push(TypeError::SuffixedPureFunction(*region, kind)); } } } diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 3c46b208bcb..bd4918f7fd8 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -45,8 +45,14 @@ pub enum TypeError { FxInPureFunction(Region, FxCallKind, Option), FxInTopLevel(Region, FxCallKind), PureStmt(Region), - UnsuffixedEffectfulFunction(Region, Symbol), - SuffixedPureFunction(Region, Symbol), + UnsuffixedEffectfulFunction(Region, SuffixErrorKind), + SuffixedPureFunction(Region, SuffixErrorKind), +} + +#[derive(Debug, Clone)] +pub enum SuffixErrorKind { + Let(Symbol), + RecordField, } impl TypeError { diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 7cb58fc902f..c9009707b76 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -16,7 +16,7 @@ use roc_module::symbol::Symbol; use roc_problem::Severity; use roc_region::all::{LineInfo, Region}; use roc_solve_problem::{ - NotDerivableContext, NotDerivableEq, TypeError, UnderivableReason, Unfulfilled, + NotDerivableContext, NotDerivableEq, SuffixErrorKind, TypeError, UnderivableReason, Unfulfilled, }; use roc_std::RocDec; use roc_types::pretty_print::{Parens, WILDCARD}; @@ -384,7 +384,7 @@ pub fn type_problem<'b>( severity, }) } - UnsuffixedEffectfulFunction(region, symbol) => { + UnsuffixedEffectfulFunction(region, SuffixErrorKind::Let(symbol)) => { let stack = [ alloc.reflow("This function is effectful, but its name does not indicate so:"), alloc.region(lines.convert_region(region), severity), @@ -401,7 +401,26 @@ pub fn type_problem<'b>( severity, }) } - SuffixedPureFunction(region, _symbol) => { + UnsuffixedEffectfulFunction(region, SuffixErrorKind::RecordField) => { + let stack = [ + alloc.reflow( + "This field's value is an effectful function, but its name does not indicate so:", + ), + alloc.region(lines.convert_region(region), severity), + alloc.reflow("Add an exclamation mark at the end of its name, like:"), + alloc + .parser_suggestion("{ readFile! : File.read! }") + .indent(4), + alloc.reflow("This will help readers identify it as a source of effects."), + ]; + Some(Report { + title: "MISSING EXCLAMATION".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } + SuffixedPureFunction(region, SuffixErrorKind::Let(_)) => { let stack = [ alloc.reflow("This function is pure, but its name suggests otherwise:"), alloc.region(lines.convert_region(region), severity), @@ -417,6 +436,9 @@ pub fn type_problem<'b>( severity, }) } + SuffixedPureFunction(region, SuffixErrorKind::RecordField) => { + todo!("[purity-inference]"); + } } } From a31a35100b628cc507b1c5d79b5f45d8fd4ee2eb Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 22 Oct 2024 20:00:19 -0300 Subject: [PATCH 45/73] Report suffixed pure function in literal record field --- crates/compiler/load/tests/test_reporting.rs | 29 ++++++++++++++++++++ crates/reporting/src/error/type.rs | 14 ++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index d18aa21e337..d5220a88789 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14966,5 +14966,34 @@ All branches in an `if` must have the same type! "### ); + test_report!( + suffixed_pure_in_record, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + notFx = { + trim!: Str.trim + } + Effect.putLine! (notFx.trim! " hello ") + "# + ), + @r###" + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This field's value is a pure function, but its name suggests + otherwise: + + 7│ trim!: Str.trim + ^^^^^^^^^^^^^^^ + + Remove the exclamation mark to give an accurate impression of its + behavior. + "### + ); + // [purity-inference] TODO: check ! in records, tuples, tags, opaques, and arguments } diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index c9009707b76..4ed3c5703ae 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -420,9 +420,16 @@ pub fn type_problem<'b>( severity, }) } - SuffixedPureFunction(region, SuffixErrorKind::Let(_)) => { + SuffixedPureFunction(region, kind) => { let stack = [ - alloc.reflow("This function is pure, but its name suggests otherwise:"), + match kind { + SuffixErrorKind::Let(_) => { + alloc.reflow("This function is pure, but its name suggests otherwise:") + } + SuffixErrorKind::RecordField => { + alloc.reflow("This field's value is a pure function, but its name suggests otherwise:") + } + }, alloc.region(lines.convert_region(region), severity), alloc.reflow( "Remove the exclamation mark to give an accurate impression of its behavior.", @@ -436,9 +443,6 @@ pub fn type_problem<'b>( severity, }) } - SuffixedPureFunction(region, SuffixErrorKind::RecordField) => { - todo!("[purity-inference]"); - } } } From e75b1cf7a0ebf479b9dd048401a3781d28a409b6 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 23 Oct 2024 02:42:56 -0300 Subject: [PATCH 46/73] Check suffixes of all pattern identifiers --- crates/compiler/can/src/constraint.rs | 72 ++++++++++++--- crates/compiler/constrain/src/expr.rs | 2 +- crates/compiler/constrain/src/pattern.rs | 4 + crates/compiler/load/tests/test_reporting.rs | 93 ++++++++++++++++++-- crates/compiler/solve/src/solve.rs | 46 ++++++---- crates/compiler/solve_problem/src/lib.rs | 11 +-- crates/reporting/src/error/type.rs | 42 ++++++--- 7 files changed, 210 insertions(+), 60 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index d08a86f8d1c..8ac14f35fda 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -30,6 +30,7 @@ pub struct Constraints { pub pattern_eq: Vec, pub cycles: Vec, pub fx_call_constraints: Vec, + pub fx_suffix_constraints: Vec, } impl std::fmt::Debug for Constraints { @@ -84,6 +85,7 @@ impl Constraints { let pattern_eq = Vec::new(); let cycles = Vec::new(); let fx_call_constraints = Vec::with_capacity(16); + let fx_suffix_constraints = Vec::new(); categories.extend([ Category::Record, @@ -134,6 +136,7 @@ impl Constraints { pattern_eq, cycles, fx_call_constraints, + fx_suffix_constraints, } } @@ -597,13 +600,39 @@ impl Constraints { Constraint::FxCall(constraint_index) } - pub fn check_record_field_fx( - &self, + pub fn fx_pattern_suffix( + &mut self, + symbol: Symbol, + type_index: TypeOrVar, + region: Region, + ) -> Constraint { + let constraint = FxSuffixConstraint { + kind: FxSuffixKind::Pattern(symbol), + type_index, + region, + }; + + let constraint_index = index_push_new(&mut self.fx_suffix_constraints, constraint); + + Constraint::FxSuffix(constraint_index) + } + + pub fn fx_record_field_suffix( + &mut self, suffix: IdentSuffix, variable: Variable, region: Region, ) -> Constraint { - Constraint::CheckRecordFieldFx(suffix, variable, region) + let type_index = Self::push_type_variable(variable); + let constraint = FxSuffixConstraint { + kind: FxSuffixKind::RecordField(suffix), + type_index, + region, + }; + + let constraint_index = index_push_new(&mut self.fx_suffix_constraints, constraint); + + Constraint::FxSuffix(constraint_index) } pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool { @@ -632,7 +661,7 @@ impl Constraints { | Constraint::Pattern(..) | Constraint::EffectfulStmt(..) | Constraint::FxCall(_) - | Constraint::CheckRecordFieldFx(_, _, _) + | Constraint::FxSuffix(_) | Constraint::FlexToPure(_) | Constraint::True | Constraint::IsOpenType(_) @@ -808,12 +837,12 @@ pub enum Constraint { ), /// Check call fx against enclosing function fx FxCall(Index), + /// Require idents to be accurately suffixed + FxSuffix(Index), /// Set an fx var as pure if flex (no effectful functions were called) FlexToPure(Variable), /// Expect statement to be effectful EffectfulStmt(Variable, Region), - /// Require field name to be accurately suffixed - CheckRecordFieldFx(IdentSuffix, Variable, Region), /// Used for things that always unify, e.g. blanks and runtime errors True, SaveTheEnvironment, @@ -906,6 +935,29 @@ pub enum FxCallKind { Stmt, } +#[derive(Debug, Clone, Copy)] +pub struct FxSuffixConstraint { + pub type_index: TypeOrVar, + pub kind: FxSuffixKind, + pub region: Region, +} + +#[derive(Debug, Clone, Copy)] +pub enum FxSuffixKind { + Let(Symbol), + Pattern(Symbol), + RecordField(IdentSuffix), +} + +impl FxSuffixKind { + pub fn suffix(&self) -> IdentSuffix { + match self { + Self::Let(symbol) | Self::Pattern(symbol) => symbol.suffix(), + Self::RecordField(suffix) => *suffix, + } + } +} + /// Custom impl to limit vertical space used by the debug output impl std::fmt::Debug for Constraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -923,7 +975,10 @@ impl std::fmt::Debug for Constraint { write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } Self::FxCall(arg0) => { - write!(f, "CallFx({arg0:?})") + write!(f, "FxCall({arg0:?})") + } + Self::FxSuffix(arg0) => { + write!(f, "FxSuffix({arg0:?})") } Self::EffectfulStmt(arg0, arg1) => { write!(f, "EffectfulStmt({arg0:?}, {arg1:?})") @@ -931,9 +986,6 @@ impl std::fmt::Debug for Constraint { Self::FlexToPure(arg0) => { write!(f, "FlexToPure({arg0:?})") } - Self::CheckRecordFieldFx(arg0, arg1, arg2) => { - write!(f, "CheckRecordFieldFx({arg0:?}, {arg1:?}, {arg2:?})") - } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(), diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 18ca1e0236b..8c41c9c779d 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -284,7 +284,7 @@ pub fn constrain_expr( constrain_field(types, constraints, env, field_var, loc_field_expr); let check_field_con = - constraints.check_record_field_fx(label.suffix(), field_var, field.region); + constraints.fx_record_field_suffix(label.suffix(), field_var, field.region); let field_con = constraints.and_constraint([field_con, check_field_con]); field_vars.push(field_var); diff --git a/crates/compiler/constrain/src/pattern.rs b/crates/compiler/constrain/src/pattern.rs index f1d686993dc..bc02e188b10 100644 --- a/crates/compiler/constrain/src/pattern.rs +++ b/crates/compiler/constrain/src/pattern.rs @@ -276,6 +276,10 @@ pub fn constrain_pattern( .push(constraints.is_open_type(type_index)); } + state + .constraints + .push(constraints.fx_pattern_suffix(*symbol, type_index, region)); + state.headers.insert( *symbol, Loc { diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index d5220a88789..a8792e2a107 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14770,8 +14770,9 @@ All branches in an `if` must have the same type! 5│ main! = \{} -> ^^^^^ - Remove the exclamation mark to give an accurate impression of its - behavior. + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? "### ); @@ -14798,7 +14799,7 @@ All branches in an `if` must have the same type! 6│ printHello = \{} -> ^^^^^^^^^^ - Add an exclamation mark at the end of its name, like: + Add an exclamation mark at the end, like: printHello! @@ -14861,8 +14862,9 @@ All branches in an `if` must have the same type! 8│ hello! = \{} -> ^^^^^^ - Remove the exclamation mark to give an accurate impression of its - behavior. + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? "### ); @@ -14926,7 +14928,7 @@ All branches in an `if` must have the same type! 8│ printLn = Effect.putLine! ^^^^^^^ - Add an exclamation mark at the end of its name, like: + Add an exclamation mark at the end, like: printLn! @@ -14958,7 +14960,7 @@ All branches in an `if` must have the same type! 7│ putLine: Effect.putLine! ^^^^^^^^^^^^^^^^^^^^^^^^ - Add an exclamation mark at the end of its name, like: + Add an exclamation mark at the end, like: { readFile! : File.read! } @@ -14990,8 +14992,81 @@ All branches in an `if` must have the same type! 7│ trim!: Str.trim ^^^^^^^^^^^^^^^ - Remove the exclamation mark to give an accurate impression of its - behavior. + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? + "### + ); + + test_report!( + unsuffixed_fx_arg, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + ["Hello", "world!"] + |> forEach! Effect.putLine! + + forEach! : List a, (a => {}) => {} + forEach! = \l, f -> + when l is + [] -> {} + [x, .. as xs] -> + f x + forEach! xs f + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This matches an effectful function, but its name does not indicate so: + + 10│ forEach! = \l, f -> + ^ + + Add an exclamation mark at the end, like: + + f! + + This will help readers identify it as a source of effects. + "### + ); + + test_report!( + suffixed_pure_arg, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + Ok " hi " + |> mapOk Str.trim + |> Result.withDefault "" + |> Effect.putLine! + + mapOk : Result a err, (a -> b) -> Result b err + mapOk = \result, fn! -> + when result is + Ok x -> Ok (fn! x) + Err e -> Err e + "# + ), + @r###" + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This matches a pure function, but the name suggests otherwise: + + 12│ mapOk = \result, fn! -> + ^^^ + + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? "### ); diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 33ecef708ab..d42bcb9208b 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -14,7 +14,9 @@ use crate::Aliases; use bumpalo::Bump; use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo}; use roc_can::constraint::Constraint::{self, *}; -use roc_can::constraint::{Cycle, FxCallConstraint, LetConstraint, OpportunisticResolve}; +use roc_can::constraint::{ + Cycle, FxCallConstraint, FxSuffixConstraint, FxSuffixKind, LetConstraint, OpportunisticResolve, +}; use roc_can::expected::{Expected, PExpected}; use roc_can::module::ModuleParams; use roc_collections::VecMap; @@ -26,7 +28,7 @@ use roc_module::ident::IdentSuffix; use roc_module::symbol::{ModuleId, Symbol}; use roc_problem::can::CycleEntry; use roc_region::all::{Loc, Region}; -use roc_solve_problem::{SuffixErrorKind, TypeError}; +use roc_solve_problem::TypeError; use roc_solve_schema::UnificationMode; use roc_types::subs::{ self, Content, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt, UlsOfVar, @@ -453,10 +455,9 @@ fn solve( check_ident_suffix( env, problems, - symbol.suffix(), + FxSuffixKind::Let(*symbol), loc_var.value, &loc_var.region, - SuffixErrorKind::Let(*symbol), ); } @@ -834,6 +835,27 @@ fn solve( } } } + FxSuffix(constraint_index) => { + let FxSuffixConstraint { + type_index, + kind, + region, + } = &env.constraints.fx_suffix_constraints[constraint_index.index()]; + + let actual = either_type_index_to_var( + env, + rank, + problems, + abilities_store, + obligation_cache, + &mut can_types, + aliases, + *type_index, + ); + + check_ident_suffix(env, problems, *kind, actual, region); + state + } EffectfulStmt(variable, region) => { let content = env.subs.get_content_without_compacting(*variable); @@ -882,17 +904,6 @@ fn solve( } } } - CheckRecordFieldFx(suffix, field_var, region) => { - check_ident_suffix( - env, - problems, - *suffix, - *field_var, - region, - SuffixErrorKind::RecordField, - ); - state - } Let(index, pool_slice) => { let let_con = &env.constraints.let_constraints[index.index()]; @@ -1614,12 +1625,11 @@ fn solve( fn check_ident_suffix( env: &mut InferenceEnv<'_>, problems: &mut Vec, - suffix: IdentSuffix, + kind: FxSuffixKind, variable: Variable, region: &Region, - kind: SuffixErrorKind, ) { - match suffix { + match kind.suffix() { IdentSuffix::None => { if let Content::Structure(FlatType::Func(_, _, _, fx)) = env.subs.get_content_without_compacting(variable) diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index bd4918f7fd8..e5b3f88d9ff 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -1,6 +1,7 @@ //! Provides types to describe problems that can occur during solving. use std::{path::PathBuf, str::Utf8Error}; +use roc_can::constraint::FxSuffixKind; use roc_can::{ constraint::FxCallKind, expected::{Expected, PExpected}, @@ -45,14 +46,8 @@ pub enum TypeError { FxInPureFunction(Region, FxCallKind, Option), FxInTopLevel(Region, FxCallKind), PureStmt(Region), - UnsuffixedEffectfulFunction(Region, SuffixErrorKind), - SuffixedPureFunction(Region, SuffixErrorKind), -} - -#[derive(Debug, Clone)] -pub enum SuffixErrorKind { - Let(Symbol), - RecordField, + UnsuffixedEffectfulFunction(Region, FxSuffixKind), + SuffixedPureFunction(Region, FxSuffixKind), } impl TypeError { diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 4ed3c5703ae..d890bc7f345 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -4,7 +4,7 @@ use crate::error::canonicalize::{to_circular_def_doc, CIRCULAR_DEF}; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder}; use itertools::EitherOrBoth; use itertools::Itertools; -use roc_can::constraint::FxCallKind; +use roc_can::constraint::{FxCallKind, FxSuffixKind}; use roc_can::expected::{Expected, PExpected}; use roc_collections::all::{HumanIndex, MutSet, SendMap}; use roc_collections::VecMap; @@ -16,7 +16,7 @@ use roc_module::symbol::Symbol; use roc_problem::Severity; use roc_region::all::{LineInfo, Region}; use roc_solve_problem::{ - NotDerivableContext, NotDerivableEq, SuffixErrorKind, TypeError, UnderivableReason, Unfulfilled, + NotDerivableContext, NotDerivableEq, TypeError, UnderivableReason, Unfulfilled, }; use roc_std::RocDec; use roc_types::pretty_print::{Parens, WILDCARD}; @@ -384,11 +384,23 @@ pub fn type_problem<'b>( severity, }) } - UnsuffixedEffectfulFunction(region, SuffixErrorKind::Let(symbol)) => { + UnsuffixedEffectfulFunction( + region, + kind @ (FxSuffixKind::Let(symbol) | FxSuffixKind::Pattern(symbol)), + ) => { let stack = [ - alloc.reflow("This function is effectful, but its name does not indicate so:"), + match kind { + FxSuffixKind::Let(_) => alloc + .reflow("This function is effectful, but its name does not indicate so:"), + FxSuffixKind::Pattern(_) => alloc.reflow( + "This matches an effectful function, but its name does not indicate so:", + ), + FxSuffixKind::RecordField(_) => { + unreachable!() + } + }, alloc.region(lines.convert_region(region), severity), - alloc.reflow("Add an exclamation mark at the end of its name, like:"), + alloc.reflow("Add an exclamation mark at the end, like:"), alloc .string(format!("{}!", symbol.as_str(alloc.interns))) .indent(4), @@ -401,13 +413,13 @@ pub fn type_problem<'b>( severity, }) } - UnsuffixedEffectfulFunction(region, SuffixErrorKind::RecordField) => { + UnsuffixedEffectfulFunction(region, FxSuffixKind::RecordField(_)) => { let stack = [ alloc.reflow( "This field's value is an effectful function, but its name does not indicate so:", ), alloc.region(lines.convert_region(region), severity), - alloc.reflow("Add an exclamation mark at the end of its name, like:"), + alloc.reflow("Add an exclamation mark at the end, like:"), alloc .parser_suggestion("{ readFile! : File.read! }") .indent(4), @@ -423,17 +435,19 @@ pub fn type_problem<'b>( SuffixedPureFunction(region, kind) => { let stack = [ match kind { - SuffixErrorKind::Let(_) => { + FxSuffixKind::Let(_) => { alloc.reflow("This function is pure, but its name suggests otherwise:") } - SuffixErrorKind::RecordField => { - alloc.reflow("This field's value is a pure function, but its name suggests otherwise:") - } + FxSuffixKind::Pattern(_) => alloc + .reflow("This matches a pure function, but the name suggests otherwise:"), + FxSuffixKind::RecordField(_) => alloc.reflow( + "This field's value is a pure function, but its name suggests otherwise:", + ), }, alloc.region(lines.convert_region(region), severity), - alloc.reflow( - "Remove the exclamation mark to give an accurate impression of its behavior.", - ), + alloc + .reflow("The exclamation mark at the end is reserved for effectful functions."), + alloc.hint("Did you forget to run an effect? Is the type annotation wrong?"), ]; Some(Report { From 2c208f938938d42abf5b06f5730a4342bbe353c3 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 23 Oct 2024 02:47:55 -0300 Subject: [PATCH 47/73] Test tuple destructure suffixes --- crates/compiler/load/tests/test_reporting.rs | 72 ++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index a8792e2a107..134dcdef5ee 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -15070,5 +15070,77 @@ All branches in an `if` must have the same type! "### ); + test_report!( + unsuffixed_tuple_fx_field, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + (get, put) = (Effect.getLine!, Effect.putLine!) + + name = get {} + put "Hi, $(name)" + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 6│ (get, put) = (Effect.getLine!, Effect.putLine!) + ^^^ + + Add an exclamation mark at the end, like: + + get! + + This will help readers identify it as a source of effects. + + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 6│ (get, put) = (Effect.getLine!, Effect.putLine!) + ^^^ + + Add an exclamation mark at the end, like: + + put! + + This will help readers identify it as a source of effects. + "### + ); + + test_report!( + suffixed_tuple_pure_field, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + (msg, trim!) = (" hi ", Str.trim) + + Effect.putLine! (trim! msg) + "# + ), + @r###" + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This function is pure, but its name suggests otherwise: + + 6│ (msg, trim!) = (" hi ", Str.trim) + ^^^^^ + + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? + "### + ); + // [purity-inference] TODO: check ! in records, tuples, tags, opaques, and arguments } From a0783c31323594ed105b0b273080ff2d8f20279a Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 23 Oct 2024 02:50:15 -0300 Subject: [PATCH 48/73] Test tag destructure suffixes --- crates/compiler/load/tests/test_reporting.rs | 72 ++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 134dcdef5ee..3dc151f7a06 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -15142,5 +15142,77 @@ All branches in an `if` must have the same type! "### ); + test_report!( + unsuffixed_tag_fx_field, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + Tag get put = Tag Effect.getLine! Effect.putLine! + + name = get {} + put "Hi, $(name)" + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 6│ Tag get put = Tag Effect.getLine! Effect.putLine! + ^^^ + + Add an exclamation mark at the end, like: + + get! + + This will help readers identify it as a source of effects. + + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 6│ Tag get put = Tag Effect.getLine! Effect.putLine! + ^^^ + + Add an exclamation mark at the end, like: + + put! + + This will help readers identify it as a source of effects. + "### + ); + + test_report!( + suffixed_tag_pure_field, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + Tag msg trim! = Tag " hi " Str.trim + + Effect.putLine! (trim! msg) + "# + ), + @r###" + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This function is pure, but its name suggests otherwise: + + 6│ Tag msg trim! = Tag " hi " Str.trim + ^^^^^ + + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? + "### + ); + // [purity-inference] TODO: check ! in records, tuples, tags, opaques, and arguments } From 70fa4d036c00e7f9af5a3898ba50d5277ad29643 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 23 Oct 2024 02:54:03 -0300 Subject: [PATCH 49/73] Test opaque destructure suffixes --- crates/compiler/load/tests/test_reporting.rs | 62 +++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 3dc151f7a06..b6918ba0f59 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -15214,5 +15214,65 @@ All branches in an `if` must have the same type! "### ); - // [purity-inference] TODO: check ! in records, tuples, tags, opaques, and arguments + test_report!( + unsuffixed_opaque_fx_field, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + PutLine := Str => {} + + main! = \{} -> + @PutLine put = @PutLine Effect.putLine! + + put "Hi!" + "# + ), + @r###" + ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── + + This function is effectful, but its name does not indicate so: + + 8│ @PutLine put = @PutLine Effect.putLine! + ^^^ + + Add an exclamation mark at the end, like: + + put! + + This will help readers identify it as a source of effects. + "### + ); + + test_report!( + suffixed_opaque_pure_field, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + Trim := Str -> Str + + main! = \{} -> + @Trim trim! = @Trim Str.trim + + Effect.putLine! (trim! " hi ") + "# + ), + @r###" + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This function is pure, but its name suggests otherwise: + + 8│ @Trim trim! = @Trim Str.trim + ^^^^^ + + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? + "### + ); } From af6fc6306fb57a48c6a35408313e89a47de0c33e Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 23 Oct 2024 21:27:04 -0300 Subject: [PATCH 50/73] Treat untyped unsuffixed functions as pure --- crates/compiler/can/src/constraint.rs | 4 + crates/compiler/constrain/src/expr.rs | 28 ++++++- crates/compiler/constrain/src/pattern.rs | 51 ++++++++++-- crates/compiler/load/tests/test_reporting.rs | 87 ++++++++++++++++++++ crates/compiler/solve/src/solve.rs | 40 ++++++--- 5 files changed, 186 insertions(+), 24 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index 8ac14f35fda..e2787bb4b8c 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -635,6 +635,10 @@ impl Constraints { Constraint::FxSuffix(constraint_index) } + pub fn flex_to_pure(&mut self, fx_var: Variable) -> Constraint { + Constraint::FlexToPure(fx_var) + } + pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool { match constraint { Constraint::SaveTheEnvironment => true, diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 8c41c9c779d..df0295bcd9a 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -234,6 +234,7 @@ fn constrain_untyped_closure( ret_constraint, Generalizable(true), ), + constraints.and_constraint(pattern_state.delayed_fx_suffix_constraints), constraints.equal_types_with_storage( function_type, expected, @@ -243,7 +244,7 @@ fn constrain_untyped_closure( ), early_returns_constraint, closure_constraint, - Constraint::FlexToPure(fx_var), + constraints.flex_to_pure(fx_var), ]; constraints.exists_many(vars, cons) @@ -2029,6 +2030,7 @@ fn constrain_function_def( vars: Vec::with_capacity(function_def.arguments.len()), constraints: Vec::with_capacity(1), delayed_is_open_constraints: vec![], + delayed_fx_suffix_constraints: Vec::with_capacity(function_def.arguments.len()), }; let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let closure_var = function_def.closure_type; @@ -2171,9 +2173,12 @@ fn constrain_function_def( Category::Lambda, region, ), + // Check argument suffixes against usage + constraints.and_constraint(argument_pattern_state.delayed_fx_suffix_constraints), // Finally put the solved closure type into the dedicated def expr variable. constraints.store(signature_index, expr_var, std::file!(), std::line!()), closure_constraint, + constraints.flex_to_pure(function_def.fx_type), ]; let expr_con = constraints.exists_many(vars, cons); @@ -2488,6 +2493,7 @@ fn constrain_when_branch_help( vars: Vec::with_capacity(2), constraints: Vec::with_capacity(2), delayed_is_open_constraints: Vec::new(), + delayed_fx_suffix_constraints: Vec::new(), }; for (i, loc_pattern) in when_branch.patterns.iter().enumerate() { @@ -2512,6 +2518,9 @@ fn constrain_when_branch_help( state .delayed_is_open_constraints .extend(partial_state.delayed_is_open_constraints); + state + .delayed_fx_suffix_constraints + .extend(partial_state.delayed_fx_suffix_constraints); if i == 0 { state.headers.extend(partial_state.headers); @@ -2840,6 +2849,7 @@ pub(crate) fn constrain_def_pattern( vars: Vec::with_capacity(1), constraints: Vec::with_capacity(1), delayed_is_open_constraints: vec![], + delayed_fx_suffix_constraints: vec![], }; constrain_pattern( @@ -2949,6 +2959,7 @@ fn constrain_typed_def( vars: Vec::with_capacity(arguments.len()), constraints: Vec::with_capacity(1), delayed_is_open_constraints: vec![], + delayed_fx_suffix_constraints: Vec::with_capacity(arguments.len()), }; let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let ret_var = *ret_var; @@ -3032,6 +3043,8 @@ fn constrain_typed_def( // This is a syntactic function, it can be generalized Generalizable(true), ), + // Check argument suffixes against usage + constraints.and_constraint(argument_pattern_state.delayed_fx_suffix_constraints), // Store the inferred ret var into the function type now, so that // when we check that the solved function type matches the annotation, we can // display the fully inferred return variable. @@ -3047,7 +3060,7 @@ fn constrain_typed_def( constraints.store(signature_index, *fn_var, std::file!(), std::line!()), constraints.store(signature_index, expr_var, std::file!(), std::line!()), closure_constraint, - Constraint::FlexToPure(fx_var), + constraints.flex_to_pure(fx_var), ]; let expr_con = constraints.exists_many(vars, cons); @@ -3933,6 +3946,7 @@ fn constraint_recursive_function( vars: Vec::with_capacity(function_def.arguments.len()), constraints: Vec::with_capacity(1), delayed_is_open_constraints: vec![], + delayed_fx_suffix_constraints: Vec::with_capacity(function_def.arguments.len()), }; let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let ret_var = function_def.return_type; @@ -4022,13 +4036,15 @@ fn constraint_recursive_function( // Syntactic function can be generalized Generalizable(true), ), + // Check argument suffixes against usage + constraints.and_constraint(argument_pattern_state.delayed_fx_suffix_constraints), constraints.equal_types(fn_type, annotation_expected, Category::Lambda, region), // "fn_var is equal to the closure's type" - fn_var is used in code gen // Store type into AST vars. We use Store so errors aren't reported twice constraints.store(signature_index, expr_var, std::file!(), std::line!()), constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), closure_constraint, - Constraint::FlexToPure(fx_var), + constraints.flex_to_pure(fx_var), ]; let and_constraint = constraints.and_constraint(cons); @@ -4502,6 +4518,7 @@ fn rec_defs_help( vars: Vec::with_capacity(arguments.len()), constraints: Vec::with_capacity(1), delayed_is_open_constraints: vec![], + delayed_fx_suffix_constraints: Vec::with_capacity(arguments.len()), }; let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); @@ -4578,6 +4595,10 @@ fn rec_defs_help( expr_con, generalizable, ), + // Check argument suffixes against usage + constraints.and_constraint( + argument_pattern_state.delayed_fx_suffix_constraints, + ), constraints.equal_types( fn_type_index, expected_index, @@ -4595,6 +4616,7 @@ fn rec_defs_help( ), constraints.store(ret_type_index, ret_var, std::file!(), std::line!()), closure_constraint, + constraints.flex_to_pure(fx_var), ]; let and_constraint = constraints.and_constraint(cons); diff --git a/crates/compiler/constrain/src/pattern.rs b/crates/compiler/constrain/src/pattern.rs index bc02e188b10..6a5a0e7b311 100644 --- a/crates/compiler/constrain/src/pattern.rs +++ b/crates/compiler/constrain/src/pattern.rs @@ -22,6 +22,7 @@ pub struct PatternState { pub vars: Vec, pub constraints: Vec, pub delayed_is_open_constraints: Vec, + pub delayed_fx_suffix_constraints: Vec, } /// If there is a type annotation, the pattern state headers can be optimized by putting the @@ -247,6 +248,28 @@ pub fn constrain_pattern( region: Region, expected: PExpectedTypeIndex, state: &mut PatternState, +) { + constrain_pattern_help( + types, + constraints, + env, + pattern, + region, + expected, + state, + true, + ); +} + +pub fn constrain_pattern_help( + types: &mut Types, + constraints: &mut Constraints, + env: &mut Env, + pattern: &Pattern, + region: Region, + expected: PExpectedTypeIndex, + state: &mut PatternState, + is_shallow: bool, ) { match pattern { Underscore => { @@ -276,9 +299,13 @@ pub fn constrain_pattern( .push(constraints.is_open_type(type_index)); } - state - .constraints - .push(constraints.fx_pattern_suffix(*symbol, type_index, region)); + if is_shallow { + // Identifiers introduced in nested patterns get let constraints + // and therefore don't need fx_pattern_suffix constraints. + state + .delayed_fx_suffix_constraints + .push(constraints.fx_pattern_suffix(*symbol, type_index, region)); + } state.headers.insert( *symbol, @@ -301,7 +328,7 @@ pub fn constrain_pattern( }, ); - constrain_pattern( + constrain_pattern_help( types, constraints, env, @@ -309,6 +336,7 @@ pub fn constrain_pattern( subpattern.region, expected, state, + false, ) } @@ -534,7 +562,7 @@ pub fn constrain_pattern( )); state.vars.push(*guard_var); - constrain_pattern( + constrain_pattern_help( types, constraints, env, @@ -542,6 +570,7 @@ pub fn constrain_pattern( loc_pattern.region, expected, state, + false, ); pat_type @@ -632,7 +661,7 @@ pub fn constrain_pattern( )); state.vars.push(*guard_var); - constrain_pattern( + constrain_pattern_help( types, constraints, env, @@ -640,6 +669,7 @@ pub fn constrain_pattern( loc_guard.region, expected, state, + false, ); RecordField::Demanded(pat_type) @@ -755,7 +785,7 @@ pub fn constrain_pattern( loc_pat.region, )); - constrain_pattern( + constrain_pattern_help( types, constraints, env, @@ -763,6 +793,7 @@ pub fn constrain_pattern( loc_pat.region, expected, state, + false, ); } @@ -811,7 +842,7 @@ pub fn constrain_pattern( pattern_type, region, )); - constrain_pattern( + constrain_pattern_help( types, constraints, env, @@ -819,6 +850,7 @@ pub fn constrain_pattern( loc_pattern.region, expected, state, + false, ); } @@ -876,7 +908,7 @@ pub fn constrain_pattern( // First, add a constraint for the argument "who" let arg_pattern_expected = constraints .push_pat_expected_type(PExpected::NoExpectation(arg_pattern_type_index)); - constrain_pattern( + constrain_pattern_help( types, constraints, env, @@ -884,6 +916,7 @@ pub fn constrain_pattern( loc_arg_pattern.region, arg_pattern_expected, state, + false, ); // Next, link `whole_var` to the opaque type of "@Id who" diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index b6918ba0f59..2df423a9cc7 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -15275,4 +15275,91 @@ All branches in an `if` must have the same type! Hint: Did you forget to run an effect? Is the type annotation wrong? "### ); + + test_report!( + fx_passed_to_untyped_pure_hof, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + pureHigherOrder Effect.putLine! "hi" + + pureHigherOrder = \f, x -> f x + "# + ), + @r###" + ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── + + This 1st argument to `pureHigherOrder` has an unexpected type: + + 6│ pureHigherOrder Effect.putLine! "hi" + ^^^^^^^^^^^^^^^ + + This `Effect.putLine!` value is a: + + Str => {} + + But `pureHigherOrder` needs its 1st argument to be: + + Str -> {} + + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This function is pure, but its name suggests otherwise: + + 5│ main! = \{} -> + ^^^^^ + + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? + "### + ); + + test_report!( + fx_passed_to_partially_inferred_pure_hof, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + pureHigherOrder Effect.putLine! "hi" + + pureHigherOrder : _, _ -> _ + pureHigherOrder = \f, x -> f x + "# + ), + @r###" + ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── + + This 1st argument to `pureHigherOrder` has an unexpected type: + + 6│ pureHigherOrder Effect.putLine! "hi" + ^^^^^^^^^^^^^^^ + + This `Effect.putLine!` value is a: + + Str => {} + + But `pureHigherOrder` needs its 1st argument to be: + + Str -> {} + + ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── + + This function is pure, but its name suggests otherwise: + + 5│ main! = \{} -> + ^^^^^ + + The exclamation mark at the end is reserved for effectful functions. + + Hint: Did you forget to run an effect? Is the type annotation wrong? + "### + ); } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index d42bcb9208b..b84f6219c06 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -452,7 +452,7 @@ fn solve( new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value); - check_ident_suffix( + solve_suffix_fx( env, problems, FxSuffixKind::Let(*symbol), @@ -853,7 +853,7 @@ fn solve( *type_index, ); - check_ident_suffix(env, problems, *kind, actual, region); + solve_suffix_fx(env, problems, *kind, actual, region); state } EffectfulStmt(variable, region) => { @@ -1622,7 +1622,7 @@ fn solve( state } -fn check_ident_suffix( +fn solve_suffix_fx( env: &mut InferenceEnv<'_>, problems: &mut Vec, kind: FxSuffixKind, @@ -1634,20 +1634,36 @@ fn check_ident_suffix( if let Content::Structure(FlatType::Func(_, _, _, fx)) = env.subs.get_content_without_compacting(variable) { - if let Content::Effectful = env.subs.get_content_without_compacting(*fx) { - problems.push(TypeError::UnsuffixedEffectfulFunction(*region, kind)); + let fx = *fx; + match env.subs.get_content_without_compacting(fx) { + Content::Effectful => { + problems.push(TypeError::UnsuffixedEffectfulFunction(*region, kind)); + } + Content::FlexVar(_) => { + env.subs.set_content(fx, Content::Pure); + } + _ => {} } } } - IdentSuffix::Bang => { - if let Content::Structure(FlatType::Func(_, _, _, fx)) = - env.subs.get_content_without_compacting(variable) - { - if let Content::Pure = env.subs.get_content_without_compacting(*fx) { - problems.push(TypeError::SuffixedPureFunction(*region, kind)); + IdentSuffix::Bang => match env.subs.get_content_without_compacting(variable) { + Content::Structure(FlatType::Func(_, _, _, fx)) => { + let fx = *fx; + match env.subs.get_content_without_compacting(fx) { + Content::Pure => { + problems.push(TypeError::SuffixedPureFunction(*region, kind)); + } + Content::FlexVar(_) => { + env.subs.set_content(fx, Content::Effectful); + } + _ => {} } } - } + Content::FlexVar(_) => { + // [purity-inference] TODO: Require effectful fn + } + _ => {} + }, } } From cfc4be5254b1f754d894e0f18a3d16f6fdba0404 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 23 Oct 2024 22:33:57 -0300 Subject: [PATCH 51/73] Treat untyped suffixed functions as effectful --- crates/cli/tests/cli_run.rs | 21 ++++++++++++++++ .../cli/tests/effectful/untyped_passed_fx.roc | 13 ++++++++++ crates/compiler/can/src/copy.rs | 2 +- crates/compiler/can/src/exhaustive.rs | 1 + crates/compiler/checkmate/schema.json | 14 +++++++++++ crates/compiler/checkmate/src/convert.rs | 1 + crates/compiler/checkmate_schema/src/lib.rs | 1 + crates/compiler/constrain/src/pattern.rs | 25 ++++++++++++++----- crates/compiler/derive_key/src/decoding.rs | 2 +- crates/compiler/derive_key/src/encoding.rs | 2 +- crates/compiler/derive_key/src/hash.rs | 2 +- crates/compiler/derive_key/src/inspect.rs | 3 +++ .../compiler/lower_params/src/type_error.rs | 3 ++- crates/compiler/mono/src/ir.rs | 1 + crates/compiler/mono/src/layout.rs | 2 ++ crates/compiler/solve/src/ability.rs | 6 +++++ crates/compiler/solve/src/deep_copy.rs | 2 +- crates/compiler/solve/src/solve.rs | 5 +++- crates/compiler/types/src/pretty_print.rs | 3 +++ crates/compiler/types/src/subs.rs | 21 +++++++++++++--- crates/compiler/types/src/types.rs | 3 +++ crates/compiler/unify/src/unify.rs | 11 ++++++++ crates/glue/src/load.rs | 3 ++- crates/glue/src/types.rs | 3 ++- crates/reporting/src/error/type.rs | 10 ++++++-- 25 files changed, 139 insertions(+), 21 deletions(-) create mode 100644 crates/cli/tests/effectful/untyped_passed_fx.roc diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 99c46cb4481..e82f0718dc2 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -986,6 +986,27 @@ mod cli_run { ); } + #[test] + #[cfg_attr(windows, ignore)] + fn effectful_untyped_passed_fx() { + test_roc_app( + "crates/cli/tests/effectful", + "untyped_passed_fx.roc", + &[], + &[], + &[], + indoc!( + r#" + Before hello + Hello, World! + After hello + "# + ), + UseValgrind::No, + TestCliCommands::Dev, + ); + } + #[test] #[cfg_attr(windows, ignore)] fn transitive_expects() { diff --git a/crates/cli/tests/effectful/untyped_passed_fx.roc b/crates/cli/tests/effectful/untyped_passed_fx.roc new file mode 100644 index 00000000000..d0021f41a6f --- /dev/null +++ b/crates/cli/tests/effectful/untyped_passed_fx.roc @@ -0,0 +1,13 @@ +app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" } + +import pf.Effect + +main! : {} => {} +main! = \{} -> + logged! "hello" (\{} -> Effect.putLine! "Hello, World!") + + +logged! = \name, fx! -> + Effect.putLine! "Before $(name)" + fx! {} + Effect.putLine! "After $(name)" diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 3e1214ee283..47d0f7269b5 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -984,7 +984,7 @@ fn deep_copy_type_vars( // Everything else is a mechanical descent. Structure(flat_type) => match flat_type { - EmptyRecord | EmptyTagUnion => Structure(flat_type), + EmptyRecord | EmptyTagUnion | EffectfulFunc => Structure(flat_type), Apply(symbol, arguments) => { descend_slice!(arguments); diff --git a/crates/compiler/can/src/exhaustive.rs b/crates/compiler/can/src/exhaustive.rs index 705cfdc8ea0..621dc407d5a 100644 --- a/crates/compiler/can/src/exhaustive.rs +++ b/crates/compiler/can/src/exhaustive.rs @@ -153,6 +153,7 @@ fn index_var( } Content::Structure(structure) => match structure { FlatType::Func(_, _, _, _) => return Err(TypeError), + FlatType::EffectfulFunc => return Err(TypeError), FlatType::Apply(Symbol::LIST_LIST, args) => { match (subs.get_subs_slice(*args), ctor) { ([elem_var], IndexCtor::List) => { diff --git a/crates/compiler/checkmate/schema.json b/crates/compiler/checkmate/schema.json index 2944d379f78..23d33cc889a 100644 --- a/crates/compiler/checkmate/schema.json +++ b/crates/compiler/checkmate/schema.json @@ -514,6 +514,20 @@ } } }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "EffectfulFunc" + ] + } + } + }, { "type": "object", "required": [ diff --git a/crates/compiler/checkmate/src/convert.rs b/crates/compiler/checkmate/src/convert.rs index 2e1dcd261e7..566d24b94cf 100644 --- a/crates/compiler/checkmate/src/convert.rs +++ b/crates/compiler/checkmate/src/convert.rs @@ -127,6 +127,7 @@ impl AsSchema for subs::FlatType { ), subs::FlatType::EmptyRecord => Content::EmptyRecord(), subs::FlatType::EmptyTagUnion => Content::EmptyTagUnion(), + subs::FlatType::EffectfulFunc => Content::EffectfulFunc(), } } } diff --git a/crates/compiler/checkmate_schema/src/lib.rs b/crates/compiler/checkmate_schema/src/lib.rs index f1d5ff3ce43..d06a010e575 100644 --- a/crates/compiler/checkmate_schema/src/lib.rs +++ b/crates/compiler/checkmate_schema/src/lib.rs @@ -99,6 +99,7 @@ impl_content! { }, EmptyRecord {}, EmptyTagUnion {}, + EffectfulFunc {}, RangedNumber { range: NumericRange, }, diff --git a/crates/compiler/constrain/src/pattern.rs b/crates/compiler/constrain/src/pattern.rs index 6a5a0e7b311..4f0bda5433d 100644 --- a/crates/compiler/constrain/src/pattern.rs +++ b/crates/compiler/constrain/src/pattern.rs @@ -6,7 +6,7 @@ use roc_can::pattern::Pattern::{self, *}; use roc_can::pattern::{DestructType, ListPatterns, RecordDestruct, TupleDestruct}; use roc_collections::all::{HumanIndex, SendMap}; use roc_collections::VecMap; -use roc_module::ident::Lowercase; +use roc_module::ident::{IdentSuffix, Lowercase}; use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; @@ -299,12 +299,25 @@ pub fn constrain_pattern_help( .push(constraints.is_open_type(type_index)); } + // Identifiers introduced in nested patterns get let constraints + // and therefore don't need fx_pattern_suffix constraints. if is_shallow { - // Identifiers introduced in nested patterns get let constraints - // and therefore don't need fx_pattern_suffix constraints. - state - .delayed_fx_suffix_constraints - .push(constraints.fx_pattern_suffix(*symbol, type_index, region)); + match symbol.suffix() { + IdentSuffix::None => { + // Unsuffixed identifiers should be constrained after we know if they're functions + state + .delayed_fx_suffix_constraints + .push(constraints.fx_pattern_suffix(*symbol, type_index, region)); + } + IdentSuffix::Bang => { + // Bang suffixed identifiers are always required to be functions + // We constrain this before the function's body, + // so that we don't think it's pure and complain about leftover statements + state + .constraints + .push(constraints.fx_pattern_suffix(*symbol, type_index, region)); + } + } } state.headers.insert( diff --git a/crates/compiler/derive_key/src/decoding.rs b/crates/compiler/derive_key/src/decoding.rs index e819209cffd..c151ce061a3 100644 --- a/crates/compiler/derive_key/src/decoding.rs +++ b/crates/compiler/derive_key/src/decoding.rs @@ -81,7 +81,7 @@ impl FlatDecodable { Err(Underivable) // yet } // - FlatType::Func(..) => Err(Underivable), + FlatType::Func(..) | FlatType::EffectfulFunc => Err(Underivable), }, Content::Alias(sym, _, real_var, _) => match from_builtin_symbol(sym) { Some(lambda) => lambda, diff --git a/crates/compiler/derive_key/src/encoding.rs b/crates/compiler/derive_key/src/encoding.rs index 39dee1f5540..f30d3788ab9 100644 --- a/crates/compiler/derive_key/src/encoding.rs +++ b/crates/compiler/derive_key/src/encoding.rs @@ -117,7 +117,7 @@ impl FlatEncodable { } FlatType::EmptyRecord => Ok(Key(FlatEncodableKey::Record(vec![]))), FlatType::EmptyTagUnion => Ok(Key(FlatEncodableKey::TagUnion(vec![]))), - FlatType::Func(..) => Err(Underivable), + FlatType::Func(..) | FlatType::EffectfulFunc => Err(Underivable), }, Content::Alias(sym, _, real_var, _) => match from_builtin_symbol(sym) { Some(lambda) => lambda, diff --git a/crates/compiler/derive_key/src/hash.rs b/crates/compiler/derive_key/src/hash.rs index 9502abfef34..c196ecd1513 100644 --- a/crates/compiler/derive_key/src/hash.rs +++ b/crates/compiler/derive_key/src/hash.rs @@ -112,7 +112,7 @@ impl FlatHash { FlatType::EmptyRecord => Ok(Key(FlatHashKey::Record(vec![]))), FlatType::EmptyTagUnion => Ok(Key(FlatHashKey::TagUnion(vec![]))), // - FlatType::Func(..) => Err(Underivable), + FlatType::Func(..) | FlatType::EffectfulFunc => Err(Underivable), }, Content::Alias(sym, _, real_var, _) => match builtin_symbol_to_hash_lambda(sym) { Some(lambda) => Ok(lambda), diff --git a/crates/compiler/derive_key/src/inspect.rs b/crates/compiler/derive_key/src/inspect.rs index 30470b9bf7c..3bf22c9177e 100644 --- a/crates/compiler/derive_key/src/inspect.rs +++ b/crates/compiler/derive_key/src/inspect.rs @@ -129,6 +129,9 @@ impl FlatInspectable { FlatType::EmptyRecord => Key(FlatInspectableKey::Record(Vec::new())), FlatType::EmptyTagUnion => Key(FlatInspectableKey::TagUnion(Vec::new())), FlatType::Func(..) => Immediate(Symbol::INSPECT_FUNCTION), + FlatType::EffectfulFunc => { + unreachable!("There must have been a bug in the solver, because we're trying to derive Inspect on a non-concrete type."); + } }, Content::Alias(sym, _, real_var, kind) => match Self::from_builtin_alias(sym) { Some(lambda) => lambda, diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index a679a33c285..4d54f932833 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -210,6 +210,7 @@ fn drop_last_argument(err_type: &mut ErrorType) { | ErrorType::RecursiveTagUnion(_, _, _, _) | ErrorType::Alias(_, _, _, _) | ErrorType::Range(_) - | ErrorType::Error => {} + | ErrorType::Error + | ErrorType::EffectfulFunc => {} } } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index b4da4740b97..6d1a6f66b63 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -10180,6 +10180,7 @@ fn find_lambda_sets_help( } FlatType::EmptyRecord => {} FlatType::EmptyTagUnion => {} + FlatType::EffectfulFunc => {} }, Content::Alias(_, _, actual, _) => { stack.push(*actual); diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 7c64dcf801a..038167cc9d1 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -2192,6 +2192,7 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) { } stack.push((ext.var(), depth_any + 1, depth_lset)); } + FlatType::EffectfulFunc => internal_error!(), FlatType::EmptyRecord | FlatType::EmptyTagUnion => {} }, Content::FlexVar(_) @@ -3465,6 +3466,7 @@ fn layout_from_flat_type<'a>( } EmptyTagUnion => cacheable(Ok(Layout::VOID)), EmptyRecord => cacheable(Ok(Layout::UNIT)), + EffectfulFunc => internal_error!(), } } diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 358f139ab00..b9156ee97e3 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -780,6 +780,12 @@ trait DerivableVisitor { } EmptyRecord => Self::visit_empty_record(var)?, EmptyTagUnion => Self::visit_empty_tag_union(var)?, + EffectfulFunc => { + return Err(NotDerivable { + var, + context: NotDerivableContext::NoContext, + }) + } }, Alias( Symbol::NUM_NUM | Symbol::NUM_INTEGER, diff --git a/crates/compiler/solve/src/deep_copy.rs b/crates/compiler/solve/src/deep_copy.rs index bd3d16e9598..0ea8b2e6575 100644 --- a/crates/compiler/solve/src/deep_copy.rs +++ b/crates/compiler/solve/src/deep_copy.rs @@ -187,7 +187,7 @@ fn deep_copy_var_help( Func(new_arguments, new_closure_var, new_ret_var, new_fx_var) } - same @ EmptyRecord | same @ EmptyTagUnion => same, + same @ (EmptyRecord | EmptyTagUnion | EffectfulFunc) => same, Record(fields, ext_var) => { let record_fields = { diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index b84f6219c06..91fce0ce977 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1660,7 +1660,8 @@ fn solve_suffix_fx( } } Content::FlexVar(_) => { - // [purity-inference] TODO: Require effectful fn + env.subs + .set_content(variable, Content::Structure(FlatType::EffectfulFunc)); } _ => {} }, @@ -2379,6 +2380,8 @@ fn adjust_rank_content( // THEORY: an empty tag never needs to get generalized EmptyTagUnion => Rank::toplevel(), + EffectfulFunc => Rank::toplevel(), + Record(fields, ext_var) => { let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var); diff --git a/crates/compiler/types/src/pretty_print.rs b/crates/compiler/types/src/pretty_print.rs index a2ff7db2e1c..b10073e3602 100644 --- a/crates/compiler/types/src/pretty_print.rs +++ b/crates/compiler/types/src/pretty_print.rs @@ -15,6 +15,7 @@ use roc_module::symbol::{Interns, ModuleId, Symbol}; pub static WILDCARD: &str = "*"; static EMPTY_RECORD: &str = "{}"; static EMPTY_TAG_UNION: &str = "[]"; +static EFFECTFUL_FUNC: &str = "! : ... => ?"; /// Requirements for parentheses. /// @@ -217,6 +218,7 @@ fn find_names_needed( find_under_alias, ); } + Structure(EffectfulFunc) => {} Structure(Record(sorted_fields, ext_var)) => { for index in sorted_fields.iter_variables() { let var = subs[index]; @@ -1139,6 +1141,7 @@ fn write_flat_type<'a>( parens, pol, ), + EffectfulFunc => buf.push_str(EFFECTFUL_FUNC), Record(fields, ext_var) => { use crate::types::{gather_fields, RecordStructure}; diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index 587e545898d..c64bf9fd3c6 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -833,6 +833,7 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f } FlatType::EmptyRecord => write!(f, "EmptyRecord"), FlatType::EmptyTagUnion => write!(f, "EmptyTagUnion"), + FlatType::EffectfulFunc => write!(f, "EffectfulFunc"), } } @@ -2584,6 +2585,8 @@ impl TagExt { pub enum FlatType { Apply(Symbol, VariableSubsSlice), Func(VariableSubsSlice, Variable, Variable, Variable), + /// A function that we know nothing about yet except that it's effectful + EffectfulFunc, Record(RecordFields, Variable), Tuple(TupleElems, Variable), TagUnion(UnionTags, TagExt), @@ -3492,7 +3495,7 @@ fn occurs( short_circuit_help(subs, root_var, ctx, ext_var) } - EmptyRecord | EmptyTagUnion => Ok(()), + EmptyRecord | EmptyTagUnion | EffectfulFunc => Ok(()), }, Alias(_, args, _, _) => { // THEORY: we only need to explore the args, as that is the surface of all @@ -3686,7 +3689,7 @@ fn explicit_substitute( subs.set_content(in_var, Structure(Tuple(vars_by_elem, new_ext))); } - EmptyRecord | EmptyTagUnion => {} + EmptyRecord | EmptyTagUnion | EffectfulFunc => {} } in_var @@ -3872,7 +3875,9 @@ fn get_var_names( accum } - FlatType::EmptyRecord | FlatType::EmptyTagUnion => taken_names, + FlatType::EmptyRecord | FlatType::EmptyTagUnion | FlatType::EffectfulFunc => { + taken_names + } FlatType::Record(vars_by_field, ext) => { let mut accum = get_var_names(subs, ext, taken_names); @@ -4236,6 +4241,7 @@ fn flat_type_to_err_type( ErrorType::Function(args, Box::new(closure), fx, Box::new(ret)) } + EffectfulFunc => ErrorType::EffectfulFunc, EmptyRecord => ErrorType::Record(SendMap::default(), TypeExt::Closed), EmptyTagUnion => ErrorType::TagUnion(SendMap::default(), TypeExt::Closed, pol), @@ -4694,6 +4700,7 @@ impl StorageSubs { ), FlatType::EmptyRecord => FlatType::EmptyRecord, FlatType::EmptyTagUnion => FlatType::EmptyTagUnion, + FlatType::EffectfulFunc => FlatType::EffectfulFunc, } } @@ -5084,6 +5091,8 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) -> RecursiveTagUnion(new_rec_var, union_tags, new_ext) } + + EffectfulFunc => EffectfulFunc, }; env.target @@ -5552,6 +5561,7 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl RecursiveTagUnion(new_rec_var, union_tags, new_ext) } + EffectfulFunc => EffectfulFunc, }; env.target @@ -5890,6 +5900,8 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) { stack.push(ext.var()); stack.push(rec_var); } + + EffectfulFunc => {} }, Alias(_, args, var, _) => { let var = *var; @@ -5997,7 +6009,7 @@ pub fn get_member_lambda_sets_at_region(subs: &Subs, var: Variable, target_regio ); stack.push(ext.var()); } - FlatType::EmptyRecord | FlatType::EmptyTagUnion => {} + FlatType::EffectfulFunc | FlatType::EmptyRecord | FlatType::EmptyTagUnion => {} }, Content::Alias(_, _, real_var, _) => { stack.push(*real_var); @@ -6073,6 +6085,7 @@ fn is_inhabited(subs: &Subs, var: Variable) -> bool { return false; } } + FlatType::EffectfulFunc => {} FlatType::FunctionOrTagUnion(_, _, _) => {} FlatType::EmptyRecord => {} FlatType::EmptyTagUnion => { diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 334e48b4e75..fffecf93380 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3660,6 +3660,7 @@ pub enum ErrorType { /// If the name was auto-generated, it will start with a `#`. FlexVar(Lowercase), RigidVar(Lowercase), + EffectfulFunc, /// If the name was auto-generated, it will start with a `#`. FlexAbleVar(Lowercase, AbilitySet), RigidAbleVar(Lowercase, AbilitySet), @@ -3750,6 +3751,7 @@ impl ErrorType { t.add_names(taken); }); } + EffectfulFunc => {} Error => {} } } @@ -3902,6 +3904,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: buf.push(')'); } } + EffectfulFunc => buf.push_str("EffectfulFunc"), Type(symbol, arguments) => { let write_parens = parens == Parens::InTypeParam && !arguments.is_empty(); diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 9ca4bd00555..bc23230ad5e 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -3334,6 +3334,17 @@ fn unify_flat_type( outcome } + (EffectfulFunc, Func(args, closure, ret, fx)) => { + let mut outcome = unify_pool(env, pool, Variable::EFFECTFUL, *fx, ctx.mode); + + outcome.union(merge( + env, + ctx, + Structure(Func(*args, *closure, *ret, Variable::EFFECTFUL)), + )); + + outcome + } (FunctionOrTagUnion(tag_names, tag_symbols, ext), Func(args, closure, ret, fx)) => { unify_function_or_tag_union_and_func( env, diff --git a/crates/glue/src/load.rs b/crates/glue/src/load.rs index 78c53136f7f..027b431a730 100644 --- a/crates/glue/src/load.rs +++ b/crates/glue/src/load.rs @@ -10,7 +10,7 @@ use roc_build::{ }, }; use roc_collections::MutMap; -use roc_error_macros::todo_lambda_erasure; +use roc_error_macros::{internal_error, todo_lambda_erasure}; use roc_gen_llvm::run_roc::RocCallResult; use roc_load::{ExecutionMode, FunctionKind, LoadConfig, LoadedModule, LoadingProblem, Threading}; use roc_mono::ir::{generate_glue_procs, CrashTag, GlueProc, OptLevel}; @@ -324,6 +324,7 @@ fn number_lambda_sets(subs: &Subs, initial: Variable) -> Vec { EmptyRecord => (), EmptyTagUnion => (), + EffectfulFunc => internal_error!(), Record(fields, ext) => { let fields = *fields; diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index b8ec3334f91..2ec9b13d521 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -1340,7 +1340,8 @@ fn add_type_help<'a>( Content::FlexVar(_) | Content::RigidVar(_) | Content::FlexAbleVar(_, _) - | Content::RigidAbleVar(_, _) => { + | Content::RigidAbleVar(_, _) + | Content::Structure(FlatType::EffectfulFunc) => { todo!("TODO give a nice error message for a non-concrete type being passed to the host") } Content::Structure(FlatType::Tuple(..)) => { diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index d890bc7f345..4bd497bbc5b 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -2767,6 +2767,8 @@ fn to_doc_help<'b>( alloc.type_variable(lowercase) } + EffectfulFunc => alloc.text("effectful function"), + Type(symbol, args) => report_text::apply( alloc, parens, @@ -2961,6 +2963,7 @@ fn count_generated_name_usages<'a>( RigidVar(name) | RigidAbleVar(name, _) => { debug_assert!(!is_generated_name(name)); } + EffectfulFunc => {} Type(_, tys) => { stack.extend(tys.iter().map(|t| (t, only_unseen))); } @@ -3820,7 +3823,9 @@ fn should_show_diff(t1: &ErrorType, t2: &ErrorType) -> bool { | (RecursiveTagUnion(_, _, _, _), _) | (_, RecursiveTagUnion(_, _, _, _)) | (Function(_, _, _, _), _) - | (_, Function(_, _, _, _)) => true, + | (_, Function(_, _, _, _)) + | (EffectfulFunc, _) + | (_, EffectfulFunc) => true, } } @@ -4861,7 +4866,7 @@ fn type_problem_to_pretty<'b>( }; match tipe { - Infinite | Error | FlexVar(_) => alloc.nil(), + Infinite | Error | FlexVar(_) | EffectfulFunc => alloc.nil(), FlexAbleVar(_, other_abilities) => { rigid_able_vs_different_flex_able(x, abilities, other_abilities) } @@ -4954,6 +4959,7 @@ fn type_problem_to_pretty<'b>( }; bad_rigid_var(x, msg) } + EffectfulFunc => alloc.reflow("an effectful function"), RigidVar(y) | RigidAbleVar(y, _) => bad_double_rigid(x, y), Function(_, _, _, _) => bad_rigid_var(x, alloc.reflow("a function value")), Record(_, _) => bad_rigid_var(x, alloc.reflow("a record value")), From 175a2b5683979ee9ed6768ccfe03d781c6fdde5e Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 23 Oct 2024 23:02:54 -0300 Subject: [PATCH 52/73] Add hint about forgetting to call a function --- crates/compiler/load/tests/test_reporting.rs | 42 ++++++++++++++++++++ crates/reporting/src/error/type.rs | 25 +++++++----- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 2df423a9cc7..6ef495c4f71 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14839,6 +14839,48 @@ All branches in an `if` must have the same type! "### ); + test_report!( + ignored_stmt_forgot_to_call, + indoc!( + r#" + app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" } + + import pf.Effect + + main! = \{} -> + Effect.getLine! + Effect.putLine! "hi" + "# + ), + @r###" + ── IGNORED RESULT in /code/proj/Main.roc ─────────────────────────────────────── + + The result of this expression is ignored: + + 6│ Effect.getLine! + ^^^^^^^^^^^^^^^ + + Standalone statements are required to produce an empty record, but the + type of this one is: + + {} => Str + + Hint: Did you forget to call the function? + + ── LEFTOVER STATEMENT in /code/proj/Main.roc ─────────────────────────────────── + + This statement does not produce any effects: + + 6│ Effect.getLine! + ^^^^^^^^^^^^^^^ + + Standalone statements are only useful if they call effectful + functions. + + Did you forget to use its result? If not, feel free to remove it. + "### + ); + test_report!( function_def_leftover_bang, indoc!( diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 4bd497bbc5b..f94451a11f1 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1825,7 +1825,7 @@ fn to_expr_report<'b>( } Reason::Stmt(opt_name) => { - let diff = to_diff(alloc, Parens::Unnecessary, found, expected_type); + let diff = to_diff(alloc, Parens::Unnecessary, found.clone(), expected_type); let lines = [ match opt_name { @@ -1839,14 +1839,21 @@ fn to_expr_report<'b>( alloc.region(lines.convert_region(region), severity), alloc.reflow("Standalone statements are required to produce an empty record, but the type of this one is:"), alloc.type_block(type_with_able_vars(alloc, diff.left, diff.left_able)), - alloc.concat([ - alloc.reflow("If you still want to ignore it, assign it to "), - alloc.keyword("_"), - alloc.reflow(", like this:"), - ]), - alloc - .parser_suggestion("_ = File.delete! \"data.json\"") - .indent(4), + match found { + ErrorType::EffectfulFunc | ErrorType::Function(_, _, _, _) => { + alloc.hint("Did you forget to call the function?") + } + _ => alloc.stack([ + alloc.concat([ + alloc.reflow("If you still want to ignore it, assign it to "), + alloc.keyword("_"), + alloc.reflow(", like this:"), + ]), + alloc + .parser_suggestion("_ = File.delete! \"data.json\"") + .indent(4), + ]) + }, ]; Report { From c9f001b041ea755097e0de00035bfc3df14992cc Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 24 Oct 2024 00:24:49 -0300 Subject: [PATCH 53/73] Allow ignored defs with an effectful RHS --- crates/cli/tests/effectful/ignore_result.roc | 8 ++ crates/compiler/can/src/constraint.rs | 17 ++- crates/compiler/can/src/copy.rs | 1 + crates/compiler/can/src/debug/pretty_print.rs | 1 + crates/compiler/can/src/def.rs | 25 +++-- crates/compiler/constrain/src/expr.rs | 64 ++++++++--- crates/compiler/load/tests/test_reporting.rs | 102 ++++-------------- .../compiler/lower_params/src/type_error.rs | 2 +- crates/compiler/problem/src/can.rs | 3 - crates/compiler/solve/src/solve.rs | 4 +- crates/compiler/solve_problem/src/lib.rs | 8 +- crates/reporting/src/error/canonicalize.rs | 8 -- crates/reporting/src/error/type.rs | 19 +++- 13 files changed, 136 insertions(+), 126 deletions(-) create mode 100644 crates/cli/tests/effectful/ignore_result.roc diff --git a/crates/cli/tests/effectful/ignore_result.roc b/crates/cli/tests/effectful/ignore_result.roc new file mode 100644 index 00000000000..38c990f88e3 --- /dev/null +++ b/crates/cli/tests/effectful/ignore_result.roc @@ -0,0 +1,8 @@ +app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" } + +import pf.Effect + +main! : {} => {} +main! = \{} -> + _ = Effect.getLine! {} + Effect.putLine! "I asked for input and I ignored it. Deal with it! 😎" diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index e2787bb4b8c..ac1304138b5 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -663,7 +663,7 @@ impl Constraints { | Constraint::Store(..) | Constraint::Lookup(..) | Constraint::Pattern(..) - | Constraint::EffectfulStmt(..) + | Constraint::ExpectEffectful(..) | Constraint::FxCall(_) | Constraint::FxSuffix(_) | Constraint::FlexToPure(_) @@ -845,8 +845,8 @@ pub enum Constraint { FxSuffix(Index), /// Set an fx var as pure if flex (no effectful functions were called) FlexToPure(Variable), - /// Expect statement to be effectful - EffectfulStmt(Variable, Region), + /// Expect statement or ignored def to be effectful + ExpectEffectful(Variable, ExpectEffectfulReason, Region), /// Used for things that always unify, e.g. blanks and runtime errors True, SaveTheEnvironment, @@ -937,6 +937,7 @@ pub struct FxExpectation { pub enum FxCallKind { Call(Option), Stmt, + Ignored, } #[derive(Debug, Clone, Copy)] @@ -962,6 +963,12 @@ impl FxSuffixKind { } } +#[derive(Debug, Clone, Copy)] +pub enum ExpectEffectfulReason { + Stmt, + Ignored, +} + /// Custom impl to limit vertical space used by the debug output impl std::fmt::Debug for Constraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -984,8 +991,8 @@ impl std::fmt::Debug for Constraint { Self::FxSuffix(arg0) => { write!(f, "FxSuffix({arg0:?})") } - Self::EffectfulStmt(arg0, arg1) => { - write!(f, "EffectfulStmt({arg0:?}, {arg1:?})") + Self::ExpectEffectful(arg0, arg1, arg2) => { + write!(f, "EffectfulStmt({arg0:?}, {arg1:?}, {arg2:?})") } Self::FlexToPure(arg0) => { write!(f, "FlexToPure({arg0:?})") diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 47d0f7269b5..860d1d8e4a3 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -390,6 +390,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr kind: match kind { DefKind::Let => DefKind::Let, DefKind::Stmt(v) => DefKind::Stmt(sub!(*v)), + DefKind::Ignored(v) => DefKind::Ignored(sub!(*v)), }, }, ) diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index 77088f3e261..6d36a8af9d7 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -112,6 +112,7 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> { match kind { DefKind::Let => def_help(c, f, &loc_pattern.value, &loc_expr.value), + DefKind::Ignored(_) => def_help(c, f, &loc_pattern.value, &loc_expr.value), DefKind::Stmt(_) => expr(c, EPrec::Free, f, &loc_expr.value), } } diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 1de569a62e8..eeea665346c 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -97,6 +97,8 @@ pub enum DefKind { Let, /// A standalone statement with an fx variable Stmt(Variable), + /// Ignored result, must be effectful + Ignored(Variable), } impl DefKind { @@ -104,6 +106,19 @@ impl DefKind { match self { DefKind::Let => DefKind::Let, DefKind::Stmt(v) => DefKind::Stmt(f(v)), + DefKind::Ignored(v) => DefKind::Ignored(f(v)), + } + } + + pub fn from_pattern(var_store: &mut VarStore, pattern: &Loc) -> Self { + if BindingsFromPattern::new(pattern) + .peekable() + .peek() + .is_none() + { + DefKind::Ignored(var_store.fresh()) + } else { + DefKind::Let } } } @@ -1212,11 +1227,7 @@ fn canonicalize_value_defs<'a>( for (def_index, pending_def) in pending_value_defs.iter().enumerate() { if let Some(loc_pattern) = pending_def.loc_pattern() { - let mut new_bindings = BindingsFromPattern::new(loc_pattern).peekable(); - - if new_bindings.peek().is_none() { - env.problem(Problem::NoIdentifiersIntroduced(loc_pattern.region)); - } + let new_bindings = BindingsFromPattern::new(loc_pattern).peekable(); for (s, r) in new_bindings { // store the top-level defs, used to ensure that closures won't capture them @@ -2439,6 +2450,8 @@ fn canonicalize_pending_value_def<'a>( } Body(loc_can_pattern, loc_expr) => { // + let def_kind = DefKind::from_pattern(var_store, &loc_can_pattern); + canonicalize_pending_body( env, output, @@ -2447,7 +2460,7 @@ fn canonicalize_pending_value_def<'a>( loc_can_pattern, loc_expr, None, - DefKind::Let, + def_kind, ) } Stmt(loc_expr) => { diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index df0295bcd9a..ed8847043e6 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -8,8 +8,8 @@ use crate::builtins::{ use crate::pattern::{constrain_pattern, PatternState}; use roc_can::annotation::IntroducedVariables; use roc_can::constraint::{ - Constraint, Constraints, ExpectedTypeIndex, FxCallKind, FxExpectation, Generalizable, - OpportunisticResolve, TypeOrVar, + Constraint, Constraints, ExpectEffectfulReason, ExpectedTypeIndex, FxCallKind, FxExpectation, + Generalizable, OpportunisticResolve, TypeOrVar, }; use roc_can::def::{Def, DefKind}; use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext}; @@ -1458,10 +1458,13 @@ pub fn constrain_expr( while let Some(def) = stack.pop() { body_con = match def.kind { - DefKind::Let => constrain_let_def(types, constraints, env, def, body_con), + DefKind::Let => constrain_let_def(types, constraints, env, def, body_con, None), DefKind::Stmt(fx_var) => { constrain_stmt_def(types, constraints, env, def, body_con, fx_var) } + DefKind::Ignored(fx_var) => { + constrain_let_def(types, constraints, env, def, body_con, Some(fx_var)) + } }; } @@ -3434,6 +3437,7 @@ fn constrain_let_def( env: &mut Env, def: &Def, body_con: Constraint, + ignored_fx_var: Option, ) -> Constraint { match &def.annotation { Some(annotation) => constrain_typed_def(types, constraints, env, def, body_con, annotation), @@ -3448,14 +3452,49 @@ fn constrain_let_def( // no annotation, so no extra work with rigids let expected = constraints.push_expected_type(NoExpectation(expr_type_index)); - let expr_con = constrain_expr( - types, - constraints, - env, - def.loc_expr.region, - &def.loc_expr.value, - expected, - ); + + let expr_con = match ignored_fx_var { + None => constrain_expr( + types, + constraints, + env, + def.loc_expr.region, + &def.loc_expr.value, + expected, + ), + Some(fx_var) => { + let expr_con = env.with_fx_expectation(fx_var, None, |env| { + constrain_expr( + types, + constraints, + env, + def.loc_expr.region, + &def.loc_expr.value, + expected, + ) + }); + + // Ignored def must be effectful, otherwise it's dead code + let effectful_constraint = Constraint::ExpectEffectful( + fx_var, + ExpectEffectfulReason::Ignored, + def.loc_pattern.region, + ); + + let enclosing_fx_constraint = constraints.fx_call( + fx_var, + FxCallKind::Ignored, + def.loc_pattern.region, + env.fx_expectation, + ); + + constraints.and_constraint([ + expr_con, + enclosing_fx_constraint, + effectful_constraint, + ]) + } + }; let expr_con = attach_resolution_constraints(constraints, env, expr_con); let generalizable = Generalizable(is_generalizable_expr(&def.loc_expr.value)); @@ -3528,7 +3567,8 @@ fn constrain_stmt_def( ); // Stmt expr must be effectful, otherwise it's dead code - let effectful_constraint = Constraint::EffectfulStmt(fx_var, region); + let effectful_constraint = + Constraint::ExpectEffectful(fx_var, ExpectEffectfulReason::Stmt, region); let fx_call_kind = match fn_name { None => FxCallKind::Stmt, diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 6ef495c4f71..d3f5d032b9d 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -10170,7 +10170,7 @@ All branches in an `if` must have the same type! main = \n -> n + 2 "# ), - @r" + @r###" ── DUPLICATE NAME in /code/proj/Main.roc ─────────────────────────────────────── The `main` name is first defined here: @@ -10185,19 +10185,7 @@ All branches in an `if` must have the same type! Since these variables have the same name, it's easy to use the wrong one by accident. Give one of them a new name. - - ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - - This destructure assignment doesn't introduce any new variables: - - 5│ main = \n -> n + 2 - ^^^^ - - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! - " + "### ); test_report!( @@ -10843,51 +10831,47 @@ All branches in an `if` must have the same type! @r###" ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - This destructure assignment doesn't introduce any new variables: + This assignment doesn't introduce any new variables: 4│ Pair _ _ = Pair 0 1 ^^^^^^^^ - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! + Since it doesn't call any effectful functions, this assignment cannot + affect the program's behavior. If you don't need to use the value on + the right-hand-side, consider removing the assignment. ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - This destructure assignment doesn't introduce any new variables: + This assignment doesn't introduce any new variables: 6│ _ = Pair 0 1 ^ - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! + Since it doesn't call any effectful functions, this assignment cannot + affect the program's behavior. If you don't need to use the value on + the right-hand-side, consider removing the assignment. ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - This destructure assignment doesn't introduce any new variables: + This assignment doesn't introduce any new variables: 8│ {} = {} ^^ - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! + Since it doesn't call any effectful functions, this assignment cannot + affect the program's behavior. If you don't need to use the value on + the right-hand-side, consider removing the assignment. ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - This destructure assignment doesn't introduce any new variables: + This assignment doesn't introduce any new variables: 10│ Foo = Foo ^^^ - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! + Since it doesn't call any effectful functions, this assignment cannot + affect the program's behavior. If you don't need to use the value on + the right-hand-side, consider removing the assignment. "### ); @@ -10906,55 +10890,7 @@ All branches in an `if` must have the same type! Foo = Foo "# ), - @r" - ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - - This destructure assignment doesn't introduce any new variables: - - 3│ Pair _ _ = Pair 0 1 - ^^^^^^^^ - - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! - - ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - - This destructure assignment doesn't introduce any new variables: - - 5│ _ = Pair 0 1 - ^ - - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! - - ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - - This destructure assignment doesn't introduce any new variables: - - 7│ {} = {} - ^^ - - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! - - ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── - - This destructure assignment doesn't introduce any new variables: - - 9│ Foo = Foo - ^^^ - - If you don't need to use the value on the right-hand-side of this - assignment, consider removing the assignment. Since Roc is purely - functional, assignments that don't introduce variables cannot affect a - program's behavior! - " + @"" ); test_report!( diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index 4d54f932833..70e0a4d2844 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -102,7 +102,7 @@ pub fn remove_module_param_arguments( | TypeError::ModuleParamsMismatch(_, _, _, _) | TypeError::FxInPureFunction(_, _, _) | TypeError::FxInTopLevel(_, _) - | TypeError::PureStmt(_) + | TypeError::ExpectedEffectful(_, _) | TypeError::UnsuffixedEffectfulFunction(_, _) | TypeError::SuffixedPureFunction(_, _) => {} } diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index 63e66441031..eb80c8e1a0a 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -195,7 +195,6 @@ pub enum Problem { unbound_symbol: Symbol, region: Region, }, - NoIdentifiersIntroduced(Region), OverloadedSpecialization { overload: Region, original_opaque: Symbol, @@ -318,7 +317,6 @@ impl Problem { Problem::ImplementsNonRequired { .. } => Warning, Problem::DoesNotImplementAbility { .. } => RuntimeError, Problem::NotBoundInAllPatterns { .. } => RuntimeError, - Problem::NoIdentifiersIntroduced(_) => Warning, Problem::OverloadedSpecialization { .. } => Warning, // Ideally, will compile Problem::UnnecessaryOutputWildcard { .. } => Warning, // TODO: sometimes this can just be a warning, e.g. if you have [1, .., .., 2] but we @@ -483,7 +481,6 @@ impl Problem { | Problem::NotAnAbility(region) | Problem::ImplementsNonRequired { region, .. } | Problem::DoesNotImplementAbility { region, .. } - | Problem::NoIdentifiersIntroduced(region) | Problem::OverloadedSpecialization { overload: region, .. } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 91fce0ce977..ca0df7cabe6 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -856,12 +856,12 @@ fn solve( solve_suffix_fx(env, problems, *kind, actual, region); state } - EffectfulStmt(variable, region) => { + ExpectEffectful(variable, reason, region) => { let content = env.subs.get_content_without_compacting(*variable); match content { Content::Pure | Content::FlexVar(_) => { - let problem = TypeError::PureStmt(*region); + let problem = TypeError::ExpectedEffectful(*region, *reason); problems.push(problem); state diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index e5b3f88d9ff..37affa703c8 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -1,7 +1,7 @@ //! Provides types to describe problems that can occur during solving. use std::{path::PathBuf, str::Utf8Error}; -use roc_can::constraint::FxSuffixKind; +use roc_can::constraint::{ExpectEffectfulReason, FxSuffixKind}; use roc_can::{ constraint::FxCallKind, expected::{Expected, PExpected}, @@ -45,7 +45,7 @@ pub enum TypeError { ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType), FxInPureFunction(Region, FxCallKind, Option), FxInTopLevel(Region, FxCallKind), - PureStmt(Region), + ExpectedEffectful(Region, ExpectEffectfulReason), UnsuffixedEffectfulFunction(Region, FxSuffixKind), SuffixedPureFunction(Region, FxSuffixKind), } @@ -72,7 +72,7 @@ impl TypeError { TypeError::ModuleParamsMismatch(..) => RuntimeError, TypeError::IngestedFileBadUtf8(..) => Fatal, TypeError::IngestedFileUnsupportedType(..) => Fatal, - TypeError::PureStmt(..) => Warning, + TypeError::ExpectedEffectful(..) => Warning, TypeError::FxInPureFunction(_, _, _) => Warning, TypeError::FxInTopLevel(_, _) => Warning, TypeError::UnsuffixedEffectfulFunction(_, _) => Warning, @@ -95,7 +95,7 @@ impl TypeError { | TypeError::ModuleParamsMismatch(region, ..) | TypeError::FxInPureFunction(region, _, _) | TypeError::FxInTopLevel(region, _) - | TypeError::PureStmt(region) + | TypeError::ExpectedEffectful(region, _) | TypeError::UnsuffixedEffectfulFunction(region, _) | TypeError::SuffixedPureFunction(region, _) => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 3a848332c8e..fb8afa8ea43 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1189,14 +1189,6 @@ pub fn can_problem<'b>( ]); title = "NAME NOT BOUND IN ALL PATTERNS".to_string(); } - Problem::NoIdentifiersIntroduced(region) => { - doc = alloc.stack([ - alloc.reflow("This destructure assignment doesn't introduce any new variables:"), - alloc.region(lines.convert_region(region), severity), - alloc.reflow("If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior!"), - ]); - title = "UNNECESSARY DEFINITION".to_string(); - } Problem::OverloadedSpecialization { ability_member, overload, diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index f94451a11f1..8e5941bdf44 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -4,7 +4,7 @@ use crate::error::canonicalize::{to_circular_def_doc, CIRCULAR_DEF}; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder}; use itertools::EitherOrBoth; use itertools::Itertools; -use roc_can::constraint::{FxCallKind, FxSuffixKind}; +use roc_can::constraint::{ExpectEffectfulReason, FxCallKind, FxSuffixKind}; use roc_can::expected::{Expected, PExpected}; use roc_collections::all::{HumanIndex, MutSet, SendMap}; use roc_collections::VecMap; @@ -368,7 +368,7 @@ pub fn type_problem<'b>( severity, }) } - PureStmt(region) => { + ExpectedEffectful(region, ExpectEffectfulReason::Stmt) => { let stack = [ alloc.reflow("This statement does not produce any effects:"), alloc.region(lines.convert_region(region), severity), @@ -384,6 +384,20 @@ pub fn type_problem<'b>( severity, }) } + ExpectedEffectful(region, ExpectEffectfulReason::Ignored) => { + let stack = [ + alloc.reflow("This assignment doesn't introduce any new variables:"), + alloc.region(lines.convert_region(region), severity), + alloc.reflow("Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on the right-hand-side, consider removing the assignment.") + ]; + + Some(Report { + title: "UNNECESSARY DEFINITION".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } UnsuffixedEffectfulFunction( region, kind @ (FxSuffixKind::Let(symbol) | FxSuffixKind::Pattern(symbol)), @@ -5522,5 +5536,6 @@ fn describe_fx_call_kind<'b>( ]), FxCallKind::Call(None) => alloc.reflow("This expression calls an effectful function:"), FxCallKind::Stmt => alloc.reflow("This statement calls an effectful function:"), + FxCallKind::Ignored => alloc.reflow("This ignored def calls an effectful function:"), } } From a2f940be4ef2f0745e630f30d9e4c892e14c1b17 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 24 Oct 2024 00:27:20 -0300 Subject: [PATCH 54/73] Use byte literal instead of cast and ignore too_many_args --- crates/compiler/can/src/env.rs | 1 + crates/compiler/constrain/src/pattern.rs | 1 + crates/compiler/module/src/ident.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index 664991ce3dd..2d651ccd8af 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -56,6 +56,7 @@ pub struct Env<'a> { } impl<'a> Env<'a> { + #[allow(clippy::too_many_arguments)] pub fn new( arena: &'a Bump, src: &'a str, diff --git a/crates/compiler/constrain/src/pattern.rs b/crates/compiler/constrain/src/pattern.rs index 4f0bda5433d..d896ca9bf85 100644 --- a/crates/compiler/constrain/src/pattern.rs +++ b/crates/compiler/constrain/src/pattern.rs @@ -261,6 +261,7 @@ pub fn constrain_pattern( ); } +#[allow(clippy::too_many_arguments)] pub fn constrain_pattern_help( types: &mut Types, constraints: &mut Constraints, diff --git a/crates/compiler/module/src/ident.rs b/crates/compiler/module/src/ident.rs index 1d97b402892..7571c0f57f8 100644 --- a/crates/compiler/module/src/ident.rs +++ b/crates/compiler/module/src/ident.rs @@ -383,7 +383,7 @@ impl IdentSuffix { debug_assert!(len > 0, "Ident name must not be empty"); - if bytes[len - 1] == ('!' as u8) { + if bytes[len - 1] == b'!' { IdentSuffix::Bang } else { IdentSuffix::None From 5f5e123bfd6be4dece430db6709eb16d152ece37 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 24 Oct 2024 10:52:22 -0300 Subject: [PATCH 55/73] Expect only one problem in test_can::shadow_annotation This is because the NoIdentifiersIntroduced error was moved to the type checker. --- crates/compiler/can/tests/test_can.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/can/tests/test_can.rs b/crates/compiler/can/tests/test_can.rs index 7543f2c9cbc..14f893ddef4 100644 --- a/crates/compiler/can/tests/test_can.rs +++ b/crates/compiler/can/tests/test_can.rs @@ -415,7 +415,7 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 2); + assert_eq!(problems.len(), 1); println!("{problems:#?}"); assert!(problems.iter().any(|problem| matches!( problem, From ca7697db91d703ce10339f07f61d3889abe9efc1 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 24 Oct 2024 11:45:57 -0300 Subject: [PATCH 56/73] update mono tests: ids increase because of new fx vars --- ...e_in_polymorphic_expression_issue_4717.txt | 116 ++++---- .../generated/call_function_in_empty_list.txt | 68 ++--- .../call_function_in_empty_list_unbound.txt | 68 ++--- .../generated/capture_void_layout_task.txt | 44 +-- ...ose_correct_recursion_var_under_record.txt | 140 ++++----- .../test_mono/generated/closure_in_list.txt | 4 +- ...lambda_set_productive_nullable_wrapped.txt | 42 +-- crates/compiler/test_mono/generated/dict.txt | 4 +- .../generated/empty_list_of_function_type.txt | 28 +- .../compiler/test_mono/generated/encode.txt | 18 +- .../encode_derived_nested_record_string.txt | 122 ++++---- ...encode_derived_record_one_field_string.txt | 72 ++--- ...ncode_derived_record_two_field_strings.txt | 72 ++--- .../generated/encode_derived_string.txt | 22 +- .../encode_derived_tag_one_field_string.txt | 72 ++--- ...encode_derived_tag_two_payloads_string.txt | 74 ++--- .../generated/inspect_derived_dict.txt | 272 +++++++++--------- .../generated/inspect_derived_list.txt | 44 +-- .../inspect_derived_nested_record_string.txt | 98 +++---- .../generated/inspect_derived_record.txt | 48 ++-- ...nspect_derived_record_one_field_string.txt | 48 ++-- ...spect_derived_record_two_field_strings.txt | 48 ++-- .../inspect_derived_tag_one_field_string.txt | 62 ++-- ...nspect_derived_tag_two_payloads_string.txt | 60 ++-- .../test_mono/generated/ir_int_add.txt | 4 +- ...cialize_errors_behind_unified_branches.txt | 54 ++-- .../test_mono/generated/issue_4770.txt | 166 +++++------ ...ure_with_multiple_recursive_structures.txt | 44 +-- .../test_mono/generated/list_append.txt | 18 +- .../generated/list_append_closure.txt | 18 +- .../generated/list_cannot_update_inplace.txt | 34 +-- .../compiler/test_mono/generated/list_get.txt | 28 +- .../compiler/test_mono/generated/list_len.txt | 8 +- .../generated/list_map_closure_borrows.txt | 102 +++---- .../generated/list_map_closure_owns.txt | 92 +++--- ...ist_map_take_capturing_or_noncapturing.txt | 86 +++--- .../generated/list_pass_to_function.txt | 32 +-- .../test_mono/generated/list_sort_asc.txt | 12 +- .../test_mono/generated/quicksort_swap.txt | 56 ++-- .../test_mono/generated/record_update.txt | 32 +-- ...function_and_union_with_inference_hole.txt | 70 ++--- .../compiler/test_mono/generated/rigids.txt | 56 ++-- ...not_duplicate_identical_concrete_types.txt | 74 ++--- ...types_without_unification_of_unifiable.txt | 132 ++++----- .../weakening_avoids_overspecialization.txt | 116 ++++---- 45 files changed, 1440 insertions(+), 1440 deletions(-) diff --git a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt index ea1183671e6..465131169d9 100644 --- a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt +++ b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt @@ -2,81 +2,81 @@ procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; -procedure List.110 (List.539, List.540, List.541): - let List.643 : U64 = 0i64; - let List.644 : U64 = CallByName List.6 List.539; - let List.642 : [C U64, C U64] = CallByName List.80 List.539 List.540 List.541 List.643 List.644; - ret List.642; +procedure List.111 (List.540, List.541, List.542): + let List.644 : U64 = 0i64; + let List.645 : U64 = CallByName List.6 List.540; + let List.643 : [C U64, C U64] = CallByName List.80 List.540 List.541 List.542 List.644 List.645; + ret List.643; -procedure List.26 (List.207, List.208, List.209): - let List.636 : [C U64, C U64] = CallByName List.110 List.207 List.208 List.209; - let List.639 : U8 = 1i64; - let List.640 : U8 = GetTagId List.636; - let List.641 : Int1 = lowlevel Eq List.639 List.640; - if List.641 then - let List.210 : U64 = UnionAtIndex (Id 1) (Index 0) List.636; - ret List.210; - else - let List.211 : U64 = UnionAtIndex (Id 0) (Index 0) List.636; +procedure List.26 (List.208, List.209, List.210): + let List.637 : [C U64, C U64] = CallByName List.111 List.208 List.209 List.210; + let List.640 : U8 = 1i64; + let List.641 : U8 = GetTagId List.637; + let List.642 : Int1 = lowlevel Eq List.640 List.641; + if List.642 then + let List.211 : U64 = UnionAtIndex (Id 1) (Index 0) List.637; ret List.211; + else + let List.212 : U64 = UnionAtIndex (Id 0) (Index 0) List.637; + ret List.212; -procedure List.38 (List.395, List.396): - let List.635 : U64 = CallByName List.6 List.395; - let List.397 : U64 = CallByName Num.77 List.635 List.396; - let List.625 : List U8 = CallByName List.43 List.395 List.397; - ret List.625; - -procedure List.43 (List.393, List.394): - let List.633 : U64 = CallByName List.6 List.393; - let List.632 : U64 = CallByName Num.77 List.633 List.394; - let List.627 : {U64, U64} = Struct {List.394, List.632}; - let List.626 : List U8 = CallByName List.49 List.393 List.627; +procedure List.38 (List.396, List.397): + let List.636 : U64 = CallByName List.6 List.396; + let List.398 : U64 = CallByName Num.77 List.636 List.397; + let List.626 : List U8 = CallByName List.43 List.396 List.398; ret List.626; -procedure List.49 (List.471, List.472): - let List.629 : U64 = StructAtIndex 1 List.472; - let List.630 : U64 = StructAtIndex 0 List.472; - let List.628 : List U8 = CallByName List.72 List.471 List.629 List.630; - ret List.628; +procedure List.43 (List.394, List.395): + let List.634 : U64 = CallByName List.6 List.394; + let List.633 : U64 = CallByName Num.77 List.634 List.395; + let List.628 : {U64, U64} = Struct {List.395, List.633}; + let List.627 : List U8 = CallByName List.49 List.394 List.628; + ret List.627; + +procedure List.49 (List.472, List.473): + let List.630 : U64 = StructAtIndex 1 List.473; + let List.631 : U64 = StructAtIndex 0 List.473; + let List.629 : List U8 = CallByName List.72 List.472 List.630 List.631; + ret List.629; procedure List.6 (#Attr.2): - let List.634 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.634; + let List.635 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.635; procedure List.66 (#Attr.2, #Attr.3): - let List.657 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.657; + let List.658 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.658; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.631 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.631; + let List.632 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.632; procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.645 List.542 List.543 List.544 List.545 List.546: - let List.647 : Int1 = CallByName Num.22 List.545 List.546; - if List.647 then - let List.656 : U8 = CallByName List.66 List.542 List.545; - let List.648 : [C U64, C U64] = CallByName Test.4 List.543 List.656; - let List.653 : U8 = 1i64; - let List.654 : U8 = GetTagId List.648; - let List.655 : Int1 = lowlevel Eq List.653 List.654; - if List.655 then - let List.547 : U64 = UnionAtIndex (Id 1) (Index 0) List.648; - let List.651 : U64 = 1i64; - let List.650 : U64 = CallByName Num.51 List.545 List.651; - jump List.645 List.542 List.547 List.544 List.650 List.546; + joinpoint List.646 List.543 List.544 List.545 List.546 List.547: + let List.648 : Int1 = CallByName Num.22 List.546 List.547; + if List.648 then + let List.657 : U8 = CallByName List.66 List.543 List.546; + let List.649 : [C U64, C U64] = CallByName Test.4 List.544 List.657; + let List.654 : U8 = 1i64; + let List.655 : U8 = GetTagId List.649; + let List.656 : Int1 = lowlevel Eq List.654 List.655; + if List.656 then + let List.548 : U64 = UnionAtIndex (Id 1) (Index 0) List.649; + let List.652 : U64 = 1i64; + let List.651 : U64 = CallByName Num.51 List.546 List.652; + jump List.646 List.543 List.548 List.545 List.651 List.547; else - dec List.542; - let List.548 : U64 = UnionAtIndex (Id 0) (Index 0) List.648; - let List.652 : [C U64, C U64] = TagId(0) List.548; - ret List.652; + dec List.543; + let List.549 : U64 = UnionAtIndex (Id 0) (Index 0) List.649; + let List.653 : [C U64, C U64] = TagId(0) List.549; + ret List.653; else - dec List.542; - let List.646 : [C U64, C U64] = TagId(1) List.543; - ret List.646; + dec List.543; + let List.647 : [C U64, C U64] = TagId(1) List.544; + ret List.647; in inc #Derived_gen.0; - jump List.645 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.646 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/call_function_in_empty_list.txt b/crates/compiler/test_mono/generated/call_function_in_empty_list.txt index a5c284002ad..d509acb3ea9 100644 --- a/crates/compiler/test_mono/generated/call_function_in_empty_list.txt +++ b/crates/compiler/test_mono/generated/call_function_in_empty_list.txt @@ -1,51 +1,51 @@ -procedure List.18 (List.166, List.167, List.168): - let List.629 : U64 = 0i64; - let List.630 : U64 = CallByName List.6 List.166; - let List.628 : List {} = CallByName List.95 List.166 List.167 List.168 List.629 List.630; - ret List.628; +procedure List.18 (List.167, List.168, List.169): + let List.630 : U64 = 0i64; + let List.631 : U64 = CallByName List.6 List.167; + let List.629 : List {} = CallByName List.96 List.167 List.168 List.169 List.630 List.631; + ret List.629; -procedure List.278 (List.279, List.280, List.276): - let List.642 : {} = CallByName Test.2 List.280; - let List.641 : List {} = CallByName List.71 List.279 List.642; - ret List.641; +procedure List.279 (List.280, List.281, List.277): + let List.643 : {} = CallByName Test.2 List.281; + let List.642 : List {} = CallByName List.71 List.280 List.643; + ret List.642; -procedure List.5 (List.275, List.276): - let List.277 : U64 = CallByName List.6 List.275; - let List.626 : List {} = CallByName List.68 List.277; - let List.625 : List {} = CallByName List.18 List.275 List.626 List.276; - ret List.625; +procedure List.5 (List.276, List.277): + let List.278 : U64 = CallByName List.6 List.276; + let List.627 : List {} = CallByName List.68 List.278; + let List.626 : List {} = CallByName List.18 List.276 List.627 List.277; + ret List.626; procedure List.6 (#Attr.2): - let List.639 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.639; + let List.640 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.640; procedure List.66 (#Attr.2, #Attr.3): - let List.638 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.638; + let List.639 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.639; procedure List.68 (#Attr.2): - let List.644 : List {} = lowlevel ListWithCapacity #Attr.2; - ret List.644; + let List.645 : List {} = lowlevel ListWithCapacity #Attr.2; + ret List.645; procedure List.71 (#Attr.2, #Attr.3): - let List.643 : List {} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.643; + let List.644 : List {} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.644; -procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.631 List.169 List.170 List.171 List.172 List.173: - let List.633 : Int1 = CallByName Num.22 List.172 List.173; - if List.633 then - let List.637 : [] = CallByName List.66 List.169 List.172; - let List.174 : List {} = CallByName List.278 List.170 List.637 List.171; - let List.636 : U64 = 1i64; - let List.635 : U64 = CallByName Num.51 List.172 List.636; - jump List.631 List.169 List.174 List.171 List.635 List.173; +procedure List.96 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.632 List.170 List.171 List.172 List.173 List.174: + let List.634 : Int1 = CallByName Num.22 List.173 List.174; + if List.634 then + let List.638 : [] = CallByName List.66 List.170 List.173; + let List.175 : List {} = CallByName List.279 List.171 List.638 List.172; + let List.637 : U64 = 1i64; + let List.636 : U64 = CallByName Num.51 List.173 List.637; + jump List.632 List.170 List.175 List.172 List.636 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.0; - jump List.631 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.632 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt b/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt index a174241d8cb..e782e32d4e2 100644 --- a/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt +++ b/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt @@ -1,51 +1,51 @@ -procedure List.18 (List.166, List.167, List.168): - let List.629 : U64 = 0i64; - let List.630 : U64 = CallByName List.6 List.166; - let List.628 : List [] = CallByName List.95 List.166 List.167 List.168 List.629 List.630; - ret List.628; +procedure List.18 (List.167, List.168, List.169): + let List.630 : U64 = 0i64; + let List.631 : U64 = CallByName List.6 List.167; + let List.629 : List [] = CallByName List.96 List.167 List.168 List.169 List.630 List.631; + ret List.629; -procedure List.278 (List.279, List.280, List.276): - let List.642 : [] = CallByName Test.2 List.280; - let List.641 : List [] = CallByName List.71 List.279 List.642; - ret List.641; +procedure List.279 (List.280, List.281, List.277): + let List.643 : [] = CallByName Test.2 List.281; + let List.642 : List [] = CallByName List.71 List.280 List.643; + ret List.642; -procedure List.5 (List.275, List.276): - let List.277 : U64 = CallByName List.6 List.275; - let List.626 : List [] = CallByName List.68 List.277; - let List.625 : List [] = CallByName List.18 List.275 List.626 List.276; - ret List.625; +procedure List.5 (List.276, List.277): + let List.278 : U64 = CallByName List.6 List.276; + let List.627 : List [] = CallByName List.68 List.278; + let List.626 : List [] = CallByName List.18 List.276 List.627 List.277; + ret List.626; procedure List.6 (#Attr.2): - let List.639 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.639; + let List.640 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.640; procedure List.66 (#Attr.2, #Attr.3): - let List.638 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.638; + let List.639 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.639; procedure List.68 (#Attr.2): - let List.644 : List [] = lowlevel ListWithCapacity #Attr.2; - ret List.644; + let List.645 : List [] = lowlevel ListWithCapacity #Attr.2; + ret List.645; procedure List.71 (#Attr.2, #Attr.3): - let List.643 : List [] = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.643; + let List.644 : List [] = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.644; -procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.631 List.169 List.170 List.171 List.172 List.173: - let List.633 : Int1 = CallByName Num.22 List.172 List.173; - if List.633 then - let List.637 : [] = CallByName List.66 List.169 List.172; - let List.174 : List [] = CallByName List.278 List.170 List.637 List.171; - let List.636 : U64 = 1i64; - let List.635 : U64 = CallByName Num.51 List.172 List.636; - jump List.631 List.169 List.174 List.171 List.635 List.173; +procedure List.96 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.632 List.170 List.171 List.172 List.173 List.174: + let List.634 : Int1 = CallByName Num.22 List.173 List.174; + if List.634 then + let List.638 : [] = CallByName List.66 List.170 List.173; + let List.175 : List [] = CallByName List.279 List.171 List.638 List.172; + let List.637 : U64 = 1i64; + let List.636 : U64 = CallByName Num.51 List.173 List.637; + jump List.632 List.170 List.175 List.172 List.636 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.0; - jump List.631 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.632 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/capture_void_layout_task.txt b/crates/compiler/test_mono/generated/capture_void_layout_task.txt index 9442fc79fb2..785c5856825 100644 --- a/crates/compiler/test_mono/generated/capture_void_layout_task.txt +++ b/crates/compiler/test_mono/generated/capture_void_layout_task.txt @@ -1,32 +1,32 @@ -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : [C {}, C *self {{}, []}] = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : [C {}, C *self {{}, []}] = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; - -procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : [] = CallByName List.66 List.169 List.172; - let List.174 : [C {}, C *self {{}, []}] = CallByName Test.29 List.170 List.634 List.171; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.636 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; + +procedure List.96 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : [] = CallByName List.66 List.170 List.173; + let List.175 : [C {}, C *self {{}, []}] = CallByName Test.29 List.171 List.635 List.172; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.13; - jump List.628 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + jump List.629 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt index 6f4be4a2c57..ba38439fd12 100644 --- a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt +++ b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt @@ -2,92 +2,92 @@ procedure Bool.1 (): let Bool.24 : Int1 = false; ret Bool.24; -procedure List.18 (List.166, List.167, List.168): - let List.645 : U64 = 0i64; - let List.646 : U64 = CallByName List.6 List.166; - let List.644 : List Str = CallByName List.95 List.166 List.167 List.168 List.645 List.646; - ret List.644; - -procedure List.2 (List.114, List.115): - let List.639 : U64 = CallByName List.6 List.114; - let List.635 : Int1 = CallByName Num.22 List.115 List.639; - if List.635 then - let List.637 : Str = CallByName List.66 List.114 List.115; - inc List.637; - let List.636 : [C {}, C Str] = TagId(1) List.637; - ret List.636; +procedure List.18 (List.167, List.168, List.169): + let List.646 : U64 = 0i64; + let List.647 : U64 = CallByName List.6 List.167; + let List.645 : List Str = CallByName List.96 List.167 List.168 List.169 List.646 List.647; + ret List.645; + +procedure List.2 (List.115, List.116): + let List.640 : U64 = CallByName List.6 List.115; + let List.636 : Int1 = CallByName Num.22 List.116 List.640; + if List.636 then + let List.638 : Str = CallByName List.66 List.115 List.116; + inc List.638; + let List.637 : [C {}, C Str] = TagId(1) List.638; + ret List.637; else - let List.634 : {} = Struct {}; - let List.633 : [C {}, C Str] = TagId(0) List.634; - ret List.633; - -procedure List.278 (List.279, List.280, List.276): - let List.658 : Str = CallByName Test.10 List.280; - let List.657 : List Str = CallByName List.71 List.279 List.658; - ret List.657; - -procedure List.5 (List.275, List.276): - let List.277 : U64 = CallByName List.6 List.275; - let List.642 : List Str = CallByName List.68 List.277; - let List.641 : List Str = CallByName List.18 List.275 List.642 List.276; - ret List.641; + let List.635 : {} = Struct {}; + let List.634 : [C {}, C Str] = TagId(0) List.635; + ret List.634; + +procedure List.279 (List.280, List.281, List.277): + let List.659 : Str = CallByName Test.10 List.281; + let List.658 : List Str = CallByName List.71 List.280 List.659; + ret List.658; + +procedure List.5 (List.276, List.277): + let List.278 : U64 = CallByName List.6 List.276; + let List.643 : List Str = CallByName List.68 List.278; + let List.642 : List Str = CallByName List.18 List.276 List.643 List.277; + ret List.642; procedure List.6 (#Attr.2): - let List.640 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.640; + let List.641 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.641; procedure List.6 (#Attr.2): - let List.655 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.655; + let List.656 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.656; procedure List.66 (#Attr.2, #Attr.3): - let List.638 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.638; + let List.639 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.639; procedure List.66 (#Attr.2, #Attr.3): - let List.654 : [C List [C List *self, C *self], C [C List *self, C *self]] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.654; + let List.655 : [C List [C List *self, C *self], C [C List *self, C *self]] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.655; procedure List.68 (#Attr.2): - let List.660 : List Str = lowlevel ListWithCapacity #Attr.2; - ret List.660; + let List.661 : List Str = lowlevel ListWithCapacity #Attr.2; + ret List.661; procedure List.71 (#Attr.2, #Attr.3): - let List.659 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.659; - -procedure List.9 (List.386): - let List.632 : U64 = 0i64; - let List.625 : [C {}, C Str] = CallByName List.2 List.386 List.632; - let List.629 : U8 = 1i64; - let List.630 : U8 = GetTagId List.625; - let List.631 : Int1 = lowlevel Eq List.629 List.630; - if List.631 then - let List.387 : Str = UnionAtIndex (Id 1) (Index 0) List.625; - let List.626 : [C {}, C Str] = TagId(1) List.387; - ret List.626; - else - dec List.625; - let List.628 : {} = Struct {}; - let List.627 : [C {}, C Str] = TagId(0) List.628; - ret List.627; + let List.660 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.660; -procedure List.95 (#Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8): - joinpoint List.647 List.169 List.170 List.171 List.172 List.173: - let List.649 : Int1 = CallByName Num.22 List.172 List.173; - if List.649 then - let List.653 : [C List [C List *self, C *self], C [C List *self, C *self]] = CallByName List.66 List.169 List.172; - inc List.653; - let List.174 : List Str = CallByName List.278 List.170 List.653 List.171; - let List.652 : U64 = 1i64; - let List.651 : U64 = CallByName Num.51 List.172 List.652; - jump List.647 List.169 List.174 List.171 List.651 List.173; +procedure List.9 (List.387): + let List.633 : U64 = 0i64; + let List.626 : [C {}, C Str] = CallByName List.2 List.387 List.633; + let List.630 : U8 = 1i64; + let List.631 : U8 = GetTagId List.626; + let List.632 : Int1 = lowlevel Eq List.630 List.631; + if List.632 then + let List.388 : Str = UnionAtIndex (Id 1) (Index 0) List.626; + let List.627 : [C {}, C Str] = TagId(1) List.388; + ret List.627; + else + dec List.626; + let List.629 : {} = Struct {}; + let List.628 : [C {}, C Str] = TagId(0) List.629; + ret List.628; + +procedure List.96 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7): + joinpoint List.648 List.170 List.171 List.172 List.173 List.174: + let List.650 : Int1 = CallByName Num.22 List.173 List.174; + if List.650 then + let List.654 : [C List [C List *self, C *self], C [C List *self, C *self]] = CallByName List.66 List.170 List.173; + inc List.654; + let List.175 : List Str = CallByName List.279 List.171 List.654 List.172; + let List.653 : U64 = 1i64; + let List.652 : U64 = CallByName Num.51 List.173 List.653; + jump List.648 List.170 List.175 List.172 List.652 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.4; - jump List.647 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8; + inc #Derived_gen.3; + jump List.648 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/closure_in_list.txt b/crates/compiler/test_mono/generated/closure_in_list.txt index 600b345fa37..d9078225e1f 100644 --- a/crates/compiler/test_mono/generated/closure_in_list.txt +++ b/crates/compiler/test_mono/generated/closure_in_list.txt @@ -1,6 +1,6 @@ procedure List.6 (#Attr.2): - let List.625 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.625; + let List.626 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.626; procedure Test.1 (Test.5): let Test.2 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt index dd88505810a..604c1b89d93 100644 --- a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt +++ b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt @@ -2,35 +2,35 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : [, C *self Int1, C *self Int1] = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : [, C *self Int1, C *self Int1] = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; -procedure List.95 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : Int1 = CallByName List.66 List.169 List.172; - let List.174 : [, C *self Int1, C *self Int1] = CallByName Test.6 List.170 List.634 List.171; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; +procedure List.96 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : Int1 = CallByName List.66 List.170 List.173; + let List.175 : [, C *self Int1, C *self Int1] = CallByName Test.6 List.171 List.635 List.172; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.5; - jump List.628 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9; + jump List.629 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/dict.txt b/crates/compiler/test_mono/generated/dict.txt index 3e3a4d569da..7a5123de466 100644 --- a/crates/compiler/test_mono/generated/dict.txt +++ b/crates/compiler/test_mono/generated/dict.txt @@ -26,8 +26,8 @@ procedure Dict.52 (): ret Dict.743; procedure List.6 (#Attr.2): - let List.625 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.625; + let List.626 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.626; procedure Num.75 (#Attr.2, #Attr.3): let Num.281 : U8 = lowlevel NumSubWrap #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt index a41d71ca8a4..eafd7d9312c 100644 --- a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -2,25 +2,25 @@ procedure Bool.1 (): let Bool.23 : Int1 = false; ret Bool.23; -procedure List.2 (List.114, List.115): - let List.631 : U64 = CallByName List.6 List.114; - let List.627 : Int1 = CallByName Num.22 List.115 List.631; - if List.627 then - let List.629 : {} = CallByName List.66 List.114 List.115; - let List.628 : [C {}, C {}] = TagId(1) List.629; - ret List.628; +procedure List.2 (List.115, List.116): + let List.632 : U64 = CallByName List.6 List.115; + let List.628 : Int1 = CallByName Num.22 List.116 List.632; + if List.628 then + let List.630 : {} = CallByName List.66 List.115 List.116; + let List.629 : [C {}, C {}] = TagId(1) List.630; + ret List.629; else - let List.626 : {} = Struct {}; - let List.625 : [C {}, C {}] = TagId(0) List.626; - ret List.625; + let List.627 : {} = Struct {}; + let List.626 : [C {}, C {}] = TagId(0) List.627; + ret List.626; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; procedure List.66 (#Attr.2, #Attr.3): - let List.630 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.630; + let List.631 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.631; procedure Num.22 (#Attr.2, #Attr.3): let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/encode.txt b/crates/compiler/test_mono/generated/encode.txt index 2d81463737d..ef5598f1e32 100644 --- a/crates/compiler/test_mono/generated/encode.txt +++ b/crates/compiler/test_mono/generated/encode.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.130, List.131): - let List.628 : U64 = 1i64; - let List.626 : List U8 = CallByName List.70 List.130 List.628; - let List.625 : List U8 = CallByName List.71 List.626 List.131; - ret List.625; +procedure List.4 (List.131, List.132): + let List.629 : U64 = 1i64; + let List.627 : List U8 = CallByName List.70 List.131 List.629; + let List.626 : List U8 = CallByName List.71 List.627 List.132; + ret List.626; procedure List.70 (#Attr.2, #Attr.3): - let List.629 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.629; + let List.630 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.630; procedure List.71 (#Attr.2, #Attr.3): - let List.627 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.627; + let List.628 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.628; procedure Test.23 (Test.24, Test.35, Test.22): let Test.37 : List U8 = CallByName List.4 Test.24 Test.22; diff --git a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt index 4825ca0a262..08a680c3d26 100644 --- a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -67,85 +67,85 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : List U8 = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; - -procedure List.18 (List.166, List.167, List.168): - let List.652 : U64 = 0i64; - let List.653 : U64 = CallByName List.6 List.166; - let List.651 : List U8 = CallByName List.95 List.166 List.167 List.168 List.652 List.653; - ret List.651; - -procedure List.4 (List.130, List.131): - let List.673 : U64 = 1i64; - let List.672 : List U8 = CallByName List.70 List.130 List.673; - let List.671 : List U8 = CallByName List.71 List.672 List.131; - ret List.671; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : List U8 = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; + +procedure List.18 (List.167, List.168, List.169): + let List.653 : U64 = 0i64; + let List.654 : U64 = CallByName List.6 List.167; + let List.652 : List U8 = CallByName List.96 List.167 List.168 List.169 List.653 List.654; + ret List.652; + +procedure List.4 (List.131, List.132): + let List.674 : U64 = 1i64; + let List.673 : List U8 = CallByName List.70 List.131 List.674; + let List.672 : List U8 = CallByName List.71 List.673 List.132; + ret List.672; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.650; + let List.651 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.651; procedure List.6 (#Attr.2): - let List.676 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.676; + let List.677 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.677; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.66 (#Attr.2, #Attr.3): - let List.661 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.661; + let List.662 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.662; procedure List.70 (#Attr.2, #Attr.3): - let List.667 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.667; + let List.668 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.668; procedure List.71 (#Attr.2, #Attr.3): - let List.665 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.665; + let List.666 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.666; procedure List.8 (#Attr.2, #Attr.3): - let List.675 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.675; - -procedure List.95 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : List U8 = CallByName Test.71 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.676 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.676; + +procedure List.96 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : List U8 = CallByName Test.71 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.26; - jump List.628 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; - -procedure List.95 (#Derived_gen.40, #Derived_gen.41, #Derived_gen.42, #Derived_gen.43, #Derived_gen.44): - joinpoint List.654 List.169 List.170 List.171 List.172 List.173: - let List.656 : Int1 = CallByName Num.22 List.172 List.173; - if List.656 then - let List.660 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.660; - let List.174 : List U8 = CallByName Test.71 List.170 List.660; - let List.659 : U64 = 1i64; - let List.658 : U64 = CallByName Num.51 List.172 List.659; - jump List.654 List.169 List.174 List.171 List.658 List.173; + inc #Derived_gen.29; + jump List.629 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33; + +procedure List.96 (#Derived_gen.34, #Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38): + joinpoint List.655 List.170 List.171 List.172 List.173 List.174: + let List.657 : Int1 = CallByName Num.22 List.173 List.174; + if List.657 then + let List.661 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.661; + let List.175 : List U8 = CallByName Test.71 List.171 List.661; + let List.660 : U64 = 1i64; + let List.659 : U64 = CallByName Num.51 List.173 List.660; + jump List.655 List.170 List.175 List.172 List.659 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.40; - jump List.654 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44; + inc #Derived_gen.34; + jump List.655 #Derived_gen.34 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38; procedure Num.127 (#Attr.2): let Num.286 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt index 3275a4c4093..925a9d40b2f 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt @@ -39,54 +39,54 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : List U8 = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; - -procedure List.4 (List.130, List.131): - let List.647 : U64 = 1i64; - let List.646 : List U8 = CallByName List.70 List.130 List.647; - let List.645 : List U8 = CallByName List.71 List.646 List.131; - ret List.645; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : List U8 = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; + +procedure List.4 (List.131, List.132): + let List.648 : U64 = 1i64; + let List.647 : List U8 = CallByName List.70 List.131 List.648; + let List.646 : List U8 = CallByName List.71 List.647 List.132; + ret List.646; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.650; + let List.651 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.651; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.70 (#Attr.2, #Attr.3): - let List.641 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.641; + let List.642 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.642; procedure List.71 (#Attr.2, #Attr.3): - let List.639 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.639; + let List.640 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.640; procedure List.8 (#Attr.2, #Attr.3): - let List.649 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.649; - -procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : List U8 = CallByName Test.71 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.650 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.650; + +procedure List.96 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : List U8 = CallByName Test.71 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.13; - jump List.628 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + inc #Derived_gen.16; + jump List.629 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20; procedure Num.127 (#Attr.2): let Num.282 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt index 5a570cd30b1..00a931097fe 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt @@ -46,54 +46,54 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : List U8 = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; - -procedure List.4 (List.130, List.131): - let List.647 : U64 = 1i64; - let List.646 : List U8 = CallByName List.70 List.130 List.647; - let List.645 : List U8 = CallByName List.71 List.646 List.131; - ret List.645; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : List U8 = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; + +procedure List.4 (List.131, List.132): + let List.648 : U64 = 1i64; + let List.647 : List U8 = CallByName List.70 List.131 List.648; + let List.646 : List U8 = CallByName List.71 List.647 List.132; + ret List.646; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.650; + let List.651 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.651; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.70 (#Attr.2, #Attr.3): - let List.641 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.641; + let List.642 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.642; procedure List.71 (#Attr.2, #Attr.3): - let List.639 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.639; + let List.640 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.640; procedure List.8 (#Attr.2, #Attr.3): - let List.649 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.649; - -procedure List.95 (#Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : List U8 = CallByName Test.71 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.650 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.650; + +procedure List.96 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : List U8 = CallByName Test.71 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.17; - jump List.628 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21; + inc #Derived_gen.20; + jump List.629 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24; procedure Num.127 (#Attr.2): let Num.282 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt index dd67fc2c7fd..c83d470e90f 100644 --- a/crates/compiler/test_mono/generated/encode_derived_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -11,23 +11,23 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.4 (List.130, List.131): - let List.635 : U64 = 1i64; - let List.634 : List U8 = CallByName List.70 List.130 List.635; - let List.633 : List U8 = CallByName List.71 List.634 List.131; - ret List.633; +procedure List.4 (List.131, List.132): + let List.636 : U64 = 1i64; + let List.635 : List U8 = CallByName List.70 List.131 List.636; + let List.634 : List U8 = CallByName List.71 List.635 List.132; + ret List.634; procedure List.70 (#Attr.2, #Attr.3): - let List.629 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.629; + let List.630 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.630; procedure List.71 (#Attr.2, #Attr.3): - let List.627 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.627; + let List.628 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.628; procedure List.8 (#Attr.2, #Attr.3): - let List.637 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.637; + let List.638 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.638; procedure Num.127 (#Attr.2): let Num.282 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt index 31dbbe8c04e..fa30b19641b 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt @@ -41,57 +41,57 @@ procedure Encode.26 (Encode.107, Encode.108): ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.651 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.651; + let List.652 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.652; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : List U8 = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : List U8 = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; -procedure List.4 (List.130, List.131): - let List.647 : U64 = 1i64; - let List.646 : List U8 = CallByName List.70 List.130 List.647; - let List.645 : List U8 = CallByName List.71 List.646 List.131; - ret List.645; +procedure List.4 (List.131, List.132): + let List.648 : U64 = 1i64; + let List.647 : List U8 = CallByName List.70 List.131 List.648; + let List.646 : List U8 = CallByName List.71 List.647 List.132; + ret List.646; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.650; + let List.651 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.651; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.70 (#Attr.2, #Attr.3): - let List.641 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.641; + let List.642 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.642; procedure List.71 (#Attr.2, #Attr.3): - let List.639 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.639; + let List.640 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.640; procedure List.8 (#Attr.2, #Attr.3): - let List.649 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.649; - -procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : Str = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : List U8 = CallByName Test.64 List.170 List.634 List.171; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.650 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.650; + +procedure List.96 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : Str = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : List U8 = CallByName Test.64 List.171 List.635 List.172; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.13; - jump List.628 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + jump List.629 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; procedure Num.127 (#Attr.2): let Num.282 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt index d76d64fda50..2acf475c886 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt @@ -44,57 +44,57 @@ procedure Encode.26 (Encode.107, Encode.108): ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.651 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.651; + let List.652 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.652; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : List U8 = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : List U8 = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; -procedure List.4 (List.130, List.131): - let List.647 : U64 = 1i64; - let List.646 : List U8 = CallByName List.70 List.130 List.647; - let List.645 : List U8 = CallByName List.71 List.646 List.131; - ret List.645; +procedure List.4 (List.131, List.132): + let List.648 : U64 = 1i64; + let List.647 : List U8 = CallByName List.70 List.131 List.648; + let List.646 : List U8 = CallByName List.71 List.647 List.132; + ret List.646; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.650; + let List.651 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.651; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.70 (#Attr.2, #Attr.3): - let List.641 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.641; + let List.642 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.642; procedure List.71 (#Attr.2, #Attr.3): - let List.639 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.639; + let List.640 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.640; procedure List.8 (#Attr.2, #Attr.3): - let List.649 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.649; - -procedure List.95 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : Str = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : List U8 = CallByName Test.64 List.170 List.634 List.171; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.650 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.650; + +procedure List.96 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : Str = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : List U8 = CallByName Test.64 List.171 List.635 List.172; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.11; - jump List.628 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15; + inc #Derived_gen.23; + jump List.629 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; procedure Num.127 (#Attr.2): let Num.282 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/inspect_derived_dict.txt b/crates/compiler/test_mono/generated/inspect_derived_dict.txt index d017de9c683..990aa944f63 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_dict.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_dict.txt @@ -304,7 +304,7 @@ procedure Dict.72 (Dict.412, Dict.413, Dict.414): let Dict.854 : {U64, U32} = CallByName Dict.73 Dict.412 Dict.417 Dict.416; ret Dict.854; -procedure Dict.73 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18): +procedure Dict.73 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23): joinpoint Dict.855 Dict.418 Dict.419 Dict.420: let Dict.421 : {U32, U32} = CallByName Dict.22 Dict.418 Dict.419; let Dict.862 : U32 = StructAtIndex 1 Dict.421; @@ -319,8 +319,8 @@ procedure Dict.73 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18): let Dict.856 : {U64, U32} = Struct {Dict.419, Dict.420}; ret Dict.856; in - inc #Derived_gen.16; - jump Dict.855 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; + inc #Derived_gen.21; + jump Dict.855 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23; procedure Dict.74 (#Derived_gen.51, #Derived_gen.52, #Derived_gen.53): joinpoint Dict.761 Dict.422 Dict.423 Dict.424: @@ -890,171 +890,171 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.11 (List.144, List.145): - let List.687 : List {U32, U32} = CallByName List.68 List.145; - let List.686 : List {U32, U32} = CallByName List.93 List.144 List.145 List.687; - ret List.686; - -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; - -procedure List.18 (List.166, List.167, List.168): - let List.690 : U64 = 0i64; - let List.691 : U64 = CallByName List.6 List.166; - let List.689 : {Str, Int1} = CallByName List.95 List.166 List.167 List.168 List.690 List.691; - ret List.689; - -procedure List.3 (List.122, List.123, List.124): - let List.651 : {List {U32, U32}, {U32, U32}} = CallByName List.64 List.122 List.123 List.124; - let List.650 : List {U32, U32} = StructAtIndex 0 List.651; - ret List.650; - -procedure List.3 (List.122, List.123, List.124): - let List.653 : {List {Str, I64}, {Str, I64}} = CallByName List.64 List.122 List.123 List.124; - let List.652 : List {Str, I64} = StructAtIndex 0 List.653; - let #Derived_gen.71 : {Str, I64} = StructAtIndex 1 List.653; +procedure List.11 (List.145, List.146): + let List.688 : List {U32, U32} = CallByName List.68 List.146; + let List.687 : List {U32, U32} = CallByName List.94 List.145 List.146 List.688; + ret List.687; + +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; + +procedure List.18 (List.167, List.168, List.169): + let List.691 : U64 = 0i64; + let List.692 : U64 = CallByName List.6 List.167; + let List.690 : {Str, Int1} = CallByName List.96 List.167 List.168 List.169 List.691 List.692; + ret List.690; + +procedure List.3 (List.123, List.124, List.125): + let List.652 : {List {U32, U32}, {U32, U32}} = CallByName List.64 List.123 List.124 List.125; + let List.651 : List {U32, U32} = StructAtIndex 0 List.652; + ret List.651; + +procedure List.3 (List.123, List.124, List.125): + let List.654 : {List {Str, I64}, {Str, I64}} = CallByName List.64 List.123 List.124 List.125; + let List.653 : List {Str, I64} = StructAtIndex 0 List.654; + let #Derived_gen.71 : {Str, I64} = StructAtIndex 1 List.654; dec #Derived_gen.71; - ret List.652; + ret List.653; -procedure List.4 (List.130, List.131): - let List.662 : U64 = 1i64; - let List.660 : List {Str, I64} = CallByName List.70 List.130 List.662; - let List.659 : List {Str, I64} = CallByName List.71 List.660 List.131; - ret List.659; +procedure List.4 (List.131, List.132): + let List.663 : U64 = 1i64; + let List.661 : List {Str, I64} = CallByName List.70 List.131 List.663; + let List.660 : List {Str, I64} = CallByName List.71 List.661 List.132; + ret List.660; procedure List.6 (#Attr.2): - let List.641 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.641; + let List.642 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.642; procedure List.6 (#Attr.2): - let List.688 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.688; + let List.689 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.689; procedure List.6 (#Attr.2): - let List.700 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.700; - -procedure List.64 (List.119, List.120, List.121): - let List.649 : U64 = CallByName List.6 List.119; - let List.646 : Int1 = CallByName Num.22 List.120 List.649; - if List.646 then - let List.647 : {List {U32, U32}, {U32, U32}} = CallByName List.67 List.119 List.120 List.121; - ret List.647; + let List.701 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.701; + +procedure List.64 (List.120, List.121, List.122): + let List.650 : U64 = CallByName List.6 List.120; + let List.647 : Int1 = CallByName Num.22 List.121 List.650; + if List.647 then + let List.648 : {List {U32, U32}, {U32, U32}} = CallByName List.67 List.120 List.121 List.122; + ret List.648; else - let List.645 : {List {U32, U32}, {U32, U32}} = Struct {List.119, List.121}; - ret List.645; - -procedure List.64 (List.119, List.120, List.121): - let List.658 : U64 = CallByName List.6 List.119; - let List.655 : Int1 = CallByName Num.22 List.120 List.658; - if List.655 then - let List.656 : {List {Str, I64}, {Str, I64}} = CallByName List.67 List.119 List.120 List.121; - ret List.656; + let List.646 : {List {U32, U32}, {U32, U32}} = Struct {List.120, List.122}; + ret List.646; + +procedure List.64 (List.120, List.121, List.122): + let List.659 : U64 = CallByName List.6 List.120; + let List.656 : Int1 = CallByName Num.22 List.121 List.659; + if List.656 then + let List.657 : {List {Str, I64}, {Str, I64}} = CallByName List.67 List.120 List.121 List.122; + ret List.657; else - let List.654 : {List {Str, I64}, {Str, I64}} = Struct {List.119, List.121}; - ret List.654; + let List.655 : {List {Str, I64}, {Str, I64}} = Struct {List.120, List.122}; + ret List.655; procedure List.66 (#Attr.2, #Attr.3): - let List.699 : {Str, I64} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.699; + let List.700 : {Str, I64} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.700; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.648 : {List {U32, U32}, {U32, U32}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.648; + let List.649 : {List {U32, U32}, {U32, U32}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.649; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.657 : {List {Str, I64}, {Str, I64}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.657; + let List.658 : {List {Str, I64}, {Str, I64}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.658; procedure List.68 (#Attr.2): - let List.685 : List {U32, U32} = lowlevel ListWithCapacity #Attr.2; - ret List.685; + let List.686 : List {U32, U32} = lowlevel ListWithCapacity #Attr.2; + ret List.686; procedure List.70 (#Attr.2, #Attr.3): - let List.663 : List {Str, I64} = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.663; + let List.664 : List {Str, I64} = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.664; procedure List.71 (#Attr.2, #Attr.3): - let List.661 : List {Str, I64} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.661; + let List.662 : List {Str, I64} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.662; procedure List.71 (#Attr.2, #Attr.3): - let List.682 : List {U32, U32} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.682; - -procedure List.83 (List.175, List.176, List.177): - let List.665 : U64 = 0i64; - let List.666 : U64 = CallByName List.6 List.175; - let List.664 : List {U32, U32} = CallByName List.96 List.175 List.176 List.177 List.665 List.666; - ret List.664; - -procedure List.93 (#Derived_gen.63, #Derived_gen.64, #Derived_gen.65): - joinpoint List.676 List.146 List.147 List.148: - let List.684 : U64 = 0i64; - let List.678 : Int1 = CallByName Num.24 List.147 List.684; - if List.678 then - let List.683 : U64 = 1i64; - let List.680 : U64 = CallByName Num.75 List.147 List.683; - let List.681 : List {U32, U32} = CallByName List.71 List.148 List.146; - jump List.676 List.146 List.680 List.681; + let List.683 : List {U32, U32} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.683; + +procedure List.83 (List.176, List.177, List.178): + let List.666 : U64 = 0i64; + let List.667 : U64 = CallByName List.6 List.176; + let List.665 : List {U32, U32} = CallByName List.97 List.176 List.177 List.178 List.666 List.667; + ret List.665; + +procedure List.94 (#Derived_gen.54, #Derived_gen.55, #Derived_gen.56): + joinpoint List.677 List.147 List.148 List.149: + let List.685 : U64 = 0i64; + let List.679 : Int1 = CallByName Num.24 List.148 List.685; + if List.679 then + let List.684 : U64 = 1i64; + let List.681 : U64 = CallByName Num.75 List.148 List.684; + let List.682 : List {U32, U32} = CallByName List.71 List.149 List.147; + jump List.677 List.147 List.681 List.682; else - ret List.148; + ret List.149; in - jump List.676 #Derived_gen.63 #Derived_gen.64 #Derived_gen.65; - -procedure List.95 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): - joinpoint List.692 List.169 List.170 List.171 List.172 List.173: - let List.694 : Int1 = CallByName Num.22 List.172 List.173; - if List.694 then - let List.698 : {Str, I64} = CallByName List.66 List.169 List.172; - inc List.698; - let List.174 : {Str, Int1} = CallByName Dict.188 List.170 List.698 List.171; - let List.697 : U64 = 1i64; - let List.696 : U64 = CallByName Num.51 List.172 List.697; - jump List.692 List.169 List.174 List.171 List.696 List.173; + jump List.677 #Derived_gen.54 #Derived_gen.55 #Derived_gen.56; + +procedure List.96 (#Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16): + joinpoint List.693 List.170 List.171 List.172 List.173 List.174: + let List.695 : Int1 = CallByName Num.22 List.173 List.174; + if List.695 then + let List.699 : {Str, I64} = CallByName List.66 List.170 List.173; + inc List.699; + let List.175 : {Str, Int1} = CallByName Dict.188 List.171 List.699 List.172; + let List.698 : U64 = 1i64; + let List.697 : U64 = CallByName Num.51 List.173 List.698; + jump List.693 List.170 List.175 List.172 List.697 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.23; - jump List.692 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; - -procedure List.95 (#Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40, #Derived_gen.41): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {Str, I64} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.159 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + inc #Derived_gen.12; + jump List.693 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16; + +procedure List.96 (#Derived_gen.59, #Derived_gen.60, #Derived_gen.61, #Derived_gen.62, #Derived_gen.63): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {Str, I64} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.159 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.37; - jump List.628 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41; - -procedure List.96 (#Derived_gen.56, #Derived_gen.57, #Derived_gen.58, #Derived_gen.59, #Derived_gen.60): - joinpoint List.667 List.178 List.179 List.180 List.181 List.182: - let List.669 : Int1 = CallByName Num.22 List.181 List.182; - if List.669 then - let List.673 : {Str, I64} = CallByName List.66 List.178 List.181; - inc List.673; - let List.183 : List {U32, U32} = CallByName Dict.406 List.179 List.673 List.181 List.180; - let List.672 : U64 = 1i64; - let List.671 : U64 = CallByName Num.51 List.181 List.672; - jump List.667 List.178 List.183 List.180 List.671 List.182; + inc #Derived_gen.59; + jump List.629 #Derived_gen.59 #Derived_gen.60 #Derived_gen.61 #Derived_gen.62 #Derived_gen.63; + +procedure List.97 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34, #Derived_gen.35): + joinpoint List.668 List.179 List.180 List.181 List.182 List.183: + let List.670 : Int1 = CallByName Num.22 List.182 List.183; + if List.670 then + let List.674 : {Str, I64} = CallByName List.66 List.179 List.182; + inc List.674; + let List.184 : List {U32, U32} = CallByName Dict.406 List.180 List.674 List.182 List.181; + let List.673 : U64 = 1i64; + let List.672 : U64 = CallByName Num.51 List.182 List.673; + jump List.668 List.179 List.184 List.181 List.672 List.183; else - dec List.178; - ret List.179; + dec List.179; + ret List.180; in - inc #Derived_gen.56; - jump List.667 #Derived_gen.56 #Derived_gen.57 #Derived_gen.58 #Derived_gen.59 #Derived_gen.60; + inc #Derived_gen.31; + jump List.668 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35; procedure Num.131 (#Attr.2): let Num.289 : U32 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/inspect_derived_list.txt b/crates/compiler/test_mono/generated/inspect_derived_list.txt index 0afd5cf8c30..0df9b458c71 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_list.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_list.txt @@ -135,35 +135,35 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : {Str, Int1} = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : {Str, Int1} = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; - -procedure List.95 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : I64 = CallByName List.66 List.169 List.172; - let List.174 : {Str, Int1} = CallByName Inspect.160 List.170 List.634 List.171; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.636 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; + +procedure List.96 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : I64 = CallByName List.66 List.170 List.173; + let List.175 : {Str, Int1} = CallByName Inspect.160 List.171 List.635 List.172; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.19; - jump List.628 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23; + jump List.629 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23; procedure Num.22 (#Attr.2, #Attr.3): let Num.283 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt index 710bc39f8b6..67829099fcc 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt @@ -221,67 +221,67 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : {Str, Int1} = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; - -procedure List.18 (List.166, List.167, List.168): - let List.638 : U64 = 0i64; - let List.639 : U64 = CallByName List.6 List.166; - let List.637 : {Str, Int1} = CallByName List.95 List.166 List.167 List.168 List.638 List.639; - ret List.637; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : {Str, Int1} = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; + +procedure List.18 (List.167, List.168, List.169): + let List.639 : U64 = 0i64; + let List.640 : U64 = CallByName List.6 List.167; + let List.638 : {Str, Int1} = CallByName List.96 List.167 List.168 List.169 List.639 List.640; + ret List.638; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.6 (#Attr.2): - let List.648 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.648; + let List.649 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.649; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.66 (#Attr.2, #Attr.3): - let List.647 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.647; - -procedure List.95 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26): - joinpoint List.640 List.169 List.170 List.171 List.172 List.173: - let List.642 : Int1 = CallByName Num.22 List.172 List.173; - if List.642 then - let List.646 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.646; - let List.174 : {Str, Int1} = CallByName Inspect.233 List.170 List.646; - let List.645 : U64 = 1i64; - let List.644 : U64 = CallByName Num.51 List.172 List.645; - jump List.640 List.169 List.174 List.171 List.644 List.173; + let List.648 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.648; + +procedure List.96 (#Derived_gen.28, #Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32): + joinpoint List.641 List.170 List.171 List.172 List.173 List.174: + let List.643 : Int1 = CallByName Num.22 List.173 List.174; + if List.643 then + let List.647 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.647; + let List.175 : {Str, Int1} = CallByName Inspect.233 List.171 List.647; + let List.646 : U64 = 1i64; + let List.645 : U64 = CallByName Num.51 List.173 List.646; + jump List.641 List.170 List.175 List.172 List.645 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.22; - jump List.640 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26; - -procedure List.95 (#Derived_gen.43, #Derived_gen.44, #Derived_gen.45, #Derived_gen.46, #Derived_gen.47): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : {Str, Int1} = CallByName Inspect.233 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + inc #Derived_gen.28; + jump List.641 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32; + +procedure List.96 (#Derived_gen.33, #Derived_gen.34, #Derived_gen.35, #Derived_gen.36, #Derived_gen.37): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : {Str, Int1} = CallByName Inspect.233 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.43; - jump List.628 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46 #Derived_gen.47; + inc #Derived_gen.33; + jump List.629 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37; procedure Num.22 (#Attr.2, #Attr.3): let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/inspect_derived_record.txt b/crates/compiler/test_mono/generated/inspect_derived_record.txt index cb4e330eaca..f87383ea6d1 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_record.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_record.txt @@ -161,36 +161,36 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : {Str, Int1} = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : {Str, Int1} = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : {[C I64, C Decimal], Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; - -procedure List.95 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {[C I64, C Decimal], Str} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : {Str, Int1} = CallByName Inspect.233 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.636 : {[C I64, C Decimal], Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; + +procedure List.96 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {[C I64, C Decimal], Str} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : {Str, Int1} = CallByName Inspect.233 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.26; - jump List.628 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; + inc #Derived_gen.22; + jump List.629 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26; procedure Num.22 (#Attr.2, #Attr.3): let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt index ca3c2197c76..9811214c088 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt @@ -139,36 +139,36 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : {Str, Int1} = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : {Str, Int1} = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; - -procedure List.95 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : {Str, Int1} = CallByName Inspect.233 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.636 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; + +procedure List.96 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : {Str, Int1} = CallByName Inspect.233 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.14; - jump List.628 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; + inc #Derived_gen.16; + jump List.629 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt index 26460742c2a..d8856469ce7 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt @@ -146,36 +146,36 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : {Str, Int1} = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : {Str, Int1} = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; - -procedure List.95 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : {Str, Str} = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : {Str, Int1} = CallByName Inspect.233 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.636 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; + +procedure List.96 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : {Str, Str} = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : {Str, Int1} = CallByName Inspect.233 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.18; - jump List.628 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22; + inc #Derived_gen.20; + jump List.629 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt index 19e5c284353..5faa75623bb 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt @@ -132,43 +132,43 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.1 (List.113): - let List.638 : U64 = CallByName List.6 List.113; - let List.639 : U64 = 0i64; - let List.637 : Int1 = CallByName Bool.11 List.638 List.639; - ret List.637; - -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : Str = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.1 (List.114): + let List.639 : U64 = CallByName List.6 List.114; + let List.640 : U64 = 0i64; + let List.638 : Int1 = CallByName Bool.11 List.639 List.640; + ret List.638; + +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : Str = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; - -procedure List.95 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : Str = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : Str = CallByName Inspect.210 List.170 List.634; - dec List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.636 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; + +procedure List.96 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : Str = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : Str = CallByName Inspect.210 List.171 List.635; + dec List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.14; - jump List.628 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; + inc #Derived_gen.20; + jump List.629 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt index 2f5b9e13a96..4239ac4296b 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt @@ -135,43 +135,43 @@ procedure Inspect.63 (Inspect.300, Inspect.296): procedure Inspect.64 (Inspect.302): ret Inspect.302; -procedure List.1 (List.113): - let List.638 : U64 = CallByName List.6 List.113; - let List.639 : U64 = 0i64; - let List.637 : Int1 = CallByName Bool.11 List.638 List.639; - ret List.637; - -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : Str = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.1 (List.114): + let List.639 : U64 = CallByName List.6 List.114; + let List.640 : U64 = 0i64; + let List.638 : Int1 = CallByName Bool.11 List.639 List.640; + ret List.638; + +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : Str = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; - -procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : Str = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : Str = CallByName Inspect.210 List.170 List.634; - dec List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.636 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; + +procedure List.96 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : Str = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : Str = CallByName Inspect.210 List.171 List.635; + dec List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.13; - jump List.628 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + jump List.629 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/ir_int_add.txt b/crates/compiler/test_mono/generated/ir_int_add.txt index 08bec6d3c2a..9ece816509c 100644 --- a/crates/compiler/test_mono/generated/ir_int_add.txt +++ b/crates/compiler/test_mono/generated/ir_int_add.txt @@ -1,6 +1,6 @@ procedure List.6 (#Attr.2): - let List.625 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.625; + let List.626 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.626; procedure Num.19 (#Attr.2, #Attr.3): let Num.283 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt index 36cf4fee81b..ce9caa2585c 100644 --- a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -6,40 +6,40 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure List.2 (List.114, List.115): - let List.639 : U64 = CallByName List.6 List.114; - let List.635 : Int1 = CallByName Num.22 List.115 List.639; - if List.635 then - let List.637 : I64 = CallByName List.66 List.114 List.115; - let List.636 : [C {}, C I64] = TagId(1) List.637; - ret List.636; +procedure List.2 (List.115, List.116): + let List.640 : U64 = CallByName List.6 List.115; + let List.636 : Int1 = CallByName Num.22 List.116 List.640; + if List.636 then + let List.638 : I64 = CallByName List.66 List.115 List.116; + let List.637 : [C {}, C I64] = TagId(1) List.638; + ret List.637; else - let List.634 : {} = Struct {}; - let List.633 : [C {}, C I64] = TagId(0) List.634; - ret List.633; + let List.635 : {} = Struct {}; + let List.634 : [C {}, C I64] = TagId(0) List.635; + ret List.634; procedure List.6 (#Attr.2): - let List.640 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.640; + let List.641 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.641; procedure List.66 (#Attr.2, #Attr.3): - let List.638 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.638; + let List.639 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.639; -procedure List.9 (List.386): - let List.632 : U64 = 0i64; - let List.625 : [C {}, C I64] = CallByName List.2 List.386 List.632; - let List.629 : U8 = 1i64; - let List.630 : U8 = GetTagId List.625; - let List.631 : Int1 = lowlevel Eq List.629 List.630; - if List.631 then - let List.387 : I64 = UnionAtIndex (Id 1) (Index 0) List.625; - let List.626 : [C Int1, C I64] = TagId(1) List.387; - ret List.626; - else - let List.628 : Int1 = true; - let List.627 : [C Int1, C I64] = TagId(0) List.628; +procedure List.9 (List.387): + let List.633 : U64 = 0i64; + let List.626 : [C {}, C I64] = CallByName List.2 List.387 List.633; + let List.630 : U8 = 1i64; + let List.631 : U8 = GetTagId List.626; + let List.632 : Int1 = lowlevel Eq List.630 List.631; + if List.632 then + let List.388 : I64 = UnionAtIndex (Id 1) (Index 0) List.626; + let List.627 : [C Int1, C I64] = TagId(1) List.388; ret List.627; + else + let List.629 : Int1 = true; + let List.628 : [C Int1, C I64] = TagId(0) List.629; + ret List.628; procedure Num.22 (#Attr.2, #Attr.3): let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/issue_4770.txt b/crates/compiler/test_mono/generated/issue_4770.txt index 084d50b6119..3bc2dbf8345 100644 --- a/crates/compiler/test_mono/generated/issue_4770.txt +++ b/crates/compiler/test_mono/generated/issue_4770.txt @@ -6,118 +6,118 @@ procedure Bool.2 (): let Bool.24 : Int1 = true; ret Bool.24; -procedure List.100 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6): - joinpoint List.663 List.285 List.286 List.287 List.288 List.289 List.290: - let List.665 : Int1 = CallByName Num.22 List.289 List.290; - if List.665 then - let List.671 : [C I64, C List *self] = CallByName List.66 List.285 List.289; - inc List.671; - let List.672 : [C I64, C List *self] = CallByName List.66 List.286 List.289; +procedure List.101 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6): + joinpoint List.664 List.286 List.287 List.288 List.289 List.290 List.291: + let List.666 : Int1 = CallByName Num.22 List.290 List.291; + if List.666 then + let List.672 : [C I64, C List *self] = CallByName List.66 List.286 List.290; inc List.672; - let List.291 : {[C I64, C List *self], [C I64, C List *self]} = CallByName Test.15 List.671 List.672; - let List.667 : List {[C I64, C List *self], [C I64, C List *self]} = CallByName List.71 List.287 List.291; - let List.669 : U64 = 1i64; - let List.668 : U64 = CallByName Num.51 List.289 List.669; - jump List.663 List.285 List.286 List.667 List.288 List.668 List.290; + let List.673 : [C I64, C List *self] = CallByName List.66 List.287 List.290; + inc List.673; + let List.292 : {[C I64, C List *self], [C I64, C List *self]} = CallByName Test.15 List.672 List.673; + let List.668 : List {[C I64, C List *self], [C I64, C List *self]} = CallByName List.71 List.288 List.292; + let List.670 : U64 = 1i64; + let List.669 : U64 = CallByName Num.51 List.290 List.670; + jump List.664 List.286 List.287 List.668 List.289 List.669 List.291; else dec List.286; - dec List.285; - ret List.287; + dec List.287; + ret List.288; in inc #Derived_gen.1; inc #Derived_gen.2; - jump List.663 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6; + jump List.664 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6; -procedure List.110 (List.539, List.540, List.541): - let List.639 : U64 = 0i64; - let List.640 : U64 = CallByName List.6 List.539; - let List.638 : [C {}, C {}] = CallByName List.80 List.539 List.540 List.541 List.639 List.640; - ret List.638; +procedure List.111 (List.540, List.541, List.542): + let List.640 : U64 = 0i64; + let List.641 : U64 = CallByName List.6 List.540; + let List.639 : [C {}, C {}] = CallByName List.80 List.540 List.541 List.542 List.640 List.641; + ret List.639; -procedure List.23 (List.281, List.282, List.283): - let List.675 : U64 = CallByName List.6 List.281; +procedure List.23 (List.282, List.283, List.284): let List.676 : U64 = CallByName List.6 List.282; - let List.284 : U64 = CallByName Num.148 List.675 List.676; - let List.661 : List {[C I64, C List *self], [C I64, C List *self]} = CallByName List.68 List.284; - let List.662 : U64 = 0i64; - let List.660 : List {[C I64, C List *self], [C I64, C List *self]} = CallByName List.100 List.281 List.282 List.661 List.283 List.662 List.284; - ret List.660; - -procedure List.242 (List.627, List.243, List.241): - let List.657 : Int1 = CallByName Test.1 List.243; - if List.657 then - let List.659 : {} = Struct {}; - let List.658 : [C {}, C {}] = TagId(1) List.659; - ret List.658; + let List.677 : U64 = CallByName List.6 List.283; + let List.285 : U64 = CallByName Num.148 List.676 List.677; + let List.662 : List {[C I64, C List *self], [C I64, C List *self]} = CallByName List.68 List.285; + let List.663 : U64 = 0i64; + let List.661 : List {[C I64, C List *self], [C I64, C List *self]} = CallByName List.101 List.282 List.283 List.662 List.284 List.663 List.285; + ret List.661; + +procedure List.243 (List.628, List.244, List.242): + let List.658 : Int1 = CallByName Test.1 List.244; + if List.658 then + let List.660 : {} = Struct {}; + let List.659 : [C {}, C {}] = TagId(1) List.660; + ret List.659; else - let List.656 : {} = Struct {}; - let List.655 : [C {}, C {}] = TagId(0) List.656; - ret List.655; - -procedure List.56 (List.240, List.241): - let List.636 : {} = Struct {}; - let List.628 : [C {}, C {}] = CallByName List.110 List.240 List.636 List.241; - let List.633 : U8 = 1i64; - let List.634 : U8 = GetTagId List.628; - let List.635 : Int1 = lowlevel Eq List.633 List.634; - if List.635 then - let List.629 : Int1 = CallByName Bool.2; - ret List.629; - else - let List.630 : Int1 = CallByName Bool.1; + let List.657 : {} = Struct {}; + let List.656 : [C {}, C {}] = TagId(0) List.657; + ret List.656; + +procedure List.56 (List.241, List.242): + let List.637 : {} = Struct {}; + let List.629 : [C {}, C {}] = CallByName List.111 List.241 List.637 List.242; + let List.634 : U8 = 1i64; + let List.635 : U8 = GetTagId List.629; + let List.636 : Int1 = lowlevel Eq List.634 List.635; + if List.636 then + let List.630 : Int1 = CallByName Bool.2; ret List.630; + else + let List.631 : Int1 = CallByName Bool.1; + ret List.631; procedure List.6 (#Attr.2): - let List.626 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.626; + let List.627 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.627; procedure List.6 (#Attr.2): - let List.654 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.654; + let List.655 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.655; procedure List.66 (#Attr.2, #Attr.3): - let List.653 : {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.653; + let List.654 : {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.654; procedure List.66 (#Attr.2, #Attr.3): - let List.673 : [C I64, C List *self] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.673; + let List.674 : [C I64, C List *self] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.674; procedure List.68 (#Attr.2): - let List.674 : List {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListWithCapacity #Attr.2; - ret List.674; + let List.675 : List {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListWithCapacity #Attr.2; + ret List.675; procedure List.71 (#Attr.2, #Attr.3): - let List.670 : List {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.670; + let List.671 : List {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.671; procedure List.80 (#Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11): - joinpoint List.641 List.542 List.543 List.544 List.545 List.546: - let List.643 : Int1 = CallByName Num.22 List.545 List.546; - if List.643 then - let List.652 : {[C I64, C List *self], [C I64, C List *self]} = CallByName List.66 List.542 List.545; - inc List.652; - let List.644 : [C {}, C {}] = CallByName List.242 List.543 List.652 List.544; - let List.649 : U8 = 1i64; - let List.650 : U8 = GetTagId List.644; - let List.651 : Int1 = lowlevel Eq List.649 List.650; - if List.651 then - let List.547 : {} = UnionAtIndex (Id 1) (Index 0) List.644; - let List.647 : U64 = 1i64; - let List.646 : U64 = CallByName Num.51 List.545 List.647; - jump List.641 List.542 List.547 List.544 List.646 List.546; + joinpoint List.642 List.543 List.544 List.545 List.546 List.547: + let List.644 : Int1 = CallByName Num.22 List.546 List.547; + if List.644 then + let List.653 : {[C I64, C List *self], [C I64, C List *self]} = CallByName List.66 List.543 List.546; + inc List.653; + let List.645 : [C {}, C {}] = CallByName List.243 List.544 List.653 List.545; + let List.650 : U8 = 1i64; + let List.651 : U8 = GetTagId List.645; + let List.652 : Int1 = lowlevel Eq List.650 List.651; + if List.652 then + let List.548 : {} = UnionAtIndex (Id 1) (Index 0) List.645; + let List.648 : U64 = 1i64; + let List.647 : U64 = CallByName Num.51 List.546 List.648; + jump List.642 List.543 List.548 List.545 List.647 List.547; else - dec List.542; - let List.548 : {} = UnionAtIndex (Id 0) (Index 0) List.644; - let List.648 : [C {}, C {}] = TagId(0) List.548; - ret List.648; + dec List.543; + let List.549 : {} = UnionAtIndex (Id 0) (Index 0) List.645; + let List.649 : [C {}, C {}] = TagId(0) List.549; + ret List.649; else - dec List.542; - let List.642 : [C {}, C {}] = TagId(1) List.543; - ret List.642; + dec List.543; + let List.643 : [C {}, C {}] = TagId(1) List.544; + ret List.643; in inc #Derived_gen.7; - jump List.641 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11; + jump List.642 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11; procedure Num.148 (Num.225, Num.226): let Num.288 : Int1 = CallByName Num.22 Num.225 Num.226; diff --git a/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt b/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt index 2dba42c8ebe..a9e937ea523 100644 --- a/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt +++ b/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt @@ -1,33 +1,33 @@ -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : [, C {[C *self, ], *self}] = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : [, C {[C *self, ], *self}] = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; procedure List.6 (#Attr.2): - let List.636 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.636; + let List.637 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.637; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : [C *self, ] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : [C *self, ] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; -procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : [C *self, ] = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : [, C {[C *self, ], *self}] = CallByName Test.7 List.170 List.634; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; +procedure List.96 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : [C *self, ] = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : [, C {[C *self, ], *self}] = CallByName Test.7 List.171 List.635; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.0; - jump List.628 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.629 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_append.txt b/crates/compiler/test_mono/generated/list_append.txt index 19886c2f757..2092957cd18 100644 --- a/crates/compiler/test_mono/generated/list_append.txt +++ b/crates/compiler/test_mono/generated/list_append.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.130, List.131): - let List.628 : U64 = 1i64; - let List.626 : List I64 = CallByName List.70 List.130 List.628; - let List.625 : List I64 = CallByName List.71 List.626 List.131; - ret List.625; +procedure List.4 (List.131, List.132): + let List.629 : U64 = 1i64; + let List.627 : List I64 = CallByName List.70 List.131 List.629; + let List.626 : List I64 = CallByName List.71 List.627 List.132; + ret List.626; procedure List.70 (#Attr.2, #Attr.3): - let List.629 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.629; + let List.630 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.630; procedure List.71 (#Attr.2, #Attr.3): - let List.627 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.627; + let List.628 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.628; procedure Test.0 (): let Test.2 : List I64 = Array [1i64]; diff --git a/crates/compiler/test_mono/generated/list_append_closure.txt b/crates/compiler/test_mono/generated/list_append_closure.txt index bac1571547c..f3083f098f9 100644 --- a/crates/compiler/test_mono/generated/list_append_closure.txt +++ b/crates/compiler/test_mono/generated/list_append_closure.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.130, List.131): - let List.628 : U64 = 1i64; - let List.626 : List I64 = CallByName List.70 List.130 List.628; - let List.625 : List I64 = CallByName List.71 List.626 List.131; - ret List.625; +procedure List.4 (List.131, List.132): + let List.629 : U64 = 1i64; + let List.627 : List I64 = CallByName List.70 List.131 List.629; + let List.626 : List I64 = CallByName List.71 List.627 List.132; + ret List.626; procedure List.70 (#Attr.2, #Attr.3): - let List.629 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.629; + let List.630 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.630; procedure List.71 (#Attr.2, #Attr.3): - let List.627 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.627; + let List.628 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.628; procedure Test.1 (Test.2): let Test.6 : I64 = 42i64; diff --git a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt index 9aa88090dd5..3eea497ce7d 100644 --- a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt +++ b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt @@ -1,25 +1,25 @@ -procedure List.3 (List.122, List.123, List.124): - let List.628 : {List I64, I64} = CallByName List.64 List.122 List.123 List.124; - let List.627 : List I64 = StructAtIndex 0 List.628; - ret List.627; +procedure List.3 (List.123, List.124, List.125): + let List.629 : {List I64, I64} = CallByName List.64 List.123 List.124 List.125; + let List.628 : List I64 = StructAtIndex 0 List.629; + ret List.628; procedure List.6 (#Attr.2): - let List.626 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.626; - -procedure List.64 (List.119, List.120, List.121): - let List.633 : U64 = CallByName List.6 List.119; - let List.630 : Int1 = CallByName Num.22 List.120 List.633; - if List.630 then - let List.631 : {List I64, I64} = CallByName List.67 List.119 List.120 List.121; - ret List.631; + let List.627 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.627; + +procedure List.64 (List.120, List.121, List.122): + let List.634 : U64 = CallByName List.6 List.120; + let List.631 : Int1 = CallByName Num.22 List.121 List.634; + if List.631 then + let List.632 : {List I64, I64} = CallByName List.67 List.120 List.121 List.122; + ret List.632; else - let List.629 : {List I64, I64} = Struct {List.119, List.121}; - ret List.629; + let List.630 : {List I64, I64} = Struct {List.120, List.122}; + ret List.630; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.632 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.632; + let List.633 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.633; procedure Num.19 (#Attr.2, #Attr.3): let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_get.txt b/crates/compiler/test_mono/generated/list_get.txt index ae71bf6b5f5..f3dbacbd928 100644 --- a/crates/compiler/test_mono/generated/list_get.txt +++ b/crates/compiler/test_mono/generated/list_get.txt @@ -1,22 +1,22 @@ -procedure List.2 (List.114, List.115): - let List.631 : U64 = CallByName List.6 List.114; - let List.627 : Int1 = CallByName Num.22 List.115 List.631; - if List.627 then - let List.629 : I64 = CallByName List.66 List.114 List.115; - let List.628 : [C {}, C I64] = TagId(1) List.629; - ret List.628; +procedure List.2 (List.115, List.116): + let List.632 : U64 = CallByName List.6 List.115; + let List.628 : Int1 = CallByName Num.22 List.116 List.632; + if List.628 then + let List.630 : I64 = CallByName List.66 List.115 List.116; + let List.629 : [C {}, C I64] = TagId(1) List.630; + ret List.629; else - let List.626 : {} = Struct {}; - let List.625 : [C {}, C I64] = TagId(0) List.626; - ret List.625; + let List.627 : {} = Struct {}; + let List.626 : [C {}, C I64] = TagId(0) List.627; + ret List.626; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; procedure List.66 (#Attr.2, #Attr.3): - let List.630 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.630; + let List.631 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.631; procedure Num.22 (#Attr.2, #Attr.3): let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_len.txt b/crates/compiler/test_mono/generated/list_len.txt index f4328073455..856c83106f2 100644 --- a/crates/compiler/test_mono/generated/list_len.txt +++ b/crates/compiler/test_mono/generated/list_len.txt @@ -1,11 +1,11 @@ -procedure List.6 (#Attr.2): - let List.625 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.625; - procedure List.6 (#Attr.2): let List.626 : U64 = lowlevel ListLenU64 #Attr.2; ret List.626; +procedure List.6 (#Attr.2): + let List.627 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.627; + procedure Num.19 (#Attr.2, #Attr.3): let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.281; diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 243ae197724..2e42dd02f4e 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -1,66 +1,66 @@ -procedure List.18 (List.166, List.167, List.168): - let List.637 : U64 = 0i64; - let List.638 : U64 = CallByName List.6 List.166; - let List.636 : List Str = CallByName List.95 List.166 List.167 List.168 List.637 List.638; - ret List.636; - -procedure List.2 (List.114, List.115): - let List.631 : U64 = CallByName List.6 List.114; - let List.627 : Int1 = CallByName Num.22 List.115 List.631; - if List.627 then - let List.629 : Str = CallByName List.66 List.114 List.115; - inc List.629; - let List.628 : [C {}, C Str] = TagId(1) List.629; - ret List.628; +procedure List.18 (List.167, List.168, List.169): + let List.638 : U64 = 0i64; + let List.639 : U64 = CallByName List.6 List.167; + let List.637 : List Str = CallByName List.96 List.167 List.168 List.169 List.638 List.639; + ret List.637; + +procedure List.2 (List.115, List.116): + let List.632 : U64 = CallByName List.6 List.115; + let List.628 : Int1 = CallByName Num.22 List.116 List.632; + if List.628 then + let List.630 : Str = CallByName List.66 List.115 List.116; + inc List.630; + let List.629 : [C {}, C Str] = TagId(1) List.630; + ret List.629; else - let List.626 : {} = Struct {}; - let List.625 : [C {}, C Str] = TagId(0) List.626; - ret List.625; - -procedure List.278 (List.279, List.280, List.276): - let List.648 : Str = CallByName Test.3 List.280; - let List.647 : List Str = CallByName List.71 List.279 List.648; - ret List.647; - -procedure List.5 (List.275, List.276): - let List.277 : U64 = CallByName List.6 List.275; - let List.634 : List Str = CallByName List.68 List.277; - let List.633 : List Str = CallByName List.18 List.275 List.634 List.276; - ret List.633; + let List.627 : {} = Struct {}; + let List.626 : [C {}, C Str] = TagId(0) List.627; + ret List.626; + +procedure List.279 (List.280, List.281, List.277): + let List.649 : Str = CallByName Test.3 List.281; + let List.648 : List Str = CallByName List.71 List.280 List.649; + ret List.648; + +procedure List.5 (List.276, List.277): + let List.278 : U64 = CallByName List.6 List.276; + let List.635 : List Str = CallByName List.68 List.278; + let List.634 : List Str = CallByName List.18 List.276 List.635 List.277; + ret List.634; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; procedure List.66 (#Attr.2, #Attr.3): - let List.630 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.630; + let List.631 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.631; procedure List.68 (#Attr.2): - let List.650 : List Str = lowlevel ListWithCapacity #Attr.2; - ret List.650; + let List.651 : List Str = lowlevel ListWithCapacity #Attr.2; + ret List.651; procedure List.71 (#Attr.2, #Attr.3): - let List.649 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.649; - -procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.639 List.169 List.170 List.171 List.172 List.173: - let List.641 : Int1 = CallByName Num.22 List.172 List.173; - if List.641 then - let List.645 : Str = CallByName List.66 List.169 List.172; - inc List.645; - let List.174 : List Str = CallByName List.278 List.170 List.645 List.171; - dec List.645; - let List.644 : U64 = 1i64; - let List.643 : U64 = CallByName Num.51 List.172 List.644; - jump List.639 List.169 List.174 List.171 List.643 List.173; + let List.650 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.650; + +procedure List.96 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.640 List.170 List.171 List.172 List.173 List.174: + let List.642 : Int1 = CallByName Num.22 List.173 List.174; + if List.642 then + let List.646 : Str = CallByName List.66 List.170 List.173; + inc List.646; + let List.175 : List Str = CallByName List.279 List.171 List.646 List.172; + dec List.646; + let List.645 : U64 = 1i64; + let List.644 : U64 = CallByName Num.51 List.173 List.645; + jump List.640 List.170 List.175 List.172 List.644 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.0; - jump List.639 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.640 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index 54b72f9c4be..0a2241854ad 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -1,65 +1,65 @@ -procedure List.18 (List.166, List.167, List.168): - let List.637 : U64 = 0i64; - let List.638 : U64 = CallByName List.6 List.166; - let List.636 : List Str = CallByName List.95 List.166 List.167 List.168 List.637 List.638; - ret List.636; +procedure List.18 (List.167, List.168, List.169): + let List.638 : U64 = 0i64; + let List.639 : U64 = CallByName List.6 List.167; + let List.637 : List Str = CallByName List.96 List.167 List.168 List.169 List.638 List.639; + ret List.637; -procedure List.2 (List.114, List.115): - let List.631 : U64 = CallByName List.6 List.114; - let List.627 : Int1 = CallByName Num.22 List.115 List.631; - if List.627 then - let List.629 : Str = CallByName List.66 List.114 List.115; - inc List.629; - let List.628 : [C {}, C Str] = TagId(1) List.629; - ret List.628; +procedure List.2 (List.115, List.116): + let List.632 : U64 = CallByName List.6 List.115; + let List.628 : Int1 = CallByName Num.22 List.116 List.632; + if List.628 then + let List.630 : Str = CallByName List.66 List.115 List.116; + inc List.630; + let List.629 : [C {}, C Str] = TagId(1) List.630; + ret List.629; else - let List.626 : {} = Struct {}; - let List.625 : [C {}, C Str] = TagId(0) List.626; - ret List.625; + let List.627 : {} = Struct {}; + let List.626 : [C {}, C Str] = TagId(0) List.627; + ret List.626; -procedure List.278 (List.279, List.280, List.276): - let List.648 : Str = CallByName Test.3 List.280; - let List.647 : List Str = CallByName List.71 List.279 List.648; - ret List.647; +procedure List.279 (List.280, List.281, List.277): + let List.649 : Str = CallByName Test.3 List.281; + let List.648 : List Str = CallByName List.71 List.280 List.649; + ret List.648; -procedure List.5 (List.275, List.276): - let List.277 : U64 = CallByName List.6 List.275; - let List.634 : List Str = CallByName List.68 List.277; - let List.633 : List Str = CallByName List.18 List.275 List.634 List.276; - ret List.633; +procedure List.5 (List.276, List.277): + let List.278 : U64 = CallByName List.6 List.276; + let List.635 : List Str = CallByName List.68 List.278; + let List.634 : List Str = CallByName List.18 List.276 List.635 List.277; + ret List.634; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; procedure List.66 (#Attr.2, #Attr.3): - let List.630 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.630; + let List.631 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.631; procedure List.68 (#Attr.2): - let List.650 : List Str = lowlevel ListWithCapacity #Attr.2; - ret List.650; + let List.651 : List Str = lowlevel ListWithCapacity #Attr.2; + ret List.651; procedure List.71 (#Attr.2, #Attr.3): - let List.649 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.649; + let List.650 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.650; -procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.639 List.169 List.170 List.171 List.172 List.173: - let List.641 : Int1 = CallByName Num.22 List.172 List.173; - if List.641 then - let List.645 : Str = CallByName List.66 List.169 List.172; - inc List.645; - let List.174 : List Str = CallByName List.278 List.170 List.645 List.171; - let List.644 : U64 = 1i64; - let List.643 : U64 = CallByName Num.51 List.172 List.644; - jump List.639 List.169 List.174 List.171 List.643 List.173; +procedure List.96 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.640 List.170 List.171 List.172 List.173 List.174: + let List.642 : Int1 = CallByName Num.22 List.173 List.174; + if List.642 then + let List.646 : Str = CallByName List.66 List.170 List.173; + inc List.646; + let List.175 : List Str = CallByName List.279 List.171 List.646 List.172; + let List.645 : U64 = 1i64; + let List.644 : U64 = CallByName Num.51 List.173 List.645; + jump List.640 List.170 List.175 List.172 List.644 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.0; - jump List.639 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.640 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt b/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt index 7dc3b9c28e6..0b373c65e6a 100644 --- a/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt +++ b/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt @@ -1,66 +1,66 @@ -procedure List.18 (List.166, List.167, List.168): - let List.629 : U64 = 0i64; - let List.630 : U64 = CallByName List.6 List.166; - let List.628 : List U8 = CallByName List.95 List.166 List.167 List.168 List.629 List.630; - ret List.628; +procedure List.18 (List.167, List.168, List.169): + let List.630 : U64 = 0i64; + let List.631 : U64 = CallByName List.6 List.167; + let List.629 : List U8 = CallByName List.96 List.167 List.168 List.169 List.630 List.631; + ret List.629; -procedure List.278 (List.279, List.280, List.276): - let List.644 : U8 = GetTagId List.276; - joinpoint List.645 List.642: - let List.641 : List U8 = CallByName List.71 List.279 List.642; - ret List.641; +procedure List.279 (List.280, List.281, List.277): + let List.645 : U8 = GetTagId List.277; + joinpoint List.646 List.643: + let List.642 : List U8 = CallByName List.71 List.280 List.643; + ret List.642; in - switch List.644: + switch List.645: case 0: - let List.646 : U8 = CallByName Test.4 List.280 List.276; - jump List.645 List.646; + let List.647 : U8 = CallByName Test.4 List.281 List.277; + jump List.646 List.647; case 1: - let List.646 : U8 = CallByName Test.6 List.280 List.276; - jump List.645 List.646; + let List.647 : U8 = CallByName Test.6 List.281 List.277; + jump List.646 List.647; default: - let List.646 : U8 = CallByName Test.8 List.280; - jump List.645 List.646; + let List.647 : U8 = CallByName Test.8 List.281; + jump List.646 List.647; -procedure List.5 (List.275, List.276): - let List.277 : U64 = CallByName List.6 List.275; - let List.626 : List U8 = CallByName List.68 List.277; - let List.625 : List U8 = CallByName List.18 List.275 List.626 List.276; - ret List.625; +procedure List.5 (List.276, List.277): + let List.278 : U64 = CallByName List.6 List.276; + let List.627 : List U8 = CallByName List.68 List.278; + let List.626 : List U8 = CallByName List.18 List.276 List.627 List.277; + ret List.626; procedure List.6 (#Attr.2): - let List.639 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.639; + let List.640 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.640; procedure List.66 (#Attr.2, #Attr.3): - let List.638 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.638; + let List.639 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.639; procedure List.68 (#Attr.2): - let List.647 : List U8 = lowlevel ListWithCapacity #Attr.2; - ret List.647; + let List.648 : List U8 = lowlevel ListWithCapacity #Attr.2; + ret List.648; procedure List.71 (#Attr.2, #Attr.3): - let List.643 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.643; + let List.644 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.644; -procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.631 List.169 List.170 List.171 List.172 List.173: - let List.633 : Int1 = CallByName Num.22 List.172 List.173; - if List.633 then - let List.637 : U8 = CallByName List.66 List.169 List.172; - let List.174 : List U8 = CallByName List.278 List.170 List.637 List.171; - let List.636 : U64 = 1i64; - let List.635 : U64 = CallByName Num.51 List.172 List.636; - jump List.631 List.169 List.174 List.171 List.635 List.173; +procedure List.96 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7): + joinpoint List.632 List.170 List.171 List.172 List.173 List.174: + let List.634 : Int1 = CallByName Num.22 List.173 List.174; + if List.634 then + let List.638 : U8 = CallByName List.66 List.170 List.173; + let List.175 : List U8 = CallByName List.279 List.171 List.638 List.172; + let List.637 : U64 = 1i64; + let List.636 : U64 = CallByName Num.51 List.173 List.637; + jump List.632 List.170 List.175 List.172 List.636 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.0; - jump List.631 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + inc #Derived_gen.3; + jump List.632 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7; procedure Num.19 (#Attr.2, #Attr.3): let Num.283 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_pass_to_function.txt b/crates/compiler/test_mono/generated/list_pass_to_function.txt index c999a248533..f8b2b47a241 100644 --- a/crates/compiler/test_mono/generated/list_pass_to_function.txt +++ b/crates/compiler/test_mono/generated/list_pass_to_function.txt @@ -1,25 +1,25 @@ -procedure List.3 (List.122, List.123, List.124): - let List.626 : {List I64, I64} = CallByName List.64 List.122 List.123 List.124; - let List.625 : List I64 = StructAtIndex 0 List.626; - ret List.625; +procedure List.3 (List.123, List.124, List.125): + let List.627 : {List I64, I64} = CallByName List.64 List.123 List.124 List.125; + let List.626 : List I64 = StructAtIndex 0 List.627; + ret List.626; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; -procedure List.64 (List.119, List.120, List.121): - let List.631 : U64 = CallByName List.6 List.119; - let List.628 : Int1 = CallByName Num.22 List.120 List.631; - if List.628 then - let List.629 : {List I64, I64} = CallByName List.67 List.119 List.120 List.121; - ret List.629; +procedure List.64 (List.120, List.121, List.122): + let List.632 : U64 = CallByName List.6 List.120; + let List.629 : Int1 = CallByName Num.22 List.121 List.632; + if List.629 then + let List.630 : {List I64, I64} = CallByName List.67 List.120 List.121 List.122; + ret List.630; else - let List.627 : {List I64, I64} = Struct {List.119, List.121}; - ret List.627; + let List.628 : {List I64, I64} = Struct {List.120, List.122}; + ret List.628; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.630 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.630; + let List.631 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.631; procedure Num.22 (#Attr.2, #Attr.3): let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_sort_asc.txt b/crates/compiler/test_mono/generated/list_sort_asc.txt index 7143b80093c..13bae21ff23 100644 --- a/crates/compiler/test_mono/generated/list_sort_asc.txt +++ b/crates/compiler/test_mono/generated/list_sort_asc.txt @@ -1,11 +1,11 @@ procedure List.28 (#Attr.2, #Attr.3): - let List.627 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; - ret List.627; + let List.628 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; + ret List.628; -procedure List.59 (List.381): - let List.626 : {} = Struct {}; - let List.625 : List I64 = CallByName List.28 List.381 List.626; - ret List.625; +procedure List.59 (List.382): + let List.627 : {} = Struct {}; + let List.626 : List I64 = CallByName List.28 List.382 List.627; + ret List.626; procedure Num.46 (#Attr.2, #Attr.3): let Num.281 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/quicksort_swap.txt b/crates/compiler/test_mono/generated/quicksort_swap.txt index 427ce71ca01..08b0ccf931f 100644 --- a/crates/compiler/test_mono/generated/quicksort_swap.txt +++ b/crates/compiler/test_mono/generated/quicksort_swap.txt @@ -1,41 +1,41 @@ -procedure List.2 (List.114, List.115): - let List.647 : U64 = CallByName List.6 List.114; - let List.644 : Int1 = CallByName Num.22 List.115 List.647; - if List.644 then - let List.646 : I64 = CallByName List.66 List.114 List.115; - let List.645 : [C {}, C I64] = TagId(1) List.646; - ret List.645; +procedure List.2 (List.115, List.116): + let List.648 : U64 = CallByName List.6 List.115; + let List.645 : Int1 = CallByName Num.22 List.116 List.648; + if List.645 then + let List.647 : I64 = CallByName List.66 List.115 List.116; + let List.646 : [C {}, C I64] = TagId(1) List.647; + ret List.646; else - let List.643 : {} = Struct {}; - let List.642 : [C {}, C I64] = TagId(0) List.643; - ret List.642; + let List.644 : {} = Struct {}; + let List.643 : [C {}, C I64] = TagId(0) List.644; + ret List.643; -procedure List.3 (List.122, List.123, List.124): - let List.634 : {List I64, I64} = CallByName List.64 List.122 List.123 List.124; - let List.633 : List I64 = StructAtIndex 0 List.634; - ret List.633; +procedure List.3 (List.123, List.124, List.125): + let List.635 : {List I64, I64} = CallByName List.64 List.123 List.124 List.125; + let List.634 : List I64 = StructAtIndex 0 List.635; + ret List.634; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; -procedure List.64 (List.119, List.120, List.121): - let List.631 : U64 = CallByName List.6 List.119; - let List.628 : Int1 = CallByName Num.22 List.120 List.631; - if List.628 then - let List.629 : {List I64, I64} = CallByName List.67 List.119 List.120 List.121; - ret List.629; +procedure List.64 (List.120, List.121, List.122): + let List.632 : U64 = CallByName List.6 List.120; + let List.629 : Int1 = CallByName Num.22 List.121 List.632; + if List.629 then + let List.630 : {List I64, I64} = CallByName List.67 List.120 List.121 List.122; + ret List.630; else - let List.627 : {List I64, I64} = Struct {List.119, List.121}; - ret List.627; + let List.628 : {List I64, I64} = Struct {List.120, List.122}; + ret List.628; procedure List.66 (#Attr.2, #Attr.3): - let List.640 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.640; + let List.641 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.641; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.630 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.630; + let List.631 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.631; procedure Num.22 (#Attr.2, #Attr.3): let Num.283 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/record_update.txt b/crates/compiler/test_mono/generated/record_update.txt index b7d85329135..0da5473a3fc 100644 --- a/crates/compiler/test_mono/generated/record_update.txt +++ b/crates/compiler/test_mono/generated/record_update.txt @@ -1,25 +1,25 @@ -procedure List.3 (List.122, List.123, List.124): - let List.634 : {List U64, U64} = CallByName List.64 List.122 List.123 List.124; - let List.633 : List U64 = StructAtIndex 0 List.634; - ret List.633; +procedure List.3 (List.123, List.124, List.125): + let List.635 : {List U64, U64} = CallByName List.64 List.123 List.124 List.125; + let List.634 : List U64 = StructAtIndex 0 List.635; + ret List.634; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; -procedure List.64 (List.119, List.120, List.121): - let List.631 : U64 = CallByName List.6 List.119; - let List.628 : Int1 = CallByName Num.22 List.120 List.631; - if List.628 then - let List.629 : {List U64, U64} = CallByName List.67 List.119 List.120 List.121; - ret List.629; +procedure List.64 (List.120, List.121, List.122): + let List.632 : U64 = CallByName List.6 List.120; + let List.629 : Int1 = CallByName Num.22 List.121 List.632; + if List.629 then + let List.630 : {List U64, U64} = CallByName List.67 List.120 List.121 List.122; + ret List.630; else - let List.627 : {List U64, U64} = Struct {List.119, List.121}; - ret List.627; + let List.628 : {List U64, U64} = Struct {List.120, List.122}; + ret List.628; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.630 : {List U64, U64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.630; + let List.631 : {List U64, U64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.631; procedure Num.22 (#Attr.2, #Attr.3): let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt b/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt index d077c2d0f04..e7135242d1d 100644 --- a/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt +++ b/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt @@ -1,52 +1,52 @@ -procedure List.18 (List.166, List.167, List.168): - let List.629 : U64 = 0i64; - let List.630 : U64 = CallByName List.6 List.166; - let List.628 : List [C List *self] = CallByName List.95 List.166 List.167 List.168 List.629 List.630; - ret List.628; +procedure List.18 (List.167, List.168, List.169): + let List.630 : U64 = 0i64; + let List.631 : U64 = CallByName List.6 List.167; + let List.629 : List [C List *self] = CallByName List.96 List.167 List.168 List.169 List.630 List.631; + ret List.629; -procedure List.278 (List.279, List.280, List.276): - let List.642 : [C List *self] = CallByName Test.2 List.280; - let List.641 : List [C List *self] = CallByName List.71 List.279 List.642; - ret List.641; +procedure List.279 (List.280, List.281, List.277): + let List.643 : [C List *self] = CallByName Test.2 List.281; + let List.642 : List [C List *self] = CallByName List.71 List.280 List.643; + ret List.642; -procedure List.5 (List.275, List.276): - let List.277 : U64 = CallByName List.6 List.275; - let List.626 : List [C List *self] = CallByName List.68 List.277; - let List.625 : List [C List *self] = CallByName List.18 List.275 List.626 List.276; - ret List.625; +procedure List.5 (List.276, List.277): + let List.278 : U64 = CallByName List.6 List.276; + let List.627 : List [C List *self] = CallByName List.68 List.278; + let List.626 : List [C List *self] = CallByName List.18 List.276 List.627 List.277; + ret List.626; procedure List.6 (#Attr.2): - let List.639 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.639; + let List.640 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.640; procedure List.66 (#Attr.2, #Attr.3): - let List.638 : [C List *self] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.638; + let List.639 : [C List *self] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.639; procedure List.68 (#Attr.2): - let List.644 : List [C List *self] = lowlevel ListWithCapacity #Attr.2; - ret List.644; + let List.645 : List [C List *self] = lowlevel ListWithCapacity #Attr.2; + ret List.645; procedure List.71 (#Attr.2, #Attr.3): - let List.643 : List [C List *self] = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.643; + let List.644 : List [C List *self] = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.644; -procedure List.95 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4, #Derived_gen.5): - joinpoint List.631 List.169 List.170 List.171 List.172 List.173: - let List.633 : Int1 = CallByName Num.22 List.172 List.173; - if List.633 then - let List.637 : [C List *self] = CallByName List.66 List.169 List.172; - inc List.637; - let List.174 : List [C List *self] = CallByName List.278 List.170 List.637 List.171; - let List.636 : U64 = 1i64; - let List.635 : U64 = CallByName Num.51 List.172 List.636; - jump List.631 List.169 List.174 List.171 List.635 List.173; +procedure List.96 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4, #Derived_gen.5): + joinpoint List.632 List.170 List.171 List.172 List.173 List.174: + let List.634 : Int1 = CallByName Num.22 List.173 List.174; + if List.634 then + let List.638 : [C List *self] = CallByName List.66 List.170 List.173; + inc List.638; + let List.175 : List [C List *self] = CallByName List.279 List.171 List.638 List.172; + let List.637 : U64 = 1i64; + let List.636 : U64 = CallByName Num.51 List.173 List.637; + jump List.632 List.170 List.175 List.172 List.636 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in inc #Derived_gen.1; - jump List.631 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5; + jump List.632 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5; procedure Num.22 (#Attr.2, #Attr.3): let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/rigids.txt b/crates/compiler/test_mono/generated/rigids.txt index 6a20a5f1f53..36ace0b325d 100644 --- a/crates/compiler/test_mono/generated/rigids.txt +++ b/crates/compiler/test_mono/generated/rigids.txt @@ -1,41 +1,41 @@ -procedure List.2 (List.114, List.115): - let List.647 : U64 = CallByName List.6 List.114; - let List.644 : Int1 = CallByName Num.22 List.115 List.647; - if List.644 then - let List.646 : I64 = CallByName List.66 List.114 List.115; - let List.645 : [C {}, C I64] = TagId(1) List.646; - ret List.645; +procedure List.2 (List.115, List.116): + let List.648 : U64 = CallByName List.6 List.115; + let List.645 : Int1 = CallByName Num.22 List.116 List.648; + if List.645 then + let List.647 : I64 = CallByName List.66 List.115 List.116; + let List.646 : [C {}, C I64] = TagId(1) List.647; + ret List.646; else - let List.643 : {} = Struct {}; - let List.642 : [C {}, C I64] = TagId(0) List.643; - ret List.642; + let List.644 : {} = Struct {}; + let List.643 : [C {}, C I64] = TagId(0) List.644; + ret List.643; -procedure List.3 (List.122, List.123, List.124): - let List.634 : {List I64, I64} = CallByName List.64 List.122 List.123 List.124; - let List.633 : List I64 = StructAtIndex 0 List.634; - ret List.633; +procedure List.3 (List.123, List.124, List.125): + let List.635 : {List I64, I64} = CallByName List.64 List.123 List.124 List.125; + let List.634 : List I64 = StructAtIndex 0 List.635; + ret List.634; procedure List.6 (#Attr.2): - let List.632 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.632; + let List.633 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.633; -procedure List.64 (List.119, List.120, List.121): - let List.631 : U64 = CallByName List.6 List.119; - let List.628 : Int1 = CallByName Num.22 List.120 List.631; - if List.628 then - let List.629 : {List I64, I64} = CallByName List.67 List.119 List.120 List.121; - ret List.629; +procedure List.64 (List.120, List.121, List.122): + let List.632 : U64 = CallByName List.6 List.120; + let List.629 : Int1 = CallByName Num.22 List.121 List.632; + if List.629 then + let List.630 : {List I64, I64} = CallByName List.67 List.120 List.121 List.122; + ret List.630; else - let List.627 : {List I64, I64} = Struct {List.119, List.121}; - ret List.627; + let List.628 : {List I64, I64} = Struct {List.120, List.122}; + ret List.628; procedure List.66 (#Attr.2, #Attr.3): - let List.640 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.640; + let List.641 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.641; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.630 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.630; + let List.631 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.631; procedure Num.22 (#Attr.2, #Attr.3): let Num.283 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt index 983dc875c1e..9a44c6ead59 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt @@ -30,57 +30,57 @@ procedure Encode.26 (Encode.107, Encode.108): ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.651 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.651; + let List.652 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.652; -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : List U8 = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : List U8 = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; -procedure List.4 (List.130, List.131): - let List.647 : U64 = 1i64; - let List.646 : List U8 = CallByName List.70 List.130 List.647; - let List.645 : List U8 = CallByName List.71 List.646 List.131; - ret List.645; +procedure List.4 (List.131, List.132): + let List.648 : U64 = 1i64; + let List.647 : List U8 = CallByName List.70 List.131 List.648; + let List.646 : List U8 = CallByName List.71 List.647 List.132; + ret List.646; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.650; + let List.651 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.651; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.70 (#Attr.2, #Attr.3): - let List.641 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.641; + let List.642 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.642; procedure List.71 (#Attr.2, #Attr.3): - let List.639 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.639; + let List.640 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.640; procedure List.8 (#Attr.2, #Attr.3): - let List.649 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.649; - -procedure List.95 (#Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : Str = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : List U8 = CallByName Test.66 List.170 List.634 List.171; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.650 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.650; + +procedure List.96 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : Str = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : List U8 = CallByName Test.66 List.171 List.635 List.172; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.12; - jump List.628 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16; + inc #Derived_gen.0; + jump List.629 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.127 (#Attr.2): let Num.282 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt index c583b08b233..8b5bfb0a374 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt @@ -88,92 +88,92 @@ procedure Encode.26 (Encode.107, Encode.108): ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.651 : List [C {}, C {}, C Str] = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.651; - -procedure List.13 (#Attr.2, #Attr.3): - let List.679 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.679; - -procedure List.18 (List.166, List.167, List.168): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.166; - let List.625 : List U8 = CallByName List.95 List.166 List.167 List.168 List.626 List.627; - ret List.625; - -procedure List.18 (List.166, List.167, List.168): - let List.653 : U64 = 0i64; - let List.654 : U64 = CallByName List.6 List.166; - let List.652 : List U8 = CallByName List.95 List.166 List.167 List.168 List.653 List.654; + let List.652 : List [C {}, C {}, C Str] = lowlevel ListPrepend #Attr.2 #Attr.3; ret List.652; -procedure List.4 (List.130, List.131): - let List.674 : U64 = 1i64; - let List.673 : List U8 = CallByName List.70 List.130 List.674; - let List.672 : List U8 = CallByName List.71 List.673 List.131; - ret List.672; +procedure List.13 (#Attr.2, #Attr.3): + let List.680 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.680; + +procedure List.18 (List.167, List.168, List.169): + let List.627 : U64 = 0i64; + let List.628 : U64 = CallByName List.6 List.167; + let List.626 : List U8 = CallByName List.96 List.167 List.168 List.169 List.627 List.628; + ret List.626; + +procedure List.18 (List.167, List.168, List.169): + let List.654 : U64 = 0i64; + let List.655 : U64 = CallByName List.6 List.167; + let List.653 : List U8 = CallByName List.96 List.167 List.168 List.169 List.654 List.655; + ret List.653; + +procedure List.4 (List.131, List.132): + let List.675 : U64 = 1i64; + let List.674 : List U8 = CallByName List.70 List.131 List.675; + let List.673 : List U8 = CallByName List.71 List.674 List.132; + ret List.673; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.650; + let List.651 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.651; procedure List.6 (#Attr.2): - let List.677 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.677; + let List.678 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.678; procedure List.66 (#Attr.2, #Attr.3): - let List.635 : [C {}, C {}, C Str] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.635; + let List.636 : [C {}, C {}, C Str] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.636; procedure List.66 (#Attr.2, #Attr.3): - let List.662 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.662; + let List.663 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.663; procedure List.70 (#Attr.2, #Attr.3): - let List.668 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.668; + let List.669 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.669; procedure List.71 (#Attr.2, #Attr.3): - let List.666 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.666; + let List.667 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.667; procedure List.8 (#Attr.2, #Attr.3): - let List.676 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.676; - -procedure List.95 (#Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39): - joinpoint List.628 List.169 List.170 List.171 List.172 List.173: - let List.630 : Int1 = CallByName Num.22 List.172 List.173; - if List.630 then - let List.634 : [C {}, C {}, C Str] = CallByName List.66 List.169 List.172; - inc List.634; - let List.174 : List U8 = CallByName Test.66 List.170 List.634 List.171; - let List.633 : U64 = 1i64; - let List.632 : U64 = CallByName Num.51 List.172 List.633; - jump List.628 List.169 List.174 List.171 List.632 List.173; + let List.677 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.677; + +procedure List.96 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): + joinpoint List.656 List.170 List.171 List.172 List.173 List.174: + let List.658 : Int1 = CallByName Num.22 List.173 List.174; + if List.658 then + let List.662 : Str = CallByName List.66 List.170 List.173; + inc List.662; + let List.175 : List U8 = CallByName Test.66 List.171 List.662 List.172; + let List.661 : U64 = 1i64; + let List.660 : U64 = CallByName Num.51 List.173 List.661; + jump List.656 List.170 List.175 List.172 List.660 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.35; - jump List.628 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39; - -procedure List.95 (#Derived_gen.49, #Derived_gen.50, #Derived_gen.51, #Derived_gen.52, #Derived_gen.53): - joinpoint List.655 List.169 List.170 List.171 List.172 List.173: - let List.657 : Int1 = CallByName Num.22 List.172 List.173; - if List.657 then - let List.661 : Str = CallByName List.66 List.169 List.172; - inc List.661; - let List.174 : List U8 = CallByName Test.66 List.170 List.661 List.171; - let List.660 : U64 = 1i64; - let List.659 : U64 = CallByName Num.51 List.172 List.660; - jump List.655 List.169 List.174 List.171 List.659 List.173; + inc #Derived_gen.23; + jump List.656 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; + +procedure List.96 (#Derived_gen.46, #Derived_gen.47, #Derived_gen.48, #Derived_gen.49, #Derived_gen.50): + joinpoint List.629 List.170 List.171 List.172 List.173 List.174: + let List.631 : Int1 = CallByName Num.22 List.173 List.174; + if List.631 then + let List.635 : [C {}, C {}, C Str] = CallByName List.66 List.170 List.173; + inc List.635; + let List.175 : List U8 = CallByName Test.66 List.171 List.635 List.172; + let List.634 : U64 = 1i64; + let List.633 : U64 = CallByName Num.51 List.173 List.634; + jump List.629 List.170 List.175 List.172 List.633 List.174; else - dec List.169; - ret List.170; + dec List.170; + ret List.171; in - inc #Derived_gen.49; - jump List.655 #Derived_gen.49 #Derived_gen.50 #Derived_gen.51 #Derived_gen.52 #Derived_gen.53; + inc #Derived_gen.46; + jump List.629 #Derived_gen.46 #Derived_gen.47 #Derived_gen.48 #Derived_gen.49 #Derived_gen.50; procedure Num.127 (#Attr.2): let Num.286 : U8 = lowlevel NumIntCast #Attr.2; diff --git a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt index b6a1673dcee..ffefb6edcc8 100644 --- a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt +++ b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt @@ -2,81 +2,81 @@ procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; -procedure List.110 (List.539, List.540, List.541): - let List.643 : U64 = 0i64; - let List.644 : U64 = CallByName List.6 List.539; - let List.642 : [C U64, C U64] = CallByName List.80 List.539 List.540 List.541 List.643 List.644; - ret List.642; +procedure List.111 (List.540, List.541, List.542): + let List.644 : U64 = 0i64; + let List.645 : U64 = CallByName List.6 List.540; + let List.643 : [C U64, C U64] = CallByName List.80 List.540 List.541 List.542 List.644 List.645; + ret List.643; -procedure List.26 (List.207, List.208, List.209): - let List.636 : [C U64, C U64] = CallByName List.110 List.207 List.208 List.209; - let List.639 : U8 = 1i64; - let List.640 : U8 = GetTagId List.636; - let List.641 : Int1 = lowlevel Eq List.639 List.640; - if List.641 then - let List.210 : U64 = UnionAtIndex (Id 1) (Index 0) List.636; - ret List.210; - else - let List.211 : U64 = UnionAtIndex (Id 0) (Index 0) List.636; +procedure List.26 (List.208, List.209, List.210): + let List.637 : [C U64, C U64] = CallByName List.111 List.208 List.209 List.210; + let List.640 : U8 = 1i64; + let List.641 : U8 = GetTagId List.637; + let List.642 : Int1 = lowlevel Eq List.640 List.641; + if List.642 then + let List.211 : U64 = UnionAtIndex (Id 1) (Index 0) List.637; ret List.211; + else + let List.212 : U64 = UnionAtIndex (Id 0) (Index 0) List.637; + ret List.212; -procedure List.38 (List.395, List.396): - let List.635 : U64 = CallByName List.6 List.395; - let List.397 : U64 = CallByName Num.77 List.635 List.396; - let List.625 : List U8 = CallByName List.43 List.395 List.397; - ret List.625; - -procedure List.43 (List.393, List.394): - let List.633 : U64 = CallByName List.6 List.393; - let List.632 : U64 = CallByName Num.77 List.633 List.394; - let List.627 : {U64, U64} = Struct {List.394, List.632}; - let List.626 : List U8 = CallByName List.49 List.393 List.627; +procedure List.38 (List.396, List.397): + let List.636 : U64 = CallByName List.6 List.396; + let List.398 : U64 = CallByName Num.77 List.636 List.397; + let List.626 : List U8 = CallByName List.43 List.396 List.398; ret List.626; -procedure List.49 (List.471, List.472): - let List.629 : U64 = StructAtIndex 1 List.472; - let List.630 : U64 = StructAtIndex 0 List.472; - let List.628 : List U8 = CallByName List.72 List.471 List.629 List.630; - ret List.628; +procedure List.43 (List.394, List.395): + let List.634 : U64 = CallByName List.6 List.394; + let List.633 : U64 = CallByName Num.77 List.634 List.395; + let List.628 : {U64, U64} = Struct {List.395, List.633}; + let List.627 : List U8 = CallByName List.49 List.394 List.628; + ret List.627; + +procedure List.49 (List.472, List.473): + let List.630 : U64 = StructAtIndex 1 List.473; + let List.631 : U64 = StructAtIndex 0 List.473; + let List.629 : List U8 = CallByName List.72 List.472 List.630 List.631; + ret List.629; procedure List.6 (#Attr.2): - let List.634 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.634; + let List.635 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.635; procedure List.66 (#Attr.2, #Attr.3): - let List.657 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.657; + let List.658 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.658; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.631 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.631; + let List.632 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.632; procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.645 List.542 List.543 List.544 List.545 List.546: - let List.647 : Int1 = CallByName Num.22 List.545 List.546; - if List.647 then - let List.656 : U8 = CallByName List.66 List.542 List.545; - let List.648 : [C U64, C U64] = CallByName Test.3 List.543 List.656; - let List.653 : U8 = 1i64; - let List.654 : U8 = GetTagId List.648; - let List.655 : Int1 = lowlevel Eq List.653 List.654; - if List.655 then - let List.547 : U64 = UnionAtIndex (Id 1) (Index 0) List.648; - let List.651 : U64 = 1i64; - let List.650 : U64 = CallByName Num.51 List.545 List.651; - jump List.645 List.542 List.547 List.544 List.650 List.546; + joinpoint List.646 List.543 List.544 List.545 List.546 List.547: + let List.648 : Int1 = CallByName Num.22 List.546 List.547; + if List.648 then + let List.657 : U8 = CallByName List.66 List.543 List.546; + let List.649 : [C U64, C U64] = CallByName Test.3 List.544 List.657; + let List.654 : U8 = 1i64; + let List.655 : U8 = GetTagId List.649; + let List.656 : Int1 = lowlevel Eq List.654 List.655; + if List.656 then + let List.548 : U64 = UnionAtIndex (Id 1) (Index 0) List.649; + let List.652 : U64 = 1i64; + let List.651 : U64 = CallByName Num.51 List.546 List.652; + jump List.646 List.543 List.548 List.545 List.651 List.547; else - dec List.542; - let List.548 : U64 = UnionAtIndex (Id 0) (Index 0) List.648; - let List.652 : [C U64, C U64] = TagId(0) List.548; - ret List.652; + dec List.543; + let List.549 : U64 = UnionAtIndex (Id 0) (Index 0) List.649; + let List.653 : [C U64, C U64] = TagId(0) List.549; + ret List.653; else - dec List.542; - let List.646 : [C U64, C U64] = TagId(1) List.543; - ret List.646; + dec List.543; + let List.647 : [C U64, C U64] = TagId(1) List.544; + ret List.647; in inc #Derived_gen.0; - jump List.645 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.646 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; From de2260e67a4526b72cc76adebd1acbecdc2bc6a9 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 24 Oct 2024 12:48:47 -0300 Subject: [PATCH 57/73] Add simple effectful cli run tests --- crates/cli/tests/cli_run.rs | 90 +++++++++++++++++++ crates/cli/tests/effectful/form.roc | 27 ++++++ crates/cli/tests/effectful/hello.roc | 7 ++ crates/cli/tests/effectful/loops.roc | 16 ++++ .../cli/tests/effectful/untyped_passed_fx.roc | 1 - 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 crates/cli/tests/effectful/form.roc create mode 100644 crates/cli/tests/effectful/hello.roc create mode 100644 crates/cli/tests/effectful/loops.roc diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index e82f0718dc2..d6fc773326f 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -986,6 +986,77 @@ mod cli_run { ); } + #[test] + #[cfg_attr(windows, ignore)] + fn effectful_hello() { + test_roc_app( + "crates/cli/tests/effectful", + "hello.roc", + &[], + &[], + &[], + indoc!( + r#" + I'm an effect 👻 + "# + ), + UseValgrind::No, + TestCliCommands::Dev, + ); + } + + #[test] + #[cfg_attr(windows, ignore)] + fn effectful_form() { + test_roc_app( + "crates/cli/tests/effectful", + "form.roc", + &["Agus\n", "Zubiaga\n", "27\n"], + &[], + &[], + indoc!( + r#" + What's your first name? + What's your last name? + + Hi, Agus Zubiaga! + + How old are you? + + Nice! You can vote! + + Bye! 👋 + "# + ), + UseValgrind::No, + TestCliCommands::Dev, + ); + } + + #[test] + #[cfg_attr(windows, ignore)] + fn effectful_loops() { + test_roc_app( + "crates/cli/tests/effectful", + "loops.roc", + &[], + &[], + &[], + indoc!( + r#" + Lu + Marce + Joaquin + Chloé + Mati + Pedro + "# + ), + UseValgrind::No, + TestCliCommands::Dev, + ); + } + #[test] #[cfg_attr(windows, ignore)] fn effectful_untyped_passed_fx() { @@ -1007,6 +1078,25 @@ mod cli_run { ); } + #[test] + #[cfg_attr(windows, ignore)] + fn effectful_ignore_result() { + test_roc_app( + "crates/cli/tests/effectful", + "ignore_result.roc", + &[], + &[], + &[], + indoc!( + r#" + I asked for input and I ignored it. Deal with it! 😎 + "# + ), + UseValgrind::No, + TestCliCommands::Dev, + ); + } + #[test] #[cfg_attr(windows, ignore)] fn transitive_expects() { diff --git a/crates/cli/tests/effectful/form.roc b/crates/cli/tests/effectful/form.roc new file mode 100644 index 00000000000..6882f2178f4 --- /dev/null +++ b/crates/cli/tests/effectful/form.roc @@ -0,0 +1,27 @@ +app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" } + +import pf.Effect + +main! : {} => {} +main! = \{} -> + first = ask! "What's your first name?" + last = ask! "What's your last name?" + + Effect.putLine! "\nHi, $(first) $(last)!\n" + + when Str.toU8 (ask! "How old are you?") is + Err InvalidNumStr -> + Effect.putLine! "Enter a valid number" + + Ok age if age >= 18 -> + Effect.putLine! "\nNice! You can vote!" + + Ok age -> + Effect.putLine! "\nYou'll be able to vote in $(Num.toStr (18 - age)) years" + + Effect.putLine! "\nBye! 👋" + +ask! : Str => Str +ask! = \question -> + Effect.putLine! question + Effect.getLine! {} diff --git a/crates/cli/tests/effectful/hello.roc b/crates/cli/tests/effectful/hello.roc new file mode 100644 index 00000000000..8e56a52dc63 --- /dev/null +++ b/crates/cli/tests/effectful/hello.roc @@ -0,0 +1,7 @@ +app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" } + +import pf.Effect + +main! : {} => {} +main! = \{} -> + Effect.putLine! "I'm an effect 👻" diff --git a/crates/cli/tests/effectful/loops.roc b/crates/cli/tests/effectful/loops.roc new file mode 100644 index 00000000000..bde0599d976 --- /dev/null +++ b/crates/cli/tests/effectful/loops.roc @@ -0,0 +1,16 @@ +app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" } + +import pf.Effect + +main! : {} => {} +main! = \{} -> + friends = ["Lu", "Marce", "Joaquin", "Chloé", "Mati", "Pedro"] + printAll! friends + +printAll! : List Str => {} +printAll! = \friends -> + when friends is + [] -> {} + [first, .. as remaining] -> + Effect.putLine! first + printAll! remaining diff --git a/crates/cli/tests/effectful/untyped_passed_fx.roc b/crates/cli/tests/effectful/untyped_passed_fx.roc index d0021f41a6f..28f567176a3 100644 --- a/crates/cli/tests/effectful/untyped_passed_fx.roc +++ b/crates/cli/tests/effectful/untyped_passed_fx.roc @@ -6,7 +6,6 @@ main! : {} => {} main! = \{} -> logged! "hello" (\{} -> Effect.putLine! "Hello, World!") - logged! = \name, fx! -> Effect.putLine! "Before $(name)" fx! {} From be0afbce2514a25ac02bc098506af4a136ff62ca Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 30 Oct 2024 19:59:34 -0300 Subject: [PATCH 58/73] update ui test --- .../generalization_among_large_recursive_group.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/compiler/uitest/tests/recursion/generalization_among_large_recursive_group.txt b/crates/compiler/uitest/tests/recursion/generalization_among_large_recursive_group.txt index c446d872225..cb79cd47420 100644 --- a/crates/compiler/uitest/tests/recursion/generalization_among_large_recursive_group.txt +++ b/crates/compiler/uitest/tests/recursion/generalization_among_large_recursive_group.txt @@ -3,22 +3,22 @@ app "test" provides [main] to "./platform" f = \{} -> -#^{-1} <2897><114>{} -<117>[[f(1)]]-> <113>[Ok <2905>{}]<77>* +#^{-1} <3219><120>{} -<123>[[f(1)]]<125>-> <119>[Ok <3228>{}]<80>* when g {} is -# ^ <2887><2905>{} -<2895>[[g(2)]]-> <69>[Ok <2905>{}]<99>* +# ^ <3209><3228>{} -<3217>[[g(2)]]<125>-> <71>[Ok <3228>{}]<104>* _ -> Ok {} g = \{} -> -#^{-1} <2887><2905>{} -<2895>[[g(2)]]-> <69>[Ok <2905>{}]<99>* +#^{-1} <3209><3228>{} -<3217>[[g(2)]]<125>-> <71>[Ok <3228>{}]<104>* when h {} is -# ^ <2892><2905>{} -<2900>[[h(3)]]-> <91>[Ok <2905>{}]<121>* +# ^ <3214><3228>{} -<3222>[[h(3)]]<125>-> <95>[Ok <3228>{}]<128>* _ -> Ok {} h = \{} -> -#^{-1} <2892><2905>{} -<2900>[[h(3)]]-> <91>[Ok <2905>{}]<121>* +#^{-1} <3214><3228>{} -<3222>[[h(3)]]<125>-> <95>[Ok <3228>{}]<128>* when f {} is -# ^ <2897><114>{} -<117>[[f(1)]]-> <113>[Ok <2905>{}]<77>* +# ^ <3219><120>{} -<123>[[f(1)]]<125>-> <119>[Ok <3228>{}]<80>* _ -> Ok {} main = f {} -# ^ <2907><130>{} -<133>[[f(1)]]-> <129>[Ok <2905>{}]<2906>w_a +# ^ <3230><138>{} -<141>[[f(1)]]<143>-> <137>[Ok <3228>{}]<3229>w_a From e3c6b756d3dcdb369063faf9c7a1ab8c49f67c20 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 24 Oct 2024 20:28:43 -0300 Subject: [PATCH 59/73] Make sure to drop suffix from symbols exposed to the host --- crates/compiler/gen_llvm/src/llvm/build.rs | 6 +++--- crates/compiler/module/src/symbol.rs | 4 ++++ crates/compiler/mono/src/layout.rs | 6 +++--- crates/linker/src/lib.rs | 21 ++++++++++++--------- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index c9f4359e65c..3154e910c7e 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -4324,7 +4324,7 @@ fn expose_function_to_host<'a, 'ctx>( return_layout: InLayout<'a>, layout_ids: &mut LayoutIds<'a>, ) { - let ident_string = symbol.as_str(&env.interns); + let ident_string = symbol.as_unsuffixed_str(&env.interns); let proc_layout = ProcLayout { arguments, @@ -5564,7 +5564,7 @@ pub fn build_procedures<'a>( let getter_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol); let name = getter_fn.get_name().to_str().unwrap(); - let getter_name = symbol.as_str(&env.interns); + let getter_name = symbol.as_unsuffixed_str(&env.interns); // Add the getter function to the module. let _ = expose_function_to_host_help_c_abi( @@ -5830,7 +5830,7 @@ fn build_procedures_help<'a>( GenTest | WasmGenTest | CliTest => { /* no host, or exposing types is not supported */ } Binary | BinaryDev | BinaryGlue => { for (proc_name, alias_name, hels) in host_exposed_lambda_sets.iter() { - let ident_string = proc_name.name().as_str(&env.interns); + let ident_string = proc_name.name().as_unsuffixed_str(&env.interns); let fn_name: String = format!("{}_{}", ident_string, hels.id.0); expose_alias_to_host( diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index d14e549109f..c94da9f0ea1 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -156,6 +156,10 @@ impl Symbol { }) } + pub fn as_unsuffixed_str(self, interns: &Interns) -> &str { + self.as_str(interns).trim_end_matches('!') + } + pub const fn as_u64(self) -> u64 { u64::from_ne_bytes(self.to_ne_bytes()) } diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 038167cc9d1..c76350f59d3 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -4648,7 +4648,7 @@ impl LayoutId { // Returns something like "#UserApp_foo_1" when given a symbol that interns to "foo" // and a LayoutId of 1. pub fn to_symbol_string(self, symbol: Symbol, interns: &Interns) -> String { - let ident_string = symbol.as_str(interns).trim_end_matches('!'); + let ident_string = symbol.as_unsuffixed_str(interns); let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap(); format!("{}_{}_{}", module_string, ident_string, self.0) } @@ -4656,12 +4656,12 @@ impl LayoutId { // Returns something like "roc__foo_1_exposed" when given a symbol that interns to "foo" // and a LayoutId of 1. pub fn to_exposed_symbol_string(self, symbol: Symbol, interns: &Interns) -> String { - let ident_string = symbol.as_str(interns).trim_end_matches('!'); + let ident_string = symbol.as_unsuffixed_str(interns); format!("roc__{}_{}_exposed", ident_string, self.0) } pub fn to_exposed_generic_symbol_string(self, symbol: Symbol, interns: &Interns) -> String { - let ident_string = symbol.as_str(interns).trim_end_matches('!'); + let ident_string = symbol.as_unsuffixed_str(interns); format!("roc__{}_{}_exposed_generic", ident_string, self.0) } } diff --git a/crates/linker/src/lib.rs b/crates/linker/src/lib.rs index 0906be4118d..210cfa8f2a2 100644 --- a/crates/linker/src/lib.rs +++ b/crates/linker/src/lib.rs @@ -97,7 +97,7 @@ pub fn generate_stub_lib( .exposed_to_host .top_level_values .keys() - .map(|x| x.as_str(&loaded.interns).to_string()) + .map(|x| x.as_unsuffixed_str(&loaded.interns).to_string()) .collect(); let exported_closure_types = loaded @@ -108,7 +108,7 @@ pub fn generate_stub_lib( format!( "{}_{}", x.module_string(&loaded.interns), - x.as_str(&loaded.interns) + x.as_unsuffixed_str(&loaded.interns) ) }) .collect(); @@ -162,7 +162,7 @@ impl ExposedSymbols { let mut custom_names = Vec::new(); for x in exposed_to_host.top_level_values.keys() { - let sym = x.as_str(interns); + let sym = x.as_unsuffixed_str(interns); custom_names.extend([ format!("roc__{sym}_1_exposed"), @@ -170,10 +170,13 @@ impl ExposedSymbols { format!("roc__{sym}_1_exposed_size"), ]); - let exported_closure_types = exposed_to_host - .closure_types - .iter() - .map(|x| format!("{}_{}", x.module_string(interns), x.as_str(interns))); + let exported_closure_types = exposed_to_host.closure_types.iter().map(|x| { + format!( + "{}_{}", + x.module_string(interns), + x.as_unsuffixed_str(interns) + ) + }); for (i, _) in exported_closure_types.enumerate() { custom_names.extend([ @@ -185,7 +188,7 @@ impl ExposedSymbols { } for x in &exposed_to_host.getters { - let sym = x.as_str(interns); + let sym = x.as_unsuffixed_str(interns); custom_names.extend([ sym.to_string(), format!("{sym}_generic"), @@ -194,7 +197,7 @@ impl ExposedSymbols { } for (top_level_value, lambda_set_id) in &exposed_to_host.lambda_sets { - let sym = top_level_value.as_str(interns); + let sym = top_level_value.as_unsuffixed_str(interns); let id = lambda_set_id.0; custom_names.extend([format!("roc__{sym}_{id}_caller")]); } From 2e5c143a0b82527456f8fd4ca73b23352e0e066e Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sun, 27 Oct 2024 14:27:17 -0300 Subject: [PATCH 60/73] Explicit message for StmtAfterExpr in desugar --- crates/compiler/can/src/desugar.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index d872e496358..ec18674c528 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -200,7 +200,9 @@ fn desugar_value_def<'a>( } IngestedFileImport(_) => *def, - StmtAfterExpr => internal_error!("unexpected StmtAfterExpr"), + StmtAfterExpr => internal_error!( + "StmtAfterExpression is only created during desugaring, so it shouldn't exist here." + ), Stmt(stmt_expr) => { if env.fx_mode == FxMode::PurityInference { From b31b30c4685e65b3778581a8cbfb7e21814c5f23 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sun, 27 Oct 2024 14:28:04 -0300 Subject: [PATCH 61/73] Print fx_suffix_constraints in Debug impl for Constraints --- crates/compiler/can/src/constraint.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index ac1304138b5..5b51cfccbf3 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -53,6 +53,7 @@ impl std::fmt::Debug for Constraints { .field("pattern_eq", &self.pattern_eq) .field("cycles", &self.cycles) .field("fx_call_constraints", &self.fx_call_constraints) + .field("fx_suffix_constraints", &self.fx_suffix_constraints) .finish() } } From dae10d10fb37f8fd61decb1f9c48dbf93dbaf252 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sun, 27 Oct 2024 14:29:36 -0300 Subject: [PATCH 62/73] Do not alias ClosureData.fx_type in pattern matches --- crates/compiler/can/src/copy.rs | 4 ++-- crates/compiler/can/src/expr.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 860d1d8e4a3..52cadf15030 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -465,7 +465,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr function_type, closure_type, return_type, - fx_type: effect_type, + fx_type, early_returns, name, captured_symbols, @@ -476,7 +476,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr function_type: sub!(*function_type), closure_type: sub!(*closure_type), return_type: sub!(*return_type), - fx_type: sub!(*effect_type), + fx_type: sub!(*fx_type), early_returns: early_returns .iter() .map(|(var, region)| (sub!(*var), *region)) diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 3debaeb0862..1e619cc503c 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -2325,7 +2325,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { function_type, closure_type, return_type, - fx_type: effect_type, + fx_type, early_returns, recursive, name, @@ -2343,7 +2343,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { function_type, closure_type, return_type, - fx_type: effect_type, + fx_type, early_returns, recursive, name, From 46e808dc3af8ddc9dec659de1d11fd7fe608a0ee Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sun, 27 Oct 2024 14:33:37 -0300 Subject: [PATCH 63/73] Refactor if-let to let-else-continue --- crates/compiler/can/src/def.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index eeea665346c..8d3754a092b 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -1226,25 +1226,27 @@ fn canonicalize_value_defs<'a>( let mut symbol_to_index: Vec<(IdentId, u32)> = Vec::with_capacity(pending_value_defs.len()); for (def_index, pending_def) in pending_value_defs.iter().enumerate() { - if let Some(loc_pattern) = pending_def.loc_pattern() { - let new_bindings = BindingsFromPattern::new(loc_pattern).peekable(); + let Some(loc_pattern) = pending_def.loc_pattern() else { + continue; + }; - for (s, r) in new_bindings { - // store the top-level defs, used to ensure that closures won't capture them - if let PatternType::TopLevelDef = pattern_type { - env.top_level_symbols.insert(s); - } + let new_bindings = BindingsFromPattern::new(loc_pattern).peekable(); - symbols_introduced.insert(s, r); + for (s, r) in new_bindings { + // store the top-level defs, used to ensure that closures won't capture them + if let PatternType::TopLevelDef = pattern_type { + env.top_level_symbols.insert(s); + } - debug_assert_eq!(env.home, s.module_id()); - debug_assert!( - !symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()), - "{s:?}" - ); + symbols_introduced.insert(s, r); - symbol_to_index.push((s.ident_id(), def_index as u32)); - } + debug_assert_eq!(env.home, s.module_id()); + debug_assert!( + !symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()), + "{s:?}" + ); + + symbol_to_index.push((s.ident_id(), def_index as u32)); } } From a0f4b38ee9998e92403e52689958ff746d22713d Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 28 Oct 2024 09:00:39 -0300 Subject: [PATCH 64/73] Update region when desugaring ! in Task mode --- crates/compiler/can/src/desugar.rs | 2 +- ...ffixed_tests__apply_argument_multiple.snap | 12 ++++---- ...suffixed_tests__apply_argument_single.snap | 6 ++-- ...ed__suffixed_tests__bang_in_pipe_root.snap | 6 ++-- .../test_suffixed__suffixed_tests__basic.snap | 14 ++++----- ...fixed__suffixed_tests__closure_simple.snap | 16 +++++----- ..._suffixed__suffixed_tests__dbg_simple.snap | 8 ++--- ...uffixed__suffixed_tests__dbg_stmt_arg.snap | 12 ++++---- ...t_suffixed__suffixed_tests__deep_when.snap | 2 +- ..._suffixed_tests__defs_suffixed_middle.snap | 2 +- ...ixed__suffixed_tests__deps_final_expr.snap | 6 ++-- ...xed__suffixed_tests__expect_then_bang.snap | 2 +- ...t_suffixed__suffixed_tests__if_simple.snap | 24 +++++++-------- ...sts__last_stmt_not_top_level_suffixed.snap | 6 ++-- ...uffixed_tests__last_suffixed_multiple.snap | 30 +++++++++---------- ...xed__suffixed_tests__multi_defs_stmts.snap | 10 +++---- ...suffixed__suffixed_tests__nested_defs.snap | 8 ++--- ...uffixed__suffixed_tests__simple_pizza.snap | 16 +++++----- ...ed_tests__trailing_suffix_inside_when.snap | 8 ++--- ...ixed__suffixed_tests__type_annotation.snap | 14 ++++----- ...uffixed__suffixed_tests__var_suffixes.snap | 14 ++++----- ...ffixed__suffixed_tests__when_branches.snap | 6 ++-- ...suffixed__suffixed_tests__when_simple.snap | 6 ++-- 23 files changed, 115 insertions(+), 115 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index ec18674c528..c28d3f7682b 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -421,7 +421,7 @@ pub fn desugar_expr<'a>( Var { module_name, ident } => { if env.fx_mode == FxMode::Task && ident.ends_with('!') { env.arena.alloc(Loc::at( - loc_expr.region, + Region::new(loc_expr.region.start(), loc_expr.region.end().sub(1)), TrySuffix { expr: env.arena.alloc(Var { module_name, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap index fa9b21ad4b7..29b6f33f3ae 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap @@ -30,13 +30,13 @@ Defs { ident: "await", }, [ - @17-19 Var { + @17-18 Var { module_name: "", ident: "a", }, @15-22 Closure( [ - @17-19 Identifier { + @17-18 Identifier { ident: "#!0_arg", }, ], @@ -46,13 +46,13 @@ Defs { ident: "await", }, [ - @20-22 Var { + @20-21 Var { module_name: "", ident: "x", }, @15-22 Closure( [ - @20-22 Identifier { + @20-21 Identifier { ident: "#!1_arg", }, ], @@ -83,11 +83,11 @@ Defs { ident: "b", }, [ - @17-19 Var { + @17-18 Var { module_name: "", ident: "#!0_arg", }, - @20-22 Var { + @20-21 Var { module_name: "", ident: "#!1_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap index 144d413c178..63b7cf74b30 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap @@ -30,13 +30,13 @@ Defs { ident: "await", }, [ - @17-19 Var { + @17-18 Var { module_name: "", ident: "a", }, @15-19 Closure( [ - @17-19 Identifier { + @17-18 Identifier { ident: "#!0_arg", }, ], @@ -67,7 +67,7 @@ Defs { ident: "b", }, [ - @17-19 Var { + @17-18 Var { module_name: "", ident: "#!0_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap index 38ecebcfd4d..5f52ead8655 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap @@ -30,13 +30,13 @@ Defs { ident: "await", }, [ - @15-17 Var { + @15-16 Var { module_name: "", ident: "a", }, @15-22 Closure( [ - @15-17 Identifier { + @15-16 Identifier { ident: "#!0_arg", }, ], @@ -67,7 +67,7 @@ Defs { ident: "b", }, [ - @15-17 Var { + @15-16 Var { module_name: "", ident: "#!0_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index cc35ebc4a18..ceecd204822 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -24,19 +24,19 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-15 Apply( - @11-15 Var { + @11-14 Apply( + @11-14 Var { module_name: "Task", ident: "await", }, [ - @11-15 Defs( + @11-14 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @11-15, + @11-14, ], space_before: [ Slice { start: 0, length: 0 }, @@ -66,19 +66,19 @@ Defs { body_pattern: @11-15 Identifier { ident: "#!0_stmt", }, - body_expr: @11-15 Var { + body_expr: @11-14 Var { module_name: "", ident: "foo", }, }, ], }, - @11-15 Var { + @11-14 Var { module_name: "", ident: "#!0_stmt", }, ), - @11-15 Closure( + @11-14 Closure( [ @11-15 Underscore( "#!stmt", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index 32090d76640..dce9c0f3f12 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -51,19 +51,19 @@ Defs { ident: "msg", }, ], - @31-43 Apply( - @31-43 Var { + @31-42 Apply( + @31-42 Var { module_name: "Task", ident: "await", }, [ - @31-43 Defs( + @31-42 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @31-43, + @31-42, ], space_before: [ Slice { start: 0, length: 0 }, @@ -93,8 +93,8 @@ Defs { body_pattern: @31-43 Identifier { ident: "#!0_stmt", }, - body_expr: @31-43 Apply( - @31-43 Var { + body_expr: @31-42 Apply( + @31-42 Var { module_name: "", ident: "line", }, @@ -111,12 +111,12 @@ Defs { }, ], }, - @31-43 Var { + @31-42 Var { module_name: "", ident: "#!0_stmt", }, ), - @31-43 Closure( + @31-42 Closure( [ @31-43 Underscore( "#!stmt", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap index ded7d6c24ef..c983f084a24 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap @@ -24,17 +24,17 @@ Defs { @0-4 Identifier { ident: "main", }, - @17-24 Apply( - @17-24 Var { + @17-23 Apply( + @17-23 Var { module_name: "Task", ident: "await", }, [ - @17-24 Var { + @17-23 Var { module_name: "", ident: "getFoo", }, - @17-24 Closure( + @17-23 Closure( [ @11-14 Identifier { ident: "foo", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap index 1140e8f1dc6..4b96a43719c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap @@ -30,28 +30,28 @@ Defs { ident: "await", }, [ - @15-17 Var { + @15-16 Var { module_name: "", ident: "a", }, @11-17 Closure( [ - @15-17 Identifier { + @15-16 Identifier { ident: "#!0_arg", }, ], @11-17 LowLevelDbg( ( "test.roc:2", - "in", + "i", ), - @15-17 Apply( - @15-17 Var { + @15-16 Apply( + @15-16 Var { module_name: "Inspect", ident: "toStr", }, [ - @15-17 Var { + @15-16 Var { module_name: "", ident: "#!0_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap index 7cf55d80bab..7c28faa1637 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap @@ -48,7 +48,7 @@ Defs { "1", ), ], - value: @97-99 Var { + value: @97-98 Var { module_name: "", ident: "c", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index ce25f53121a..97bba1ffc9f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -128,7 +128,7 @@ Defs { "#!stmt", ), ], - @45-54 Var { + @45-53 Var { module_name: "", ident: "printBar", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap index 52b2c8abf9b..5b864ea68fa 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap @@ -70,13 +70,13 @@ Defs { module_name: "", ident: "a", }, - @92-94 Var { + @92-93 Var { module_name: "", ident: "b", }, ), ], - final_else: @128-130 Var { + final_else: @128-129 Var { module_name: "", ident: "c", }, @@ -91,7 +91,7 @@ Defs { "B", ), ], - value: @156-158 Var { + value: @156-157 Var { module_name: "", ident: "d", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap index 3b6a49b0a51..55ff4499790 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap @@ -63,7 +63,7 @@ Defs { }, ], }, - @29-31 Var { + @29-30 Var { module_name: "", ident: "x", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap index 5c3fc0381e6..616b0f261c8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap @@ -85,26 +85,26 @@ Defs { ), ], }, - @79-87 Apply( - @79-87 Var { + @79-86 Apply( + @79-86 Var { module_name: "Task", ident: "await", }, [ - @79-87 Var { + @79-86 Var { module_name: "", ident: "isFalse", }, - @79-87 Closure( + @79-86 Closure( [ - @79-87 Identifier { + @79-86 Identifier { ident: "#!0_arg", }, ], @76-189 If { if_thens: [ ( - @79-87 Var { + @79-86 Var { module_name: "", ident: "#!0_arg", }, @@ -124,26 +124,26 @@ Defs { ), ), ], - final_else: @125-132 Apply( - @125-132 Var { + final_else: @125-131 Apply( + @125-131 Var { module_name: "Task", ident: "await", }, [ - @125-132 Var { + @125-131 Var { module_name: "", ident: "isTrue", }, - @125-132 Closure( + @125-131 Closure( [ - @125-132 Identifier { + @125-131 Identifier { ident: "#!1_arg", }, ], @76-189 If { if_thens: [ ( - @125-132 Var { + @125-131 Var { module_name: "", ident: "#!1_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap index 5f4bd8be7f5..aa775291e93 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap @@ -57,13 +57,13 @@ Defs { ident: "await", }, [ - @24-26 Var { + @24-25 Var { module_name: "", ident: "b", }, @11-26 Closure( [ - @24-26 Identifier { + @24-25 Identifier { ident: "#!0_arg", }, ], @@ -73,7 +73,7 @@ Defs { ident: "a", }, [ - @24-26 Var { + @24-25 Var { module_name: "", ident: "#!0_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index 3f995ca4ae7..86f8ef922c4 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -24,19 +24,19 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-15 Apply( - @11-15 Var { + @11-14 Apply( + @11-14 Var { module_name: "Task", ident: "await", }, [ - @11-15 Defs( + @11-14 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @11-15, + @11-14, ], space_before: [ Slice { start: 0, length: 0 }, @@ -66,37 +66,37 @@ Defs { body_pattern: @11-15 Identifier { ident: "#!2_stmt", }, - body_expr: @11-15 Var { + body_expr: @11-14 Var { module_name: "", ident: "foo", }, }, ], }, - @11-15 Var { + @11-14 Var { module_name: "", ident: "#!2_stmt", }, ), - @11-15 Closure( + @11-14 Closure( [ @11-15 Underscore( "#!stmt", ), ], - @20-24 Apply( - @20-24 Var { + @20-23 Apply( + @20-23 Var { module_name: "Task", ident: "await", }, [ - @20-24 Defs( + @20-23 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @20-24, + @20-23, ], space_before: [ Slice { start: 0, length: 0 }, @@ -126,25 +126,25 @@ Defs { body_pattern: @20-24 Identifier { ident: "#!1_stmt", }, - body_expr: @20-24 Var { + body_expr: @20-23 Var { module_name: "", ident: "bar", }, }, ], }, - @20-24 Var { + @20-23 Var { module_name: "", ident: "#!1_stmt", }, ), - @20-24 Closure( + @20-23 Closure( [ @20-24 Underscore( "#!stmt", ), ], - @29-33 Var { + @29-32 Var { module_name: "", ident: "baz", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index dd46fbbf6c4..8e98fb84a8d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -94,14 +94,14 @@ Defs { "#!stmt", ), ], - @33-56 Apply( - @33-56 Var { + @33-55 Apply( + @33-55 Var { module_name: "Task", ident: "await", }, [ - @33-56 Apply( - @33-56 Var { + @33-55 Apply( + @33-55 Var { module_name: "Stdout", ident: "line", }, @@ -116,7 +116,7 @@ Defs { Pizza, ), ), - @33-56 Closure( + @33-55 Closure( [ @28-30 RecordDestructure( [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap index db5d8e4578a..3e932186681 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap @@ -45,17 +45,17 @@ Defs { @11-12 Identifier { ident: "x", }, - @27-29 Apply( - @27-29 Var { + @27-28 Apply( + @27-28 Var { module_name: "Task", ident: "await", }, [ - @27-29 Var { + @27-28 Var { module_name: "", ident: "b", }, - @27-29 Closure( + @27-28 Closure( [ @23-24 Identifier { ident: "a", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 3a33e1d5d6a..1d5573d9e00 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -24,19 +24,19 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-57 Apply( - @11-57 Var { + @11-56 Apply( + @11-56 Var { module_name: "Task", ident: "await", }, [ - @11-57 Defs( + @11-56 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @11-57, + @11-56, ], space_before: [ Slice { start: 0, length: 0 }, @@ -66,8 +66,8 @@ Defs { body_pattern: @11-57 Identifier { ident: "#!0_stmt", }, - body_expr: @11-57 Apply( - @11-57 Var { + body_expr: @11-56 Apply( + @11-56 Var { module_name: "", ident: "line", }, @@ -101,12 +101,12 @@ Defs { }, ], }, - @11-57 Var { + @11-56 Var { module_name: "", ident: "#!0_stmt", }, ), - @11-57 Closure( + @11-56 Closure( [ @11-57 Underscore( "#!stmt", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap index 7f75c769b09..2e648dd0fde 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap @@ -24,17 +24,17 @@ Defs { @0-4 Identifier { ident: "main", }, - @20-31 Apply( - @20-31 Var { + @20-30 Apply( + @20-30 Var { module_name: "Task", ident: "await", }, [ - @20-31 Var { + @20-30 Var { module_name: "Stdin", ident: "line", }, - @20-31 Closure( + @20-30 Closure( [ @11-17 Identifier { ident: "result", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap index 52fed59e429..19735343baa 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -30,19 +30,19 @@ Defs { ident: "x", }, ], - @28-30 Apply( - @28-30 Var { + @28-29 Apply( + @28-29 Var { module_name: "Task", ident: "await", }, [ - @14-30 Defs( + @14-29 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @28-30, + @28-29, ], space_before: [ Slice { start: 0, length: 0 }, @@ -73,19 +73,19 @@ Defs { body_pattern: @24-25 Identifier { ident: "#!0_expr", }, - body_expr: @28-30 Var { + body_expr: @28-29 Var { module_name: "", ident: "x", }, }, ], }, - @28-30 Var { + @28-29 Var { module_name: "", ident: "#!0_expr", }, ), - @28-30 Closure( + @28-29 Closure( [ @24-25 Identifier { ident: "r", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap index 350f6498714..115d3078156 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap @@ -24,24 +24,24 @@ Defs { @0-4 Identifier { ident: "main", }, - @15-19 Apply( - @15-19 Var { + @15-18 Apply( + @15-18 Var { module_name: "Task", ident: "await", }, [ - @15-19 Var { + @15-18 Var { module_name: "", ident: "foo", }, - @15-19 Closure( + @15-18 Closure( [ @11-12 Identifier { ident: "a", }, ], - @15-19 Apply( - @15-19 Var { + @15-18 Apply( + @15-18 Var { module_name: "Task", ident: "await", }, @@ -50,7 +50,7 @@ Defs { module_name: "", ident: "bar", }, - @15-19 Closure( + @15-18 Closure( [ @28-33 Identifier { ident: "#!0_arg", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index 66bc423980c..dda750d1dd2 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -30,18 +30,18 @@ Defs { ident: "await", }, [ - @16-24 Var { + @16-23 Var { module_name: "", ident: "getList", }, @11-120 Closure( [ - @16-24 Identifier { + @16-23 Identifier { ident: "#!2_arg", }, ], @11-120 When( - @16-24 Var { + @16-23 Var { module_name: "", ident: "#!2_arg", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap index 8408c5fbacb..fbe8232063e 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap @@ -30,18 +30,18 @@ Defs { ident: "await", }, [ - @16-24 Var { + @16-23 Var { module_name: "", ident: "getList", }, @11-74 Closure( [ - @16-24 Identifier { + @16-23 Identifier { ident: "#!0_arg", }, ], @11-74 When( - @16-24 Var { + @16-23 Var { module_name: "", ident: "#!0_arg", }, From 1a3d8cef78aadb797425559332a263f26b5095b6 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 28 Oct 2024 09:06:48 -0300 Subject: [PATCH 65/73] Return early when encountering `!` in an ident --- crates/compiler/parse/src/ident.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/compiler/parse/src/ident.rs b/crates/compiler/parse/src/ident.rs index 34595ca78e2..dae3f1b015b 100644 --- a/crates/compiler/parse/src/ident.rs +++ b/crates/compiler/parse/src/ident.rs @@ -490,7 +490,14 @@ fn chomp_identifier_chain<'a>( chomped += width; } else if ch == '!' && !first_is_uppercase { chomped += width; - break; + + let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + let ident = Ident::Access { + module_name: "", + parts: arena.alloc([Accessor::RecordField(value)]), + }; + + return Ok((chomped as u32, ident)); } else { // we're done break; From 5848a3e926bf9d71a42779bfa84baa6ca024dda1 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 28 Oct 2024 09:09:14 -0300 Subject: [PATCH 66/73] Do not error when encountering EffectfulFunc in lambda_set_size --- crates/compiler/mono/src/layout.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index c76350f59d3..99a3027da44 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -2192,8 +2192,7 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) { } stack.push((ext.var(), depth_any + 1, depth_lset)); } - FlatType::EffectfulFunc => internal_error!(), - FlatType::EmptyRecord | FlatType::EmptyTagUnion => {} + FlatType::EmptyRecord | FlatType::EmptyTagUnion | FlatType::EffectfulFunc => {} }, Content::FlexVar(_) | Content::RigidVar(_) From 2fdd1ef98f7de405f565422eb72b8abf69eb5b68 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 28 Oct 2024 09:10:55 -0300 Subject: [PATCH 67/73] Add explicit error for EffectfulFunc in layout --- crates/compiler/mono/src/layout.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 99a3027da44..6cdfdb9d018 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -3465,7 +3465,9 @@ fn layout_from_flat_type<'a>( } EmptyTagUnion => cacheable(Ok(Layout::VOID)), EmptyRecord => cacheable(Ok(Layout::UNIT)), - EffectfulFunc => internal_error!(), + EffectfulFunc => { + internal_error!("Cannot create a layout for an unconstrained EffectfulFunc") + } } } From 935d46023601904c93689b76188e0f4ffc157bea Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 30 Oct 2024 10:27:34 -0300 Subject: [PATCH 68/73] Use plural 'effects' in FxInTopLevel error --- crates/compiler/load/tests/test_reporting.rs | 2 +- crates/reporting/src/error/type.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index d3f5d032b9d..390bbb2d7a2 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14880,7 +14880,7 @@ All branches in an `if` must have the same type! Stdout.line! "What's your name?" Stdin.line! {} - This will allow the caller to control when the effect runs. + This will allow the caller to control when the effects run. "### ); diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 8e5941bdf44..e5af7757240 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -358,7 +358,7 @@ pub fn type_problem<'b>( alloc.reflow("If you don't need any arguments, use an empty record:"), ]), alloc.parser_suggestion(" askName! : {} => Str\n askName! = \\{} ->\n Stdout.line! \"What's your name?\"\n Stdin.line! {}"), - alloc.reflow("This will allow the caller to control when the effect runs."), + alloc.reflow("This will allow the caller to control when the effects run."), ]; Some(Report { From 1e835bbb179646b160bd1aebd1801f6ae88fa295 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 30 Oct 2024 10:33:38 -0300 Subject: [PATCH 69/73] Fix 'right-hand side' typo in errors --- crates/compiler/load/tests/test_reporting.rs | 8 ++++---- crates/reporting/src/error/type.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 390bbb2d7a2..649843c703c 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -10838,7 +10838,7 @@ All branches in an `if` must have the same type! Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on - the right-hand-side, consider removing the assignment. + the right-hand side, consider removing the assignment. ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── @@ -10849,7 +10849,7 @@ All branches in an `if` must have the same type! Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on - the right-hand-side, consider removing the assignment. + the right-hand side, consider removing the assignment. ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── @@ -10860,7 +10860,7 @@ All branches in an `if` must have the same type! Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on - the right-hand-side, consider removing the assignment. + the right-hand side, consider removing the assignment. ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── @@ -10871,7 +10871,7 @@ All branches in an `if` must have the same type! Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on - the right-hand-side, consider removing the assignment. + the right-hand side, consider removing the assignment. "### ); diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index e5af7757240..6fcba85617d 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -388,7 +388,7 @@ pub fn type_problem<'b>( let stack = [ alloc.reflow("This assignment doesn't introduce any new variables:"), alloc.region(lines.convert_region(region), severity), - alloc.reflow("Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on the right-hand-side, consider removing the assignment.") + alloc.reflow("Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on the right-hand side, consider removing the assignment.") ]; Some(Report { From a4296ca19d84ee211e3eb0dfa85171b0b552a083 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 30 Oct 2024 10:35:04 -0300 Subject: [PATCH 70/73] Improve wording in pattern suffix errors --- crates/compiler/load/tests/test_reporting.rs | 4 ++-- crates/reporting/src/error/type.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 649843c703c..25f00b5afd8 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -15000,7 +15000,7 @@ All branches in an `if` must have the same type! @r###" ── MISSING EXCLAMATION in /code/proj/Main.roc ────────────────────────────────── - This matches an effectful function, but its name does not indicate so: + This is an effectful function, but its name does not indicate so: 10│ forEach! = \l, f -> ^ @@ -15037,7 +15037,7 @@ All branches in an `if` must have the same type! @r###" ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── - This matches a pure function, but the name suggests otherwise: + This is a pure function, but its name suggests otherwise: 12│ mapOk = \result, fn! -> ^^^ diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 6fcba85617d..e7541dddbf3 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -407,7 +407,7 @@ pub fn type_problem<'b>( FxSuffixKind::Let(_) => alloc .reflow("This function is effectful, but its name does not indicate so:"), FxSuffixKind::Pattern(_) => alloc.reflow( - "This matches an effectful function, but its name does not indicate so:", + "This is an effectful function, but its name does not indicate so:", ), FxSuffixKind::RecordField(_) => { unreachable!() @@ -452,8 +452,9 @@ pub fn type_problem<'b>( FxSuffixKind::Let(_) => { alloc.reflow("This function is pure, but its name suggests otherwise:") } - FxSuffixKind::Pattern(_) => alloc - .reflow("This matches a pure function, but the name suggests otherwise:"), + FxSuffixKind::Pattern(_) => { + alloc.reflow("This is a pure function, but its name suggests otherwise:") + } FxSuffixKind::RecordField(_) => alloc.reflow( "This field's value is a pure function, but its name suggests otherwise:", ), From bc0cfef1285e2bd36ed247b18e9d45a8df986477 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 30 Oct 2024 18:53:41 -0300 Subject: [PATCH 71/73] Restore UNNCESSARY DEFINITION errors for top-level defs Non-top-level defs are already covered --- crates/compiler/can/src/def.rs | 8 +++ crates/compiler/load/tests/test_reporting.rs | 62 +++++++++++++++++++- crates/compiler/problem/src/can.rs | 3 + crates/reporting/src/error/canonicalize.rs | 8 +++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 8d3754a092b..a10fd50dd85 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -1267,6 +1267,14 @@ fn canonicalize_value_defs<'a>( output = temp_output.output; + if let (PatternType::TopLevelDef, DefKind::Ignored(_)) = + (pattern_type, temp_output.def.kind) + { + env.problems.push(Problem::NoIdentifiersIntroduced( + temp_output.def.loc_pattern.region, + )) + } + defs.push(Some(temp_output.def)); def_ordering.insert_symbol_references(def_id as u32, &temp_output.references) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 25f00b5afd8..97d419fb509 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -10185,6 +10185,18 @@ All branches in an `if` must have the same type! Since these variables have the same name, it's easy to use the wrong one by accident. Give one of them a new name. + + ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── + + This destructure assignment doesn't introduce any new variables: + + 5│ main = \n -> n + 2 + ^^^^ + + If you don't need to use the value on the right-hand side of this + assignment, consider removing the assignment. Since effects are not + allowed at the top-level, assignments that don't introduce variables + cannot affect a program's behavior "### ); @@ -10890,7 +10902,55 @@ All branches in an `if` must have the same type! Foo = Foo "# ), - @"" + @r###" + ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── + + This destructure assignment doesn't introduce any new variables: + + 3│ Pair _ _ = Pair 0 1 + ^^^^^^^^ + + If you don't need to use the value on the right-hand side of this + assignment, consider removing the assignment. Since effects are not + allowed at the top-level, assignments that don't introduce variables + cannot affect a program's behavior + + ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── + + This destructure assignment doesn't introduce any new variables: + + 5│ _ = Pair 0 1 + ^ + + If you don't need to use the value on the right-hand side of this + assignment, consider removing the assignment. Since effects are not + allowed at the top-level, assignments that don't introduce variables + cannot affect a program's behavior + + ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── + + This destructure assignment doesn't introduce any new variables: + + 7│ {} = {} + ^^ + + If you don't need to use the value on the right-hand side of this + assignment, consider removing the assignment. Since effects are not + allowed at the top-level, assignments that don't introduce variables + cannot affect a program's behavior + + ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── + + This destructure assignment doesn't introduce any new variables: + + 9│ Foo = Foo + ^^^ + + If you don't need to use the value on the right-hand side of this + assignment, consider removing the assignment. Since effects are not + allowed at the top-level, assignments that don't introduce variables + cannot affect a program's behavior + "### ); test_report!( diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index eb80c8e1a0a..479b8432440 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -195,6 +195,7 @@ pub enum Problem { unbound_symbol: Symbol, region: Region, }, + NoIdentifiersIntroduced(Region), OverloadedSpecialization { overload: Region, original_opaque: Symbol, @@ -317,6 +318,7 @@ impl Problem { Problem::ImplementsNonRequired { .. } => Warning, Problem::DoesNotImplementAbility { .. } => RuntimeError, Problem::NotBoundInAllPatterns { .. } => RuntimeError, + Problem::NoIdentifiersIntroduced(_) => Warning, Problem::OverloadedSpecialization { .. } => Warning, // Ideally, will compile Problem::UnnecessaryOutputWildcard { .. } => Warning, // TODO: sometimes this can just be a warning, e.g. if you have [1, .., .., 2] but we @@ -480,6 +482,7 @@ impl Problem { } | Problem::NotAnAbility(region) | Problem::ImplementsNonRequired { region, .. } + | Problem::NoIdentifiersIntroduced(region) | Problem::DoesNotImplementAbility { region, .. } | Problem::OverloadedSpecialization { overload: region, .. diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index fb8afa8ea43..e1c3225f570 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1189,6 +1189,14 @@ pub fn can_problem<'b>( ]); title = "NAME NOT BOUND IN ALL PATTERNS".to_string(); } + Problem::NoIdentifiersIntroduced(region) => { + doc = alloc.stack([ + alloc.reflow("This destructure assignment doesn't introduce any new variables:"), + alloc.region(lines.convert_region(region), severity), + alloc.reflow("If you don't need to use the value on the right-hand side of this assignment, consider removing the assignment. Since effects are not allowed at the top-level, assignments that don't introduce variables cannot affect a program's behavior"), + ]); + title = "UNNECESSARY DEFINITION".to_string(); + } Problem::OverloadedSpecialization { ability_member, overload, From 564188466909c590900501c5bfb9b1272996bfab Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 7 Nov 2024 00:42:20 -0300 Subject: [PATCH 72/73] Update regions in suffixed tests --- ...uffixed__suffixed_tests__closure_simple.snap | 17 +++++++++-------- ...fixed__suffixed_tests__multi_defs_stmts.snap | 11 ++++++----- ..._suffixed__suffixed_tests__simple_pizza.snap | 17 +++++++++-------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index dce9c0f3f12..e7e9b933213 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -51,19 +52,19 @@ Defs { ident: "msg", }, ], - @31-42 Apply( - @31-42 Var { + @31-43 Apply( + @31-43 Var { module_name: "Task", ident: "await", }, [ - @31-42 Defs( + @31-43 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @31-42, + @31-43, ], space_before: [ Slice { start: 0, length: 0 }, @@ -93,8 +94,8 @@ Defs { body_pattern: @31-43 Identifier { ident: "#!0_stmt", }, - body_expr: @31-42 Apply( - @31-42 Var { + body_expr: @31-43 Apply( + @31-43 Var { module_name: "", ident: "line", }, @@ -111,12 +112,12 @@ Defs { }, ], }, - @31-42 Var { + @31-43 Var { module_name: "", ident: "#!0_stmt", }, ), - @31-42 Closure( + @31-43 Closure( [ @31-43 Underscore( "#!stmt", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index 8e98fb84a8d..a72d226a2e0 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -94,14 +95,14 @@ Defs { "#!stmt", ), ], - @33-55 Apply( - @33-55 Var { + @33-56 Apply( + @33-56 Var { module_name: "Task", ident: "await", }, [ - @33-55 Apply( - @33-55 Var { + @33-56 Apply( + @33-56 Var { module_name: "Stdout", ident: "line", }, @@ -116,7 +117,7 @@ Defs { Pizza, ), ), - @33-55 Closure( + @33-56 Closure( [ @28-30 RecordDestructure( [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 1d5573d9e00..ff13f20710f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -24,19 +25,19 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-56 Apply( - @11-56 Var { + @11-57 Apply( + @11-57 Var { module_name: "Task", ident: "await", }, [ - @11-56 Defs( + @11-57 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @11-56, + @11-57, ], space_before: [ Slice { start: 0, length: 0 }, @@ -66,8 +67,8 @@ Defs { body_pattern: @11-57 Identifier { ident: "#!0_stmt", }, - body_expr: @11-56 Apply( - @11-56 Var { + body_expr: @11-57 Apply( + @11-57 Var { module_name: "", ident: "line", }, @@ -101,12 +102,12 @@ Defs { }, ], }, - @11-56 Var { + @11-57 Var { module_name: "", ident: "#!0_stmt", }, ), - @11-56 Closure( + @11-57 Closure( [ @11-57 Underscore( "#!stmt", From 7f8149d386806957ba47de0370258162caddd7e4 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 7 Nov 2024 01:46:03 -0300 Subject: [PATCH 73/73] Ignore all but one cli_run effectful tests on Linux Things go wrong when multiple tests depend on the same platform on Linux. Tried a few workarounds but the problem persisted. We decided to keep only one test for now. --- crates/cli/tests/cli_run.rs | 76 ++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index d6fc773326f..70ba8733fd5 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -986,25 +986,6 @@ mod cli_run { ); } - #[test] - #[cfg_attr(windows, ignore)] - fn effectful_hello() { - test_roc_app( - "crates/cli/tests/effectful", - "hello.roc", - &[], - &[], - &[], - indoc!( - r#" - I'm an effect 👻 - "# - ), - UseValgrind::No, - TestCliCommands::Dev, - ); - } - #[test] #[cfg_attr(windows, ignore)] fn effectful_form() { @@ -1033,8 +1014,48 @@ mod cli_run { ); } + // Other effectful tests are disabled in Linux because of a platform build issue + // The program actually runs fine, but it fails on tests + + #[test] + #[cfg_attr(windows, ignore)] + #[cfg_attr(target_os = "linux", ignore)] + fn interactive_effects() { + test_roc_app( + "examples/cli", + "effects.roc", + &["hi there!"], + &[], + &[], + "hi there!\nIt is known\n", + UseValgrind::Yes, + TestCliCommands::Run, + ) + } + #[test] #[cfg_attr(windows, ignore)] + #[cfg_attr(target_os = "linux", ignore)] + fn effectful_hello() { + test_roc_app( + "crates/cli/tests/effectful", + "hello.roc", + &[], + &[], + &[], + indoc!( + r#" + I'm an effect 👻 + "# + ), + UseValgrind::No, + TestCliCommands::Dev, + ); + } + + #[test] + #[cfg_attr(windows, ignore)] + #[cfg_attr(target_os = "linux", ignore)] fn effectful_loops() { test_roc_app( "crates/cli/tests/effectful", @@ -1059,6 +1080,7 @@ mod cli_run { #[test] #[cfg_attr(windows, ignore)] + #[cfg_attr(target_os = "linux", ignore)] fn effectful_untyped_passed_fx() { test_roc_app( "crates/cli/tests/effectful", @@ -1080,6 +1102,7 @@ mod cli_run { #[test] #[cfg_attr(windows, ignore)] + #[cfg_attr(target_os = "linux", ignore)] fn effectful_ignore_result() { test_roc_app( "crates/cli/tests/effectful", @@ -1255,21 +1278,6 @@ mod cli_run { assert_valid_roc_check_status(out.status); } - #[test] - #[cfg_attr(windows, ignore)] - fn interactive_effects() { - test_roc_app( - "examples/cli", - "effects.roc", - &["hi there!"], - &[], - &[], - "hi there!\nIt is known\n", - UseValgrind::Yes, - TestCliCommands::Run, - ) - } - #[test] #[cfg_attr(windows, ignore)] // tea = The Elm Architecture