From 5303610174e2c3e3f3e67b758815d9c829e23bd1 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Tue, 6 Feb 2024 13:31:55 -0800 Subject: [PATCH] [move 2024] Add macro functions (#15928) ## Description - Added support for macros in 2024.alpha - Minor reworks to labels and blocks TODO: - [x] Decide on lambda type syntax - [x] Decide on lambda type implicit () - [x] Subst tparams inside macro body - [x] Fix lvalues for lambdas (nesting and type annots) ## Test Plan How did you test the new or updated feature? --- If your changes are not user-facing and do not break anything, you can skip the following section. Otherwise, please briefly describe what has changed under the Release Notes section. ### Type of Change (Check all that apply) - [ ] protocol change - [X] user-visible impact - [X] breaking change for a client SDKs - [ ] breaking change for FNs (FN binary must upgrade) - [ ] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [ ] necessitate either a data wipe or data migration ### Release notes A set of changes for the Move compiler - macro function support has been added for 2024.alpha. - Bug fix (and minor breaking change): Move type parameters now properly respect the rules of restricted identifiers, similar to all other names. - To support macros, the syntax for the type signature of lambdas has changed. This will break any existing specs (which are no longer supported) when moving to the new edition. --- Cargo.lock | 25 + crates/workspace-hack/Cargo.toml | 8 +- external-crates/move/Cargo.lock | 23 + external-crates/move/Cargo.toml | 1 + .../move/crates/move-analyzer/src/symbols.rs | 16 +- .../args.exp | 2 +- .../public_package_different_both/args.exp | 2 +- .../args.exp | 2 +- .../control_flow/basic_named_blocks.move | 20 +- .../control_flow/named_block_nesting.move | 73 +- .../tests/control_flow/nested_blocks.move | 26 +- .../macros/break_and_continue_by_name.exp | 1 + .../macros/break_and_continue_by_name.move | 39 + .../tests/macros/break_from_macro_arg.exp | 10 + .../tests/macros/break_from_macro_arg.move | 27 + .../tests/macros/do_not_evaluate_unused.exp | 1 + .../tests/macros/do_not_evaluate_unused.move | 17 + .../tests/macros/lambda_return.exp | 7 + .../tests/macros/lambda_return.move | 28 + .../tests/macros/loop.exp | 1 + .../tests/macros/loop.move | 39 + .../tests/macros/loop_nested.exp | 1 + .../tests/macros/loop_nested.move | 53 + .../tests/macros/method_is_by_value.exp | 37 + .../tests/macros/method_is_by_value.move | 43 + .../method_is_by_value_even_when_ignored.exp | 19 + .../method_is_by_value_even_when_ignored.move | 28 + .../move/crates/move-compiler/Cargo.toml | 1 + .../move-compiler/src/cfgir/locals/mod.rs | 2 +- .../src/command_line/compiler.rs | 181 ++-- .../move-compiler/src/diagnostics/codes.rs | 9 + .../move-compiler/src/diagnostics/mod.rs | 38 + .../crates/move-compiler/src/editions/mod.rs | 3 + .../crates/move-compiler/src/expansion/ast.rs | 88 +- .../move-compiler/src/expansion/translate.rs | 436 ++++++-- .../move/crates/move-compiler/src/hlir/ast.rs | 2 +- .../src/hlir/detect_dead_code.rs | 2 +- .../move-compiler/src/hlir/translate.rs | 27 +- .../move/crates/move-compiler/src/lib.rs | 10 +- .../crates/move-compiler/src/naming/ast.rs | 241 ++++- .../src/naming/resolve_use_funs.rs | 27 +- .../move-compiler/src/naming/translate.rs | 720 +++++++++---- .../crates/move-compiler/src/parser/ast.rs | 96 +- .../crates/move-compiler/src/parser/lexer.rs | 44 +- .../crates/move-compiler/src/parser/syntax.rs | 335 ++++-- .../crates/move-compiler/src/shared/mod.rs | 6 + .../move-compiler/src/shared/program_info.rs | 2 + .../src/sui_mode/linters/coin_field.rs | 4 +- .../src/sui_mode/linters/freeze_wrapped.rs | 3 +- .../move-compiler/src/sui_mode/linters/mod.rs | 2 +- .../move-compiler/src/sui_mode/typing.rs | 14 +- .../crates/move-compiler/src/typing/ast.rs | 90 +- .../crates/move-compiler/src/typing/core.rs | 443 +++++++- .../src/typing/dependency_ordering.rs | 6 +- .../crates/move-compiler/src/typing/expand.rs | 32 +- .../src/typing/infinite_instantiations.rs | 13 +- .../move-compiler/src/typing/macro_expand.rs | 955 ++++++++++++++++++ .../crates/move-compiler/src/typing/mod.rs | 1 + .../src/typing/recursive_structs.rs | 4 + .../move-compiler/src/typing/translate.rs | 682 +++++++++++-- .../move-compiler/src/typing/visitor.rs | 2 +- .../src/unit_test/filter_test_members.rs | 3 +- .../tests/move_2024/expansion/entry_macro.exp | 8 + .../move_2024/expansion/entry_macro.move | 4 + .../macro_identifier_for_non_macro.exp | 30 + .../macro_identifier_for_non_macro.move | 6 + .../macro_identifier_invalid_position.exp | 24 + .../macro_identifier_invalid_position.move | 6 + .../expansion/macro_identifier_missing.exp | 30 + .../expansion/macro_identifier_missing.move | 5 + .../macro_identifier_valid_names.move | 16 + .../move_2024/expansion/native_macro.exp | 8 + .../move_2024/expansion/native_macro.move | 4 + .../lambda_captures_break_and_continue.exp | 134 +++ .../lambda_captures_break_and_continue.move | 44 + .../naming/lambda_shadows_function.exp | 18 + .../naming/lambda_shadows_function.move | 7 + .../naming/lambda_to_outer_loop.move | 22 + .../naming/lambda_with_control_flow.move | 11 + .../lambda_with_control_flow_named.move | 39 + .../naming/lambda_with_type_args.exp | 6 + .../naming/lambda_with_type_args.move | 5 + .../naming/macro_identifier_assignment.exp | 28 + .../naming/macro_identifier_assignment.move | 12 + .../naming/macro_parameter_assignment.exp | 20 + .../naming/macro_parameter_assignment.move | 12 + .../tests/move_2024/naming/mut_underscore.exp | 18 + .../move_2024/naming/mut_underscore.move | 13 + .../move_2024/naming/named_blocks_invalid.exp | 36 +- .../naming/named_blocks_invalid.move | 30 +- .../move_2024/naming/unbound_var_call.exp | 6 + .../move_2024/naming/unbound_var_call.move | 9 + .../move_2024/naming/unused_by_value_arg.exp | 16 + .../move_2024/naming/unused_by_value_arg.move | 10 + .../move_2024/naming/unused_lambda_arg.exp | 32 + .../move_2024/naming/unused_lambda_arg.move | 17 + .../tests/move_2024/naming/use_fun_local.exp | 28 + .../tests/move_2024/naming/use_fun_local.move | 16 + .../naming/use_fun_local_doesnt_shadow.move | 12 + .../move_2024/naming/use_fun_unbound.exp | 18 + .../move_2024/naming/use_fun_unbound.move | 13 + .../parser/duplicate_macro_modifier.exp | 8 + .../parser/duplicate_macro_modifier.move | 3 + ...lobal_access_value_invalid_macro_ident.exp | 9 + ...obal_access_value_invalid_macro_ident.move | 5 + .../move_2024/parser/invalid_macro_bang.exp | 9 + .../move_2024/parser/invalid_macro_bang.move | 4 + .../parser/invalid_macro_bang_no_args.exp | 9 + .../parser/invalid_macro_bang_no_args.move | 4 + .../parser/invalid_macro_identifier_usage.exp | 27 + .../invalid_macro_identifier_usage.move | 5 + .../parser/invalid_macro_modifier.exp | 40 + .../parser/invalid_macro_modifier.move | 7 + .../parser/lambda_expression_return_type.move | 12 + .../lambda_expression_return_type_invalid.exp | 9 + ...lambda_expression_return_type_invalid.move | 9 + ...bda_expression_return_type_named_block.exp | 9 + ...da_expression_return_type_named_block.move | 9 + .../tests/move_2024/parser/lambda_lvalue.move | 30 + .../parser/lambda_lvalue_with_types.move | 34 + .../parser/macro_identifier_invalid.exp | 6 + .../parser/macro_identifier_invalid.move | 4 + ...tifier_invalid_no_following_characters.exp | 6 + ...ifier_invalid_no_following_characters.move | 2 + .../tests/move_2024/parser/named_blocks.move | 26 +- .../move_2024/parser/valid_fun_types.move | 27 + .../move_2024/typing/annotated_lambda.exp | 6 + .../move_2024/typing/annotated_lambda.move | 10 + .../typing/break_in_macro_arg_invalid.exp | 36 + .../typing/break_in_macro_arg_invalid.move | 17 + .../typing/call_on_non_lambda_arg.exp | 27 + .../typing/call_on_non_lambda_arg.move | 13 + .../move_2024/typing/call_target_mismatch.exp | 36 + .../typing/call_target_mismatch.move | 14 + .../move_2024/typing/dot_call_private.exp | 4 +- .../typing/lambda_annotated_return_type.move | 17 + .../lambda_annotated_return_type_invalid.exp | 18 + .../lambda_annotated_return_type_invalid.move | 16 + .../tests/move_2024/typing/lambda_return.move | 40 + .../lambda_return_invalid_conditional.exp | 72 ++ .../lambda_return_invalid_conditional.move | 14 + .../typing/lambda_return_invalid_simple.exp | 30 + .../typing/lambda_return_invalid_simple.move | 13 + .../typing/lambda_return_mismatched.exp | 57 ++ .../typing/lambda_return_mismatched.move | 29 + .../typing/lambda_subtyping_invalid.exp | 24 + .../typing/lambda_subtyping_invalid.move | 15 + ...a_subtyping_usage_respects_annotations.exp | 33 + ..._subtyping_usage_respects_annotations.move | 26 + .../typing/lambda_subtyping_valid.move | 15 + .../macro_arg_by_name_invalid_usage_deref.exp | 16 + ...macro_arg_by_name_invalid_usage_deref.move | 11 + .../macro_arg_by_name_invalid_usage_value.exp | 12 + ...macro_arg_by_name_invalid_usage_value.move | 11 + .../macro_arg_by_name_invalid_usage_var.exp | 12 + .../macro_arg_by_name_invalid_usage_var.move | 13 + .../macro_arg_by_name_strange_usage.exp | 21 + .../macro_arg_by_name_strange_usage.move | 19 + ...macro_arg_by_name_strange_usage_fields.exp | 10 + ...acro_arg_by_name_strange_usage_fields.move | 16 + .../macro_by_name_gives_unique_locals.exp | 9 + .../macro_by_name_gives_unique_locals.move | 29 + .../macro_call_indirect_lambda_invalid.exp | 48 + .../macro_call_indirect_lambda_invalid.move | 23 + .../typing/macro_dot_call_auto_borrow.move | 30 + .../typing/macro_duck_typing_constraint.move | 18 + .../macro_duck_typing_constraint_invalid.exp | 57 ++ .../macro_duck_typing_constraint_invalid.move | 18 + .../typing/macro_duck_typing_method.move | 26 + .../macro_duck_typing_method_invalid.exp | 9 + .../macro_duck_typing_method_invalid.move | 10 + .../macro_duck_typing_specific_type.move | 26 + ...acro_duck_typing_specific_type_invalid.exp | 51 + ...cro_duck_typing_specific_type_invalid.move | 23 + .../move_2024/typing/macro_subst_tparams.move | 29 + .../typing/macro_subst_tparams_invalid.exp | 206 ++++ .../typing/macro_subst_tparams_invalid.move | 31 + .../typing/macro_type_args_ref_or_tuple.move | 11 + .../typing/macro_unique_use_fun_scopes.move | 55 + ...unique_use_fun_scopes_captured_lambda.move | 25 + ...ro_unique_use_fun_scopes_cross_module.move | 36 + ...o_unique_use_fun_scopes_in_lambda_arg.move | 17 + .../typing/macros_have_unique_scopes.move | 32 + ...acros_have_unique_scopes_block_labels.move | 103 ++ ...macros_have_unique_scopes_by_name_arg.move | 38 + .../macros_have_unique_scopes_nested.move | 40 + ..._have_unique_scopes_unhygienic_capture.exp | 12 + ...have_unique_scopes_unhygienic_capture.move | 18 + ..._scopes_unhygienic_capture_by_name_arg.exp | 48 + ...scopes_unhygienic_capture_by_name_arg.move | 22 + .../typing/macros_lambdas_checked.move | 15 + .../typing/macros_lambdas_checked_invalid.exp | 39 + .../macros_lambdas_checked_invalid.move | 25 + .../macros_lambdas_checked_invalid_arity.exp | 116 +++ .../macros_lambdas_checked_invalid_arity.move | 40 + .../typing/macros_not_type_checked.move | 6 + .../typing/macros_not_type_checked_called.exp | 9 + .../macros_not_type_checked_called.move | 12 + .../typing/macros_return_checked_invalid.exp | 78 ++ .../typing/macros_return_checked_invalid.move | 25 + .../typing/macros_return_checked_valid.move | 28 + .../typing/macros_types_checked_invalid.move | 20 + ...cros_types_checked_invalid_constraints.exp | 127 +++ ...ros_types_checked_invalid_constraints.move | 30 + ...pes_checked_invalid_constraints_simple.exp | 51 + ...es_checked_invalid_constraints_simple.move | 21 + .../typing/macros_types_checked_valid.move | 17 + .../typing/macros_visibility_not_checked.move | 16 + .../macros_visibility_not_checked_called.exp | 36 + .../macros_visibility_not_checked_called.move | 32 + .../typing/mismatched_lambda_arity.exp | 27 + .../typing/mismatched_lambda_arity.move | 11 + ...module_call_visibility_package_invalid.exp | 12 +- .../recursive_macros_by_name_arg_invalid.exp | 42 + .../recursive_macros_by_name_arg_invalid.move | 33 + .../recursive_macros_by_name_arg_valid.move | 18 + .../typing/recursive_macros_invalid.exp | 48 + .../typing/recursive_macros_invalid.move | 44 + .../recursive_macros_invalid_lambdas.exp | 42 + .../recursive_macros_invalid_lambdas.move | 33 + .../typing/recursive_macros_valid.move | 18 + .../unused_let_mut_leading_underscore.move | 6 + .../move_2024/typing/unused_macro_arg.exp | 32 + .../move_2024/typing/unused_macro_arg.move | 14 + .../unused_macro_arg_affects_inference.exp | 14 + .../unused_macro_arg_affects_inference.move | 16 + .../unused_macro_arg_invalid_constraint.exp | 32 + .../unused_macro_arg_invalid_constraint.move | 18 + .../typing/unused_macro_arg_method_call.exp | 12 + .../typing/unused_macro_arg_method_call.move | 11 + .../tests/move_2024/typing/unused_use_fun.exp | 32 + .../move_2024/typing/unused_use_fun.move | 24 + .../unused_use_fun_silenced_by_macros.exp | 16 + .../unused_use_fun_silenced_by_macros.move | 43 + .../use_lambda_outside_call_invalid.exp | 36 + .../use_lambda_outside_call_invalid.move | 20 + .../typing/use_lambda_outside_call_valid.move | 41 + .../expansion/invalid_local_name.exp | 6 +- .../restricted_type_parameter_names.exp | 156 +++ .../restricted_type_parameter_names.move | 33 + ...tricted_type_parameter_names_peculiar.move | 16 + .../move_check/feature_gate/macro_call.exp | 29 + .../move_check/feature_gate/macro_call.move | 6 + .../feature_gate/macro_definition.exp | 8 + .../feature_gate/macro_definition.move | 3 + .../move_check/feature_gate/macro_lambda.exp | 16 + .../move_check/feature_gate/macro_lambda.move | 7 + .../move_check/parser/constant_native.exp | 2 +- .../tests/move_check/parser/entry_struct.exp | 2 +- .../function_conflicting_visibility.exp | 140 ++- .../function_conflicting_visibility.move | 12 +- .../parser/spec_lambda_return_missing.exp | 9 + .../parser/spec_lambda_return_missing.move | 3 + .../parser/spec_parsing_fun_type_fail.exp | 14 - .../parser/spec_parsing_fun_type_fail.move | 4 - .../parser/spec_parsing_lambda_fail.exp | 14 +- .../move_check/parser/spec_parsing_ok.move | 2 +- .../move_check/parser/use_with_modifiers.exp | 4 +- .../move/commands/break_outside_loop.exp | 2 +- .../commands/break_outside_loop_in_else.exp | 2 +- .../commands/break_outside_loop_in_if.exp | 2 +- .../move/commands/continue_outside_loop.exp | 2 +- .../commands/continue_outside_loop_in_if.exp | 2 +- .../move_check/typing/break_outside_loop.exp | 6 +- .../tests/move_check/typing/cast_invalid.exp | 54 +- .../typing/constant_unsupported_exps.exp | 4 +- .../typing/continue_outside_loop.exp | 2 +- .../tests/move_check/typing/large_binop.move | 13 +- ...module_call_entry_function_was_invalid.exp | 2 +- .../typing/module_call_internal.exp | 2 +- .../module_call_visibility_friend_invalid.exp | 6 +- .../crates/move-stdlib/sources/vector.move | 1 + .../move/crates/move-symbol-pool/src/lib.rs | 2 + 273 files changed, 8985 insertions(+), 1023 deletions(-) create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.move create mode 100644 external-crates/move/crates/move-compiler/src/typing/macro_expand.rs create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_to_outer_loop.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow_named.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local_doesnt_shadow.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue_with_types.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/parser/valid_fun_types.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_valid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_dot_call_auto_borrow.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_type_args_ref_or_tuple.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_captured_lambda.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_cross_module.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_in_lambda_arg.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_block_labels.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_by_name_arg.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_nested.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_valid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_valid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_valid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_valid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_let_mut_leading_underscore.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_valid.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names_peculiar.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.move delete mode 100644 external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.exp delete mode 100644 external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.move diff --git a/Cargo.lock b/Cargo.lock index 3e6a575b14f9f..36c5682191dda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6742,6 +6742,7 @@ dependencies = [ "petgraph 0.5.1", "regex", "serde", + "stacker", "tempfile", ] @@ -9555,6 +9556,15 @@ dependencies = [ "autotools", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "quanta" version = "0.11.1" @@ -11414,6 +11424,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -16654,6 +16677,7 @@ dependencies = [ "prost-types", "protobuf", "protobuf-src", + "psm", "quanta", "quick-error 1.2.3", "quick-error 2.0.1", @@ -16803,6 +16827,7 @@ dependencies = [ "spki 0.6.0", "spki 0.7.1", "stable_deref_trait", + "stacker", "static_assertions", "str-buf", "strip-ansi-escapes", diff --git a/crates/workspace-hack/Cargo.toml b/crates/workspace-hack/Cargo.toml index 96c0787fe237e..4a09254f15c2f 100644 --- a/crates/workspace-hack/Cargo.toml +++ b/crates/workspace-hack/Cargo.toml @@ -604,6 +604,7 @@ prost-5ef9efb8ec2df382 = { package = "prost", version = "0.12" } prost-a6292c17cd707f01 = { package = "prost", version = "0.11" } prost-types = { version = "0.12" } protobuf = { version = "2", default-features = false, features = ["with-bytes"] } +psm = { version = "0.1", default-features = false } quanta = { version = "0.11" } quick-error-dff4ba8e3ae991db = { package = "quick-error", version = "1", default-features = false } quick-error-f595c2ba2a3f28df = { package = "quick-error", version = "2", default-features = false } @@ -721,6 +722,7 @@ spin-d8f496e17d97b5cb = { package = "spin", version = "0.5", default-features = spinners = { version = "4", default-features = false } spki-3b31131e45eafb45 = { package = "spki", version = "0.6", default-features = false, features = ["pem", "std"] } spki-ca01ad9e24f5d932 = { package = "spki", version = "0.7", default-features = false, features = ["pem", "std"] } +stacker = { version = "0.1", default-features = false } static_assertions = { version = "1", default-features = false } strip-ansi-escapes = { version = "0.1", default-features = false } strsim = { version = "0.10", default-features = false } @@ -1530,6 +1532,7 @@ prost-derive-5ef9efb8ec2df382 = { package = "prost-derive", version = "0.12", de prost-derive-a6292c17cd707f01 = { package = "prost-derive", version = "0.11", default-features = false } prost-types = { version = "0.12" } protobuf = { version = "2", default-features = false, features = ["with-bytes"] } +psm = { version = "0.1", default-features = false } quanta = { version = "0.11" } quick-error-dff4ba8e3ae991db = { package = "quick-error", version = "1", default-features = false } quick-error-f595c2ba2a3f28df = { package = "quick-error", version = "2", default-features = false } @@ -1665,6 +1668,7 @@ spin-d8f496e17d97b5cb = { package = "spin", version = "0.5", default-features = spinners = { version = "4", default-features = false } spki-3b31131e45eafb45 = { package = "spki", version = "0.6", default-features = false, features = ["pem", "std"] } spki-ca01ad9e24f5d932 = { package = "spki", version = "0.7", default-features = false, features = ["pem", "std"] } +stacker = { version = "0.1", default-features = false } static_assertions = { version = "1", default-features = false } strip-ansi-escapes = { version = "0.1", default-features = false } strsim = { version = "0.10", default-features = false } @@ -2026,7 +2030,7 @@ scrypt = { version = "0.10", default-features = false } str-buf = { version = "1", default-features = false } uuid-c38e5c1d305a1b54 = { package = "uuid", version = "0.8", features = ["serde", "v4"] } widestring = { version = "0.5" } -winapi = { version = "0.3", default-features = false, features = ["cfg", "combaseapi", "consoleapi", "errhandlingapi", "evntrace", "fileapi", "handleapi", "heapapi", "ifdef", "impl-default", "in6addr", "inaddr", "ioapiset", "iphlpapi", "knownfolders", "libloaderapi", "lmaccess", "lmapibuf", "lmcons", "minwinbase", "namedpipeapi", "netioapi", "ntlsa", "ntsecapi", "ntstatus", "objbase", "objidl", "oleauto", "pdh", "powerbase", "processenv", "processthreadsapi", "profileapi", "psapi", "rpcdce", "securitybaseapi", "shellapi", "shlobj", "std", "stringapiset", "synchapi", "sysinfoapi", "wbemcli", "winbase", "wincon", "windef", "winerror", "winioctl", "winnt", "winsock2", "winuser", "ws2ipdef", "ws2tcpip", "wtypesbase"] } +winapi = { version = "0.3", default-features = false, features = ["cfg", "combaseapi", "consoleapi", "errhandlingapi", "evntrace", "fibersapi", "fileapi", "handleapi", "heapapi", "ifdef", "impl-default", "in6addr", "inaddr", "ioapiset", "iphlpapi", "knownfolders", "libloaderapi", "lmaccess", "lmapibuf", "lmcons", "minwinbase", "namedpipeapi", "netioapi", "ntlsa", "ntsecapi", "ntstatus", "objbase", "objidl", "oleauto", "pdh", "powerbase", "processenv", "processthreadsapi", "profileapi", "psapi", "rpcdce", "securitybaseapi", "shellapi", "shlobj", "std", "stringapiset", "synchapi", "sysinfoapi", "wbemcli", "winbase", "wincon", "windef", "winerror", "winioctl", "winnt", "winsock2", "winuser", "ws2ipdef", "ws2tcpip", "wtypesbase"] } winapi-util = { version = "0.1", default-features = false } windows-sys-b32c9ddb6d93a9d2 = { package = "windows-sys", version = "0.42", features = ["Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_IO", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse"] } windows-sys-c8eced492e86ede7 = { package = "windows-sys", version = "0.48", features = ["Win32_Foundation", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Shell"] } @@ -2061,7 +2065,7 @@ str-buf = { version = "1", default-features = false } uuid-c38e5c1d305a1b54 = { package = "uuid", version = "0.8", features = ["serde", "v4"] } vcpkg = { version = "0.2", default-features = false } widestring = { version = "0.5" } -winapi = { version = "0.3", default-features = false, features = ["cfg", "combaseapi", "consoleapi", "errhandlingapi", "evntrace", "fileapi", "handleapi", "heapapi", "ifdef", "impl-default", "in6addr", "inaddr", "ioapiset", "iphlpapi", "knownfolders", "libloaderapi", "lmaccess", "lmapibuf", "lmcons", "minwinbase", "namedpipeapi", "netioapi", "ntlsa", "ntsecapi", "ntstatus", "objbase", "objidl", "oleauto", "pdh", "powerbase", "processenv", "processthreadsapi", "profileapi", "psapi", "rpcdce", "securitybaseapi", "shellapi", "shlobj", "std", "stringapiset", "synchapi", "sysinfoapi", "wbemcli", "winbase", "wincon", "windef", "winerror", "winioctl", "winnt", "winsock2", "winuser", "ws2ipdef", "ws2tcpip", "wtypesbase"] } +winapi = { version = "0.3", default-features = false, features = ["cfg", "combaseapi", "consoleapi", "errhandlingapi", "evntrace", "fibersapi", "fileapi", "handleapi", "heapapi", "ifdef", "impl-default", "in6addr", "inaddr", "ioapiset", "iphlpapi", "knownfolders", "libloaderapi", "lmaccess", "lmapibuf", "lmcons", "minwinbase", "namedpipeapi", "netioapi", "ntlsa", "ntsecapi", "ntstatus", "objbase", "objidl", "oleauto", "pdh", "powerbase", "processenv", "processthreadsapi", "profileapi", "psapi", "rpcdce", "securitybaseapi", "shellapi", "shlobj", "std", "stringapiset", "synchapi", "sysinfoapi", "wbemcli", "winbase", "wincon", "windef", "winerror", "winioctl", "winnt", "winsock2", "winuser", "ws2ipdef", "ws2tcpip", "wtypesbase"] } winapi-util = { version = "0.1", default-features = false } windows-sys-b32c9ddb6d93a9d2 = { package = "windows-sys", version = "0.42", features = ["Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_IO", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse"] } windows-sys-c8eced492e86ede7 = { package = "windows-sys", version = "0.48", features = ["Win32_Foundation", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Shell"] } diff --git a/external-crates/move/Cargo.lock b/external-crates/move/Cargo.lock index b80ab9f2c6de1..cf4fc81cb4107 100644 --- a/external-crates/move/Cargo.lock +++ b/external-crates/move/Cargo.lock @@ -1715,6 +1715,7 @@ dependencies = [ "petgraph", "regex", "serde", + "stacker", "tempfile", ] @@ -2723,6 +2724,15 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3245,6 +3255,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/external-crates/move/Cargo.toml b/external-crates/move/Cargo.toml index 0b2cd27a0b4e2..55a39c32d6327 100644 --- a/external-crates/move/Cargo.toml +++ b/external-crates/move/Cargo.toml @@ -87,6 +87,7 @@ sha3 = "0.9.1" shell-words = "1.0.0" simplelog = "0.9.0" smallvec = "1.6.1" +stacker = "0.1.15" static_assertions = "1.1.0" syn = { version = "1.0.64", features = ["derive"] } tempfile = "3.2.0" diff --git a/external-crates/move/crates/move-analyzer/src/symbols.rs b/external-crates/move/crates/move-analyzer/src/symbols.rs index 57bbfd2f9ead0..7cc16379671b7 100644 --- a/external-crates/move/crates/move-analyzer/src/symbols.rs +++ b/external-crates/move/crates/move-analyzer/src/symbols.rs @@ -521,6 +521,13 @@ fn type_to_ide_string(sp!(_, t): &Type) -> String { ) } }, + Type_::Fun(args, ret) => { + format!( + "|{}| -> {}", + type_list_to_ide_string(args), + type_to_ide_string(ret) + ) + } Type_::Anything => "_".to_string(), Type_::Var(_) => "invalid type (var)".to_string(), Type_::UnresolvedError => "unknown type (unresolved)".to_string(), @@ -1534,7 +1541,7 @@ impl<'a> ParsingSymbolicator<'a> { self.exp_symbols(e2); } E::Loop(e) => self.exp_symbols(e), - E::NamedBlock(_, seq) => self.seq_symbols(seq), + E::Labled(_, e) => self.exp_symbols(e), E::Block(seq) => self.seq_symbols(seq), E::ExpList(l) => l.iter().for_each(|e| self.exp_symbols(e)), E::Assign(e1, e2) => { @@ -1560,7 +1567,7 @@ impl<'a> ParsingSymbolicator<'a> { } E::Borrow(_, e) => self.exp_symbols(e), E::Dot(e, _) => self.exp_symbols(e), - E::DotCall(e, _, vo, v) => { + E::DotCall(e, _, _, vo, v) => { self.exp_symbols(e); if let Some(v) = vo { v.iter().for_each(|t| self.type_symbols(t)); @@ -1993,7 +2000,7 @@ impl<'a> TypingSymbolicator<'a> { self.seq_item_symbols(&mut scope, seq_item); } } - FunctionBody_::Native => (), + FunctionBody_::Macro | FunctionBody_::Native => (), } // process return types @@ -2126,7 +2133,7 @@ impl<'a> TypingSymbolicator<'a> { self.exp_symbols(t, scope); self.exp_symbols(f, scope); } - E::While(cond, _, body) => { + E::While(_, cond, body) => { self.exp_symbols(cond, scope); self.exp_symbols(body, scope); } @@ -2219,6 +2226,7 @@ impl<'a> TypingSymbolicator<'a> { let UseFuns { resolved, implicit_candidates, + color: _, } = use_funs; // at typing there should be no unresolved candidates (it's also checked in typing diff --git a/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_addresses/args.exp b/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_addresses/args.exp index 34f1ee8de495d..803ff6baf0db9 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_addresses/args.exp +++ b/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_addresses/args.exp @@ -4,7 +4,7 @@ error[E04001]: restricted visibility ┌─ ./sources/B.move:3:31 │ 3 │ public fun usage(): u64 { defn::definition() } - │ ^^^^^^^^^^^^^^^^^^ Invalid call to 'A::defn::definition' + │ ^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function 'A::defn::definition' │ ┌─ ./sources/A.move:2:5 │ diff --git a/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_both/args.exp b/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_both/args.exp index 98ef7811e8245..0836ea9e9eff9 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_both/args.exp +++ b/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_both/args.exp @@ -5,7 +5,7 @@ error[E04001]: restricted visibility ┌─ ./sources/B.move:3:31 │ 3 │ public fun usage(): u64 { defn::definition() } - │ ^^^^^^^^^^^^^^^^^^ Invalid call to 'A::defn::definition' + │ ^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function 'A::defn::definition' │ ┌─ ./defn/sources/A.move:2:5 │ diff --git a/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_packages/args.exp b/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_packages/args.exp index 2f0553a5d8188..99527ba85b421 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_packages/args.exp +++ b/external-crates/move/crates/move-cli/tests/build_tests/public_package_different_packages/args.exp @@ -5,7 +5,7 @@ error[E04001]: restricted visibility ┌─ ./sources/A.move:3:31 │ 3 │ public fun usage(): u64 { defn::definition() } - │ ^^^^^^^^^^^^^^^^^^ Invalid call to 'A::defn::definition' + │ ^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function 'A::defn::definition' │ ┌─ ./defn/sources/A.move:2:5 │ diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/basic_named_blocks.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/basic_named_blocks.move index d9413e91f5431..8fd2a8bcd9bd0 100644 --- a/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/basic_named_blocks.move +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/basic_named_blocks.move @@ -5,7 +5,7 @@ module 0x42::m { #[allow(dead_code)] public fun t00(): u64 { - 'name: { + 'name: { return 'name 10; 20 } @@ -13,7 +13,7 @@ module 0x42::m { #[allow(dead_code)] public fun t01(): u64 { - 'name: { + 'name: { 'name2: { return 'name 10; 20 @@ -23,8 +23,8 @@ module 0x42::m { #[allow(dead_code)] public fun t02(): u64 { - loop 'outer: { - let _x = loop 'inner: { + 'outer: loop { + let _x = 'inner: loop { break 'outer 10; break 'inner 20 }; @@ -33,8 +33,8 @@ module 0x42::m { #[allow(dead_code)] public fun t03(): u64 { - loop 'outer: { - let x = loop 'inner: { + 'outer: loop { + let x = 'inner: loop { break 'inner 10 }; break 'outer x @@ -42,15 +42,15 @@ module 0x42::m { } public fun t04(cond: bool): u64 { - while (cond) 'name: { + 'name: while (cond) { break 'name }; 10 } public fun t05(cond: bool): u64 { - loop 'outer: { - while (cond) 'body: { + 'outer: loop { + 'body: while (cond) { if (cond) { break 'outer 10 }; continue 'body } @@ -58,7 +58,7 @@ module 0x42::m { } public fun t06(cond: bool): u64 { - while (cond) 'name: { + 'name: while (cond) { return 10 }; 20 diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/named_block_nesting.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/named_block_nesting.move index e1693d030e0cd..6d21653229ddb 100644 --- a/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/named_block_nesting.move +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/named_block_nesting.move @@ -6,31 +6,31 @@ module 42::m { entry fun t00() { - loop 'a: { + 'a: loop { loop { break 'a } } } entry fun t01() { - loop 'a: { + 'a: loop { 'b: { break 'a } } } entry fun t02() { - loop 'a: { + 'a: loop { (loop { break 'a } : ()) } } entry fun t03() { - loop 'a: { + 'a: loop { ('b: { break 'a } : ()) } } entry fun t04() { - let x = loop 'a: { + let x = 'a: loop { (loop { break 'a 0 } : ()) }; assert!(x == 0, 42); @@ -38,7 +38,7 @@ module 42::m { entry fun t05() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; (loop { continue 'a } : ()) }; @@ -46,7 +46,7 @@ module 42::m { entry fun t06() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; 'b: { continue 'a } }; @@ -54,7 +54,7 @@ module 42::m { entry fun t07() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; ('b: { continue 'a } : ()) }; @@ -62,7 +62,7 @@ module 42::m { entry fun t08() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; 'b: { break 'a } }; @@ -70,29 +70,29 @@ module 42::m { entry fun t09() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; ('b: { break 'a } : ()) }; } entry fun t10() { - loop 'a: { + 'a: loop { 'b: { - 'c: { loop 'd: { break 'a } } + 'c: { 'd: loop { break 'a } } } } } entry fun t11() { - let _x = loop 'a: { + let _x = 'a: loop { (loop { break 'a 0 } : ()) }; } entry fun t12() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; (loop { continue 'a } : ()) } @@ -100,7 +100,7 @@ module 42::m { entry fun t13() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; 'b: { continue 'a } } @@ -108,7 +108,7 @@ module 42::m { entry fun t14() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; ('b: { continue 'a } : ()) } @@ -116,7 +116,7 @@ module 42::m { entry fun t15() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; 'b: { break 'a } } @@ -124,82 +124,82 @@ module 42::m { entry fun t16() { let mut i = 0; - while (i < 10) 'a: { + 'a: while (i < 10) { i = i + 1; ('b: { break 'a } : ()) } } entry fun t17() { - loop 'a: { + 'a: loop { ('b: { - 'c: { loop 'd: { break 'a } } + 'c: { 'd: loop { break 'a } } }: ()) } } entry fun t18() { - loop 'a: { + 'a: loop { 'b: { - ('c: { loop 'd: { break 'a } } : ()) + ('c: { 'd: loop { break 'a } } : ()) } } } entry fun t19() { - loop 'a: { + 'a: loop { ('b: { - ('c: { loop 'd: { break 'a } } : ()) + ('c: { 'd: loop { break 'a } } : ()) } : ()) } } entry fun t20(): u64 { - loop 'a: { + 'a: loop { ('b: { - 'c: { loop 'd: { break 'a 0 } } + 'c: { 'd: loop { break 'a 0 } } }: ()) } } entry fun t21(): u64 { - loop 'a: { + 'a: loop { 'b: { - ('c: { loop 'd: { break 'a 0 } } : ()) + ('c: { 'd: loop { break 'a 0 } } : ()) } } } entry fun t22(): u64 { - loop 'a: { + 'a: loop { ('b: { - ('c: { loop 'd: { break 'a 0 } } : ()) + ('c: { 'd: loop { break 'a 0 } } : ()) } : ()) } } entry fun t23(): u64 { - let x = loop 'a: { + let x = 'a: loop { ('b: { - 'c: { loop 'd: { break 'a 0 } } + 'c: { 'd: loop { break 'a 0 } } }: ()) }; x } entry fun t24(): u64 { - let x = loop 'a: { + let x = 'a: loop { 'b: { - ('c: { loop 'd: { break 'a 0 } } : ()) + ('c: { 'd: loop { break 'a 0 } } : ()) } }; x } entry fun t25(): u64 { - let x = loop 'a: { + let x = 'a: loop { ('b: { - ('c: { loop 'd: { break 'a 0 } } : ()) + ('c: { 'd: loop { break 'a 0 } } : ()) } : ()) }; x @@ -257,4 +257,3 @@ module 42::m { //# run 42::m::t24 //# run 42::m::t25 - diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/nested_blocks.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/nested_blocks.move index e2c233d8e8c47..c7f9e4505b612 100644 --- a/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/nested_blocks.move +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/control_flow/nested_blocks.move @@ -5,8 +5,8 @@ module 0x42::m { #[allow(dead_code)] public fun t00(): u64 { - if ('name: { return 'name 10 } == 10) { - loop 'outer: { + if ('name: { return 'name 10 } == 10) { + 'outer: loop { return 10 } } else { @@ -17,12 +17,12 @@ module 0x42::m { #[allow(dead_code)] public fun t01(): u64 { if ('name: { return 'name 20 } == 10) { - loop 'outer: { + 'outer: loop { return 20 } }; return ('name: { - loop 'inner: { + 'inner: loop { return 'name 10 } }); @@ -31,9 +31,9 @@ module 0x42::m { #[allow(dead_code)] public fun t02(): u64 { - let x = loop 'outer: { - if ('name: { return 'name 20 } == 10) { - loop 'outer: { + let x = 'outer: loop { + if ('name: { return 'name 20 } == 10) { + 'outer: loop { return 20 } }; @@ -47,9 +47,9 @@ module 0x42::m { #[allow(dead_code)] public fun t03(): u64 { - let x = loop 'outer: { + let x = 'outer: loop { if ('name: { return 'name 20 } == 10) { - loop 'outer: { + 'outer: loop { return 20 } }; @@ -65,9 +65,9 @@ module 0x42::m { public fun t04(): u64 { let a = 'all: { loop { - let x = loop 'outer: { + let x = 'outer: loop { if ('name: { return 'name 20 } == 10) { - loop 'outer: { + 'outer: loop { return 20 } }; @@ -86,9 +86,9 @@ module 0x42::m { public fun t05(): u64 { let a = 'all: { loop { - let x = loop 'outer: { + let x = 'outer: loop { if ('name: { return 'name 20 } == 10) { - loop 'outer: { + 'outer: loop { return 20 } }; diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.exp new file mode 100644 index 0000000000000..b134af1aea2b5 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.exp @@ -0,0 +1 @@ +processed 5 tasks diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.move new file mode 100644 index 0000000000000..67fdfd5279d8f --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_and_continue_by_name.move @@ -0,0 +1,39 @@ +//# init --edition 2024.alpha + +//# publish + +module 42::m { + macro fun loop_forever<$T>($x: $T) { + loop $x + } + + entry fun t0() { + // TODO fix me. This should break the outer loop. + // loop { + // loop_forever!(break) + // } + } + + entry fun t1() { + // TODO fix me. This should break the outer loop. + // let x = loop { + // loop_forever!(break 0) + // }; + // assert!(x == 0, 42); + } + + entry fun t2() { + // TODO fix me. This should continue the outer loop. + // let mut i = 0; + // while (i < 10) { + // i = i + 1; + // loop_forever!(continue); + // }; + } +} + +//# run 42::m::t0 + +//# run 42::m::t1 + +//# run 42::m::t2 diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.exp new file mode 100644 index 0000000000000..37c1ea3337a35 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.exp @@ -0,0 +1,10 @@ +processed 4 tasks + +task 3 'run'. lines 27-27: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: 0x2a::m, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 22)], +} diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.move new file mode 100644 index 0000000000000..d4d33b483b866 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/break_from_macro_arg.move @@ -0,0 +1,27 @@ +//# init --edition 2024.alpha + +//# publish + +module 42::m { + macro fun foo($x: u64): u64 { + $x + $x + } + + fun t(cond: bool) { + let res = foo!('a: { + if (cond) return'a 1; + 0 + }); + assert!(res == 2, 0); + let res = foo!('a: { + if (!cond) return'a 2; + 4 + }); + assert!(res == 8, 0); + } +} + +//# run 42::m::t --args true + +// should abort +//# run 42::m::t --args false diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.exp new file mode 100644 index 0000000000000..fc5a4436b29d4 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.exp @@ -0,0 +1 @@ +processed 3 tasks diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.move new file mode 100644 index 0000000000000..14fd8d990697a --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/do_not_evaluate_unused.move @@ -0,0 +1,17 @@ +//# init --edition 2024.alpha + +//# publish +#[allow(all)] +module 42::m { + macro fun ignore<$T>(_: $T) {} + + macro fun unused<$T>($x: $T) {} + + // unused macro arguments are not evaluated, so this does not abort + fun does_not_abort() { + ignore!(abort 0); + unused!(abort 0); + } +} + +//# run 42::m::does_not_abort diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.exp new file mode 100644 index 0000000000000..0019d5d7c72fd --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.exp @@ -0,0 +1,7 @@ +processed 4 tasks + +task 2 'run'. lines 26-26: +return values: [2, 8, 16] + +task 3 'run'. lines 28-28: +return values: [0, 4, 16] diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.move new file mode 100644 index 0000000000000..1478a93fbb857 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/lambda_return.move @@ -0,0 +1,28 @@ +//# init --edition 2024.alpha + +//# publish + +#[allow(dead_code)] +module 42::m { + macro fun foo($x: || -> u64): u64 { + $x() + $x() + } + + fun t(cond: bool): vector { + vector[ + foo!(|| { + if (cond) return 1; + 0 + }), + foo!(|| { + if (!cond) return 2; + 4 + }), + foo!(|| return 8), + ] + } +} + +//# run 42::m::t --args true + +//# run 42::m::t --args false diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.exp new file mode 100644 index 0000000000000..fc5a4436b29d4 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.exp @@ -0,0 +1 @@ +processed 3 tasks diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.move new file mode 100644 index 0000000000000..772b1d0fa2158 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop.move @@ -0,0 +1,39 @@ +//# init --edition 2024.alpha + +//# publish + +module 0x42::m { + + macro fun for($start: u64, $stop: u64, $body: |u64|) { + let mut i = $start; + let stop = $stop; + while (i < stop) { + $body(i); + i = i + 1 + } + } + + macro fun for_each<$T>($v: &vector<$T>, $body: |&$T|) { + let v = $v; + let mut i = 0; + let n = v.length(); + while (i < n) { + $body(v.borrow(i)); + i = i + 1 + } + } + + entry fun t0() { + let mut count = 0; + 0x42::m::for!(0, 10, |x| count = count + x*x); + assert!(count == 285, 0); + + let es = vector[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut sum = 0; + 0x42::m::for_each!(&es, |x| sum = sum + *x); + assert!(sum == 45, 0); + } + +} + +//# run 0x42::m::t0 diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.exp new file mode 100644 index 0000000000000..fc5a4436b29d4 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.exp @@ -0,0 +1 @@ +processed 3 tasks diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.move new file mode 100644 index 0000000000000..1464d92a1bc5e --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/loop_nested.move @@ -0,0 +1,53 @@ +//# init --edition 2024.alpha + +//# publish + +module 0x42::m { + + macro fun for($start: u64, $stop: u64, $body: |u64|) { + let mut i = $start; + let stop = $stop; + while (i < stop) { + $body(i); + i = i + 1 + } + } + + macro fun new<$T>($len: u64, $f: |u64| -> $T): vector<$T> { + let mut v = vector[]; + for!(0, $len, |i| v.push_back($f(i))); + v + } + + macro fun for_each<$T>($v: &vector<$T>, $body: |&$T|) { + let v = $v; + for!(0, v.length(), |i| $body(v.borrow(i))) + } + + macro fun fold<$T, $U>( + $xs: &vector<$T>, + $init: $U, + $body: |$U, &$T| -> $U, + ): $U { + let xs = $xs; + let mut acc = $init; + for_each!(xs, |x| acc = $body(acc, x)); + acc + } + + macro fun sum($v: &vector): u64 { + fold!($v, 0, |acc, x| acc + *x) + } + + entry fun main() { + let v = new!(10, |i| i); + assert!(sum!(&v) == 45, 0); + + let vs = new!(10, |i| new!(10, |j| i * j)); + let total = fold!(&vs, 0, |acc, v| acc + sum!(v)); + assert!(total == 2025, 0); + } + +} + +//# run 0x42::m::main diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.exp new file mode 100644 index 0000000000000..25eb8ef96bd9d --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.exp @@ -0,0 +1,37 @@ +processed 6 tasks + +task 2 'run'. lines 37-37: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: 0x2a::m, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 3 'run'. lines 39-39: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(1), + location: 0x2a::m, + indices: [], + offsets: [(FunctionDefinitionIndex(1), 1)], +} + +task 4 'run'. lines 41-41: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(2), + location: 0x2a::m, + indices: [], + offsets: [(FunctionDefinitionIndex(4), 1)], +} + +task 5 'run'. lines 43-43: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(2), + location: 0x2a::m, + indices: [], + offsets: [(FunctionDefinitionIndex(5), 1)], +} diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.move new file mode 100644 index 0000000000000..95ff091a35b64 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value.move @@ -0,0 +1,43 @@ +//# init --edition 2024.alpha + +//# publish + +#[allow(dead_code, unused_assignment)] +module 42::m { + public struct X() has copy, drop; + + public fun x_abort(): X { abort 0 } + public fun id_abort(_: X): X { abort 1 } + + macro fun macro_abort($x: X) { + abort 2; + $x; + } + + // method syntax results in the macro arg being bound first before being passed to the method + // meaning these should abort from the LHS not the macro + fun aborts0() { + x_abort().macro_abort!(); + } + + fun aborts1() { + X().id_abort().macro_abort!(); + } + + // The macro should abort here, since the arg is evaluated after the abort + fun aborts2_not_0() { + macro_abort!(x_abort()); + } + + fun aborts2_not_1() { + macro_abort!(X().id_abort()); + } +} + +//# run 42::m::aborts0 + +//# run 42::m::aborts1 + +//# run 42::m::aborts2_not_0 + +//# run 42::m::aborts2_not_1 diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.exp new file mode 100644 index 0000000000000..d812846c4a3d4 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.exp @@ -0,0 +1,19 @@ +processed 4 tasks + +task 2 'run'. lines 26-26: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: 0x2a::m, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 3 'run'. lines 28-28: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(1), + location: 0x2a::m, + indices: [], + offsets: [(FunctionDefinitionIndex(1), 1)], +} diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.move new file mode 100644 index 0000000000000..47b5cf4aaf21b --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/macros/method_is_by_value_even_when_ignored.move @@ -0,0 +1,28 @@ +//# init --edition 2024.alpha + +//# publish + +#[allow(dead_code, unused_assignment)] +module 42::m { + public struct X() has copy, drop; + + public fun x_abort(): X { abort 0 } + public fun id_abort(_: X): X { abort 1 } + + macro fun macro_abort(_: X) { + abort 2 + } + // method syntax results in the macro arg being bound first before being passed to the method + // meaning these should abort from the LHS not the macro. Even though the LHS is discarded + fun aborts0() { + x_abort().macro_abort!(); + } + + fun aborts1() { + X().id_abort().macro_abort!(); + } +} + +//# run 42::m::aborts0 + +//# run 42::m::aborts1 diff --git a/external-crates/move/crates/move-compiler/Cargo.toml b/external-crates/move/crates/move-compiler/Cargo.toml index 3251279579586..dad2f235cd606 100644 --- a/external-crates/move/crates/move-compiler/Cargo.toml +++ b/external-crates/move/crates/move-compiler/Cargo.toml @@ -17,6 +17,7 @@ petgraph.workspace = true tempfile.workspace = true once_cell.workspace = true serde.workspace = true +stacker.workspace = true bcs.workspace = true diff --git a/external-crates/move/crates/move-compiler/src/cfgir/locals/mod.rs b/external-crates/move/crates/move-compiler/src/cfgir/locals/mod.rs index 5bb1b381ffcbb..c46019e24e542 100644 --- a/external-crates/move/crates/move-compiler/src/cfgir/locals/mod.rs +++ b/external-crates/move/crates/move-compiler/src/cfgir/locals/mod.rs @@ -416,7 +416,7 @@ fn add_drop_ability_tip(context: &Context, diag: &mut Diagnostic, st: SingleType T::Param(TParam { abilities, .. }) | T::Apply(Some(abilities), _, _) => { abilities.clone() } - T::Var(_) | T::Apply(None, _, _) => panic!("ICE expansion failed"), + T::Var(_) | T::Apply(None, _, _) | T::Fun(_, _) => panic!("ICE expansion failed"), }; (ty_arg, abilities) }), diff --git a/external-crates/move/crates/move-compiler/src/command_line/compiler.rs b/external-crates/move/crates/move-compiler/src/command_line/compiler.rs index ef8343b494ef9..9f5356a07ed23 100644 --- a/external-crates/move/crates/move-compiler/src/command_line/compiler.rs +++ b/external-crates/move/crates/move-compiler/src/command_line/compiler.rs @@ -294,7 +294,7 @@ impl<'a> Compiler<'a> { } let (source_text, pprog, comments) = - parse_program(&mut compilation_env, maps, targets, deps)?; + with_large_stack!(parse_program(&mut compilation_env, maps, targets, deps))?; let res: Result<_, Diagnostics> = SteppedCompiler::new_at_parser(compilation_env, pre_compiled_lib, pprog) @@ -850,91 +850,108 @@ fn run( pre_compiled_lib: Option<&FullyCompiledProgram>, cur: PassResult, until: Pass, - mut result_check: impl FnMut(&PassResult, &CompilationEnv), + result_check: impl FnMut(&PassResult, &CompilationEnv), ) -> Result { - compilation_env.check_diags_at_or_above_severity(Severity::Bug)?; - assert!( - until <= PASS_COMPILATION, - "Invalid pass for run_to. Target is greater than maximum pass" - ); - result_check(&cur, compilation_env); - if cur.equivalent_pass() >= until { - return Ok(cur); - } - - match cur { - PassResult::Parser(prog) => { - let prog = unit_test::filter_test_members::program(compilation_env, prog); - let prog = verification_attribute_filter::program(compilation_env, prog); - let eprog = expansion::translate::program(compilation_env, pre_compiled_lib, prog); - run( - compilation_env, - pre_compiled_lib, - PassResult::Expansion(eprog), - until, - result_check, - ) - } - PassResult::Expansion(eprog) => { - let nprog = naming::translate::program(compilation_env, pre_compiled_lib, eprog); - run( - compilation_env, - pre_compiled_lib, - PassResult::Naming(nprog), - until, - result_check, - ) - } - PassResult::Naming(nprog) => { - let tprog = typing::translate::program(compilation_env, pre_compiled_lib, nprog); - run( - compilation_env, - pre_compiled_lib, - PassResult::Typing(tprog), - until, - result_check, - ) - } - PassResult::Typing(tprog) => { - compilation_env.check_diags_at_or_above_severity(Severity::BlockingError)?; - let hprog = hlir::translate::program(compilation_env, pre_compiled_lib, tprog); - run( - compilation_env, - pre_compiled_lib, - PassResult::HLIR(hprog), - until, - result_check, - ) - } - PassResult::HLIR(hprog) => { - let cprog = cfgir::translate::program(compilation_env, pre_compiled_lib, hprog); - run( - compilation_env, - pre_compiled_lib, - PassResult::CFGIR(cprog), - until, - result_check, - ) + fn rec( + compilation_env: &mut CompilationEnv, + pre_compiled_lib: Option<&FullyCompiledProgram>, + cur: PassResult, + until: Pass, + mut result_check: impl FnMut(&PassResult, &CompilationEnv), + ) -> Result { + compilation_env.check_diags_at_or_above_severity(Severity::Bug)?; + assert!( + until <= PASS_COMPILATION, + "Invalid pass for run_to. Target is greater than maximum pass" + ); + result_check(&cur, compilation_env); + if cur.equivalent_pass() >= until { + return Ok(cur); } - PassResult::CFGIR(cprog) => { - // Don't generate bytecode if there are any errors - compilation_env.check_diags_at_or_above_severity(Severity::NonblockingError)?; - let compiled_units = - to_bytecode::translate::program(compilation_env, pre_compiled_lib, cprog); - // Report any errors from bytecode generation - compilation_env.check_diags_at_or_above_severity(Severity::NonblockingError)?; - let warnings = compilation_env.take_final_warning_diags(); - assert!(until == PASS_COMPILATION); - run( - compilation_env, - pre_compiled_lib, - PassResult::Compilation(compiled_units, warnings), - PASS_COMPILATION, - result_check, - ) + + match cur { + PassResult::Parser(prog) => { + let eprog = { + let prog = unit_test::filter_test_members::program(compilation_env, prog); + let prog = verification_attribute_filter::program(compilation_env, prog); + expansion::translate::program(compilation_env, pre_compiled_lib, prog) + }; + rec( + compilation_env, + pre_compiled_lib, + PassResult::Expansion(eprog), + until, + result_check, + ) + } + PassResult::Expansion(eprog) => { + let nprog = naming::translate::program(compilation_env, pre_compiled_lib, eprog); + rec( + compilation_env, + pre_compiled_lib, + PassResult::Naming(nprog), + until, + result_check, + ) + } + PassResult::Naming(nprog) => { + let tprog = typing::translate::program(compilation_env, pre_compiled_lib, nprog); + rec( + compilation_env, + pre_compiled_lib, + PassResult::Typing(tprog), + until, + result_check, + ) + } + PassResult::Typing(tprog) => { + compilation_env.check_diags_at_or_above_severity(Severity::BlockingError)?; + let hprog = hlir::translate::program(compilation_env, pre_compiled_lib, tprog); + rec( + compilation_env, + pre_compiled_lib, + PassResult::HLIR(hprog), + until, + result_check, + ) + } + PassResult::HLIR(hprog) => { + let cprog = cfgir::translate::program(compilation_env, pre_compiled_lib, hprog); + rec( + compilation_env, + pre_compiled_lib, + PassResult::CFGIR(cprog), + until, + result_check, + ) + } + PassResult::CFGIR(cprog) => { + // Don't generate bytecode if there are any errors + compilation_env.check_diags_at_or_above_severity(Severity::NonblockingError)?; + let compiled_units = + to_bytecode::translate::program(compilation_env, pre_compiled_lib, cprog); + // Report any errors from bytecode generation + compilation_env.check_diags_at_or_above_severity(Severity::NonblockingError)?; + let warnings = compilation_env.take_final_warning_diags(); + assert!(until == PASS_COMPILATION); + rec( + compilation_env, + pre_compiled_lib, + PassResult::Compilation(compiled_units, warnings), + PASS_COMPILATION, + result_check, + ) + } + PassResult::Compilation(_, _) => unreachable!("ICE Pass::Compilation is >= all passes"), } - PassResult::Compilation(_, _) => unreachable!("ICE Pass::Compilation is >= all passes"), } + with_large_stack!(rec( + compilation_env, + pre_compiled_lib, + cur, + until, + result_check + )) } //************************************************************************************************** diff --git a/external-crates/move/crates/move-compiler/src/diagnostics/codes.rs b/external-crates/move/crates/move-compiler/src/diagnostics/codes.rs index b1c1f48544c47..b8425d8a4cac3 100644 --- a/external-crates/move/crates/move-compiler/src/diagnostics/codes.rs +++ b/external-crates/move/crates/move-compiler/src/diagnostics/codes.rs @@ -181,6 +181,7 @@ codes!( InvalidRestrictedIdentifier: { msg: "invalid identifier escape", severity: NonblockingError }, InvalidMoveOrCopy: { msg: "invalid 'move' or 'copy'", severity: NonblockingError }, + InvalidLabel: { msg: "invalid expression label", severity: NonblockingError }, ], // errors for any rules around declaration items Declarations: [ @@ -224,6 +225,9 @@ codes!( UnboundMacro: { msg: "unbound macro", severity: BlockingError }, PositionalCallMismatch: { msg: "positional call mismatch", severity: NonblockingError }, InvalidLabel: { msg: "invalid use of label", severity: BlockingError }, + UnboundLabel: { msg: "unbound label", severity: BlockingError }, + InvalidMut: { msg: "invalid 'mut' declaration", severity: NonblockingError }, + InvalidMacroParameter: { msg: "invalid macro parameter", severity: NonblockingError }, ], // errors for typing rules. mostly typing/translate TypeSafety: [ @@ -262,6 +266,10 @@ codes!( InvalidCopyOp: { msg: "invalid 'copy' usage", severity: NonblockingError }, InvalidMoveOp: { msg: "invalid 'move' usage", severity: NonblockingError }, ImplicitConstantCopy: { msg: "implicit copy of a constant", severity: Warning }, + InvalidCallTarget: { msg: "invalid function call", severity: BlockingError }, + UnexpectedFunctionType: { msg: "invalid usage of lambda type", severity: BlockingError }, + UnexpectedLambda: { msg: "invalid usage of lambda", severity: BlockingError }, + CannotExpandMacro: { msg: "unable to expand macro function", severity: BlockingError }, ], // errors for ability rules. mostly typing/translate AbilitySafety: [ @@ -322,6 +330,7 @@ codes!( Bug: [ BytecodeGeneration: { msg: "BYTECODE GENERATION FAILED", severity: Bug }, BytecodeVerification: { msg: "BYTECODE VERIFICATION FAILED", severity: Bug }, + ICE: { msg: "INTERNAL COMPILER ERROR", severity: Bug }, ], Editions: [ FeatureTooNew: { diff --git a/external-crates/move/crates/move-compiler/src/diagnostics/mod.rs b/external-crates/move/crates/move-compiler/src/diagnostics/mod.rs index 26c016d1804eb..92208387f9b37 100644 --- a/external-crates/move/crates/move-compiler/src/diagnostics/mod.rs +++ b/external-crates/move/crates/move-compiler/src/diagnostics/mod.rs @@ -283,6 +283,7 @@ impl Diagnostics { pub fn max_severity(&self) -> Option { let Self(Some(inner)) = self else { return None }; + // map would be empty at the severity, so it should never be zero debug_assert!(inner.severity_count.values().all(|count| *count > 0)); inner .severity_count @@ -291,6 +292,18 @@ impl Diagnostics { .map(|(sev, _count)| *sev) } + pub fn count_diags_at_or_above_severity(&self, threshold: Severity) -> usize { + let Self(Some(inner)) = self else { return 0 }; + // map would be empty at the severity, so it should never be zero + debug_assert!(inner.severity_count.values().all(|count| *count > 0)); + inner + .severity_count + .iter() + .filter(|(sev, _count)| **sev >= threshold) + .map(|(_sev, count)| *count) + .sum() + } + pub fn is_empty(&self) -> bool { let Self(Some(inner)) = self else { return true }; inner.diagnostics.is_empty() @@ -500,6 +513,31 @@ macro_rules! diag { }}; } +#[macro_export] +macro_rules! ice { + ($primary: expr $(,)?) => {{ + $crate::diagnostics::print_stack_trace(); + diag!($crate::diagnostics::codes::Bug::ICE, $primary) + }}; + ($primary: expr, $($secondary: expr),+ $(,)?) => {{ + $crate::diagnostics::print_stack_trace(); + diag!($crate::diagnostics::codes::Bug::ICE, $primary, $($secondary, )*) + }} +} + +#[allow(clippy::wildcard_in_or_patterns)] +pub fn print_stack_trace() { + use std::backtrace::{Backtrace, BacktraceStatus}; + let stacktrace = Backtrace::capture(); + match stacktrace.status() { + BacktraceStatus::Captured => { + eprintln!("stacktrace:"); + eprintln!("{}", stacktrace); + } + BacktraceStatus::Unsupported | BacktraceStatus::Disabled | _ => (), + } +} + impl WarningFilters { pub fn new_for_source() -> Self { Self { diff --git a/external-crates/move/crates/move-compiler/src/editions/mod.rs b/external-crates/move/crates/move-compiler/src/editions/mod.rs index 5a51092bee2e7..d19929f833945 100644 --- a/external-crates/move/crates/move-compiler/src/editions/mod.rs +++ b/external-crates/move/crates/move-compiler/src/editions/mod.rs @@ -38,6 +38,7 @@ pub enum FeatureGate { Move2024Keywords, BlockLabels, Move2024Paths, + MacroFuns, Move2024Migration, } @@ -116,6 +117,7 @@ const E2024_ALPHA_FEATURES: &[FeatureGate] = &[ FeatureGate::Move2024Keywords, FeatureGate::BlockLabels, FeatureGate::Move2024Paths, + FeatureGate::MacroFuns, ]; const E2024_MIGRATION_FEATURES: &[FeatureGate] = &[FeatureGate::Move2024Migration]; @@ -208,6 +210,7 @@ impl FeatureGate { FeatureGate::Move2024Keywords => "Move 2024 keywords are", FeatureGate::BlockLabels => "Block labels are", FeatureGate::Move2024Paths => "Move 2024 paths are", + FeatureGate::MacroFuns => "'macro' functions are", FeatureGate::Move2024Migration => "Move 2024 migration is", } } diff --git a/external-crates/move/crates/move-compiler/src/expansion/ast.rs b/external-crates/move/crates/move-compiler/src/expansion/ast.rs index d60583be4dbcf..0acf32464e940 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/ast.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/ast.rs @@ -7,6 +7,7 @@ use crate::{ parser::ast::{ self as P, Ability, Ability_, BinOp, BlockLabel, ConstantName, Field, FunctionName, ModuleName, Mutability, QuantKind, StructName, UnaryOp, Var, ENTRY_MODIFIER, + MACRO_MODIFIER, NATIVE_MODIFIER, }, shared::{ ast_debug::*, known_attributes::KnownAttribute, unique_map::UniqueMap, @@ -209,6 +210,7 @@ pub struct Function { pub loc: Loc, pub visibility: Visibility, pub entry: Option, + pub macro_: Option, pub signature: FunctionSignature, pub body: FunctionBody, } @@ -279,6 +281,9 @@ pub type LValueWithRange = Spanned; pub type LValueWithRangeList_ = Vec; pub type LValueWithRangeList = Spanned; +pub type LambdaLValues_ = Vec<(LValueList, Option)>; +pub type LambdaLValues = Spanned; + #[derive(Debug, Clone, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum ExpDotted_ { @@ -329,20 +334,25 @@ pub enum Exp_ { Name(ModuleAccess, Option>), Call( ModuleAccess, - /* is_macro */ bool, + /* is_macro */ Option, + Option>, + Spanned>, + ), + MethodCall( + Box, + Name, + /* is_macro */ Option, Option>, Spanned>, ), - MethodCall(Box, Name, Option>, Spanned>), Pack(ModuleAccess, Option>, Fields), Vector(Loc, Option>, Spanned>), IfElse(Box, Box, Box), - While(Box, Option, Box), + While(Option, Box, Box), Loop(Option, Box), - NamedBlock(BlockLabel, Sequence), - Block(Sequence), - Lambda(LValueList, Box), // spec only + Block(Option, Sequence), + Lambda(LambdaLValues, Option, Box), Quant( QuantKind, LValueWithRangeList, @@ -381,9 +391,9 @@ pub type Exp = Spanned; pub type Sequence = (UseFuns, VecDeque); #[derive(Debug, Clone, PartialEq)] pub enum SequenceItem_ { - Seq(Exp), + Seq(Box), Declare(LValueList, Option), - Bind(LValueList, Exp), + Bind(LValueList, Box), } pub type SequenceItem = Spanned; @@ -590,6 +600,8 @@ impl AbilitySet { pub const SIGNER: [Ability_; 1] = [Ability_::Drop]; /// Abilities for vector<_>, note they are predicated on the type argument pub const COLLECTION: [Ability_; 3] = [Ability_::Copy, Ability_::Drop, Ability_::Store]; + /// Abilities for functions + pub const FUNCTIONS: [Ability_; 0] = []; pub fn empty() -> Self { AbilitySet(UniqueSet::new()) @@ -655,23 +667,27 @@ impl AbilitySet { } pub fn all(loc: Loc) -> Self { - Self::from_abilities_(loc, Self::ALL.to_vec()).unwrap() + Self::from_abilities_(loc, Self::ALL).unwrap() } pub fn primitives(loc: Loc) -> Self { - Self::from_abilities_(loc, Self::PRIMITIVES.to_vec()).unwrap() + Self::from_abilities_(loc, Self::PRIMITIVES).unwrap() } pub fn references(loc: Loc) -> Self { - Self::from_abilities_(loc, Self::REFERENCES.to_vec()).unwrap() + Self::from_abilities_(loc, Self::REFERENCES).unwrap() } pub fn signer(loc: Loc) -> Self { - Self::from_abilities_(loc, Self::SIGNER.to_vec()).unwrap() + Self::from_abilities_(loc, Self::SIGNER).unwrap() } pub fn collection(loc: Loc) -> Self { - Self::from_abilities_(loc, Self::COLLECTION.to_vec()).unwrap() + Self::from_abilities_(loc, Self::COLLECTION).unwrap() + } + + pub fn functions(loc: Loc) -> Self { + Self::from_abilities_(loc, Self::COLLECTION).unwrap() } } @@ -1074,6 +1090,7 @@ impl AstDebug for (FunctionName, &Function) { loc: _loc, visibility, entry, + macro_, signature, body, warning_filter, @@ -1085,8 +1102,11 @@ impl AstDebug for (FunctionName, &Function) { if entry.is_some() { w.write(&format!("{} ", ENTRY_MODIFIER)); } + if macro_.is_some() { + w.write(&format!("{} ", MACRO_MODIFIER)); + } if let FunctionBody_::Native = &body.value { - w.write("native "); + w.write(&format!("{} ", NATIVE_MODIFIER)); } w.write(&format!("fun#{index} {name}")); signature.ast_debug(w); @@ -1319,7 +1339,7 @@ impl AstDebug for Exp_ { } E::Call(ma, is_macro, tys_opt, sp!(_, rhs)) => { ma.ast_debug(w); - if *is_macro { + if is_macro.is_some() { w.write("!"); } if let Some(ss) = tys_opt { @@ -1331,9 +1351,12 @@ impl AstDebug for Exp_ { w.comma(rhs, |w, e| e.ast_debug(w)); w.write(")"); } - E::MethodCall(e, f, tys_opt, sp!(_, rhs)) => { + E::MethodCall(e, f, is_macro, tys_opt, sp!(_, rhs)) => { e.ast_debug(w); w.write(&format!(".{}", f)); + if is_macro.is_some() { + w.write("!"); + } if let Some(ss) = tys_opt { w.write("<"); ss.ast_debug(w); @@ -1377,27 +1400,28 @@ impl AstDebug for Exp_ { w.write(" else "); f.ast_debug(w); } - E::While(b, name, e) => { + E::While(name, b, e) => { + name.map(|name| w.write(format!("'{}: ", name))); w.write("while ("); b.ast_debug(w); w.write(")"); - name.map(|name| w.write(format!(" '{}: ", name))); e.ast_debug(w); } E::Loop(name, e) => { + name.map(|name| w.write(format!("'{}: ", name))); w.write("loop "); - name.map(|name| w.write(format!(" '{}: ", name))); e.ast_debug(w); } - E::NamedBlock(name, seq) => { - w.write(format!("'{}: ", name)); + E::Block(name, seq) => { + name.map(|name| w.write(format!("'{}: ", name))); seq.ast_debug(w); } - E::Block(seq) => seq.ast_debug(w), - E::Lambda(sp!(_, bs), e) => { - w.write("fun "); + E::Lambda(sp!(_, bs), ty_opt, e) => { bs.ast_debug(w); - w.write(" "); + if let Some(ty) = ty_opt { + w.write(" -> "); + ty.ast_debug(w); + } e.ast_debug(w); } E::Quant(kind, sp!(_, rs), trs, c_opt, e) => { @@ -1581,6 +1605,20 @@ impl AstDebug for (LValue, Exp) { } } +impl AstDebug for LambdaLValues_ { + fn ast_debug(&self, w: &mut AstWriter) { + w.write("|"); + w.comma(self, |w, (lv, ty_opt)| { + lv.ast_debug(w); + if let Some(ty) = ty_opt { + w.write(": "); + ty.ast_debug(w); + } + }); + w.write("| "); + } +} + impl AstDebug for Vec> { fn ast_debug(&self, w: &mut AstWriter) { for trigger in self { diff --git a/external-crates/move/crates/move-compiler/src/expansion/translate.rs b/external-crates/move/crates/move-compiler/src/expansion/translate.rs index 409aec83e1526..8b0bbb88443bd 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/translate.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/translate.rs @@ -16,7 +16,7 @@ use crate::{ }, parser::ast::{ self as P, Ability, BlockLabel, ConstantName, Field, FieldBindings, FunctionName, - ModuleName, Mutability, StructName, Var, + ModuleName, Mutability, StructName, Var, ENTRY_MODIFIER, MACRO_MODIFIER, NATIVE_MODIFIER, }, shared::{known_attributes::AttributePosition, unique_map::UniqueMap, *}, FullyCompiledProgram, @@ -652,7 +652,7 @@ fn module_( P::ModuleMember::Use(_) => unreachable!(), P::ModuleMember::Friend(f) => friend(context, &mut friends, f), P::ModuleMember::Function(mut f) => { - if !context.is_source_definition { + if !context.is_source_definition && f.macro_.is_none() { f.body.value = P::FunctionBody_::Native } function( @@ -2391,7 +2391,7 @@ fn constant_( .env() .add_warning_filter_scope(warning_filter.clone()); let signature = type_(context, psignature); - let value = exp_(context, pvalue); + let value = *exp(context, Box::new(pvalue)); let constant = E::Constant { warning_filter, index, @@ -2432,6 +2432,7 @@ fn function_( name, visibility: pvisibility, entry, + macro_, signature: psignature, body: pbody, } = pfunction; @@ -2440,8 +2441,39 @@ fn function_( context .env() .add_warning_filter_scope(warning_filter.clone()); + if let (Some(entry_loc), Some(macro_loc)) = (entry, macro_) { + let e_msg = format!( + "Invalid function declaration. \ + It is meaningless for '{MACRO_MODIFIER}' functions to be '{ENTRY_MODIFIER}' since they \ + are fully-expanded inline during compilation" + ); + let m_msg = format!("Function declared as '{MACRO_MODIFIER}' here"); + context.env().add_diag(diag!( + Declarations::InvalidFunction, + (entry_loc, e_msg), + (macro_loc, m_msg), + )); + } + if let (Some(macro_loc), sp!(native_loc, P::FunctionBody_::Native)) = (macro_, &pbody) { + let n_msg = format!( + "Invalid function declaration. \ + '{NATIVE_MODIFIER}' functions cannot be '{MACRO_MODIFIER}'", + ); + let m_msg = format!("Function declared as '{MACRO_MODIFIER}' here"); + context.env().add_diag(diag!( + Declarations::InvalidFunction, + (*native_loc, n_msg), + (macro_loc, m_msg), + )); + } + if let Some(macro_loc) = macro_ { + let current_package = context.current_package; + context + .env() + .check_feature(FeatureGate::MacroFuns, current_package, macro_loc); + } let visibility = visibility(pvisibility); - let signature = function_signature(context, psignature); + let signature = function_signature(context, macro_, psignature); let body = function_body(context, pbody); if let Some((m, use_funs_builder)) = module_and_use_funs { let implicit = E::ImplicitUseFunCandidate { @@ -2462,6 +2494,7 @@ fn function_( loc, visibility, entry, + macro_, signature, body, }; @@ -2481,6 +2514,7 @@ fn visibility(pvisibility: P::Visibility) -> E::Visibility { fn function_signature( context: &mut Context, + is_macro: Option, psignature: P::FunctionSignature, ) -> E::FunctionSignature { let P::FunctionSignature { @@ -2488,14 +2522,14 @@ fn function_signature( parameters: pparams, return_type: pret_ty, } = psignature; - let type_parameters = type_parameters(context, pty_params); + let type_parameters = function_type_parameters(context, is_macro, pty_params); context.push_type_parameters(type_parameters.iter().map(|(name, _)| name)); let parameters = pparams .into_iter() .map(|(pmut, v, t)| (mutability(context, v.loc(), pmut), v, type_(context, t))) .collect::>(); for (_, v, _) in ¶meters { - check_valid_local_name(context, v) + check_valid_function_parameter_name(context, is_macro, v) } let return_type = type_(context, pret_ty); E::FunctionSignature { @@ -2534,14 +2568,16 @@ fn ability_set(context: &mut Context, case: &str, abilities_vec: Vec) - set } -fn type_parameters( +fn function_type_parameters( context: &mut Context, + is_macro: Option, pty_params: Vec<(Name, Vec)>, ) -> Vec<(Name, E::AbilitySet)> { pty_params .into_iter() .map(|(name, constraints_vec)| { let constraints = ability_set(context, "constraint", constraints_vec); + let _ = check_valid_type_parameter_name(context, is_macro, &name); (name, constraints) }) .collect() @@ -2553,10 +2589,13 @@ fn struct_type_parameters( ) -> Vec { pty_params .into_iter() - .map(|param| E::StructTypeParameter { - is_phantom: param.is_phantom, - name: param.name, - constraints: ability_set(context, "constraint", param.constraints), + .map(|param| { + let _ = check_valid_type_parameter_name(context, None, ¶m.name); + E::StructTypeParameter { + is_phantom: param.is_phantom, + name: param.name, + constraints: ability_set(context, "constraint", param.constraints), + } }) .collect() } @@ -2578,10 +2617,10 @@ fn type_(context: &mut Context, sp!(loc, pt_): P::Type) -> E::Type { } } PT::Ref(mut_, inner) => ET::Ref(mut_, Box::new(type_(context, *inner))), - PT::Fun(_, _) => { - // TODO these will be used later by macros - context.spec_deprecated(loc, /* is_error */ true); - ET::UnresolvedError + PT::Fun(args, result) => { + let args = types(context, args); + let result = type_(context, *result); + ET::Fun(args, Box::new(result)) } }; sp(loc, t_) @@ -2609,14 +2648,14 @@ fn sequence(context: &mut Context, loc: Loc, seq: P::Sequence) -> E::Sequence { .into_iter() .map(|item| sequence_item(context, item)) .collect(); - let final_e_opt = pfinal_item.map(|item| exp_(context, item)); + let final_e_opt = pfinal_item.map(|item| exp(context, Box::new(item))); let final_e = match final_e_opt { None => { let last_semicolon_loc = match maybe_last_semicolon_loc { Some(l) => l, None => loc, }; - sp(last_semicolon_loc, E::Exp_::Unit { trailing: true }) + Box::new(sp(last_semicolon_loc, E::Exp_::Unit { trailing: true })) } Some(e) => e, }; @@ -2630,14 +2669,14 @@ fn sequence_item(context: &mut Context, sp!(loc, pitem_): P::SequenceItem) -> E: use E::SequenceItem_ as ES; use P::SequenceItem_ as PS; let item_ = match pitem_ { - PS::Seq(e) => ES::Seq(exp_(context, *e)), + PS::Seq(e) => ES::Seq(exp(context, e)), PS::Declare(pb, pty_opt) => { let b_opt = bind_list(context, pb); let ty_opt = pty_opt.map(|t| type_(context, t)); match b_opt { None => { assert!(context.env().has_errors()); - ES::Seq(sp(loc, E::Exp_::UnresolvedError)) + ES::Seq(Box::new(sp(loc, E::Exp_::UnresolvedError))) } Some(b) => ES::Declare(b, ty_opt), } @@ -2645,15 +2684,15 @@ fn sequence_item(context: &mut Context, sp!(loc, pitem_): P::SequenceItem) -> E: PS::Bind(pb, pty_opt, pe) => { let b_opt = bind_list(context, pb); let ty_opt = pty_opt.map(|t| type_(context, t)); - let e_ = exp_(context, *pe); + let e_ = exp(context, pe); let e = match ty_opt { None => e_, - Some(ty) => sp(e_.loc, E::Exp_::Annotate(Box::new(e_), ty)), + Some(ty) => Box::new(sp(e_.loc, E::Exp_::Annotate(e_, ty))), }; match b_opt { None => { assert!(context.env().has_errors()); - ES::Seq(sp(loc, E::Exp_::UnresolvedError)) + ES::Seq(Box::new(sp(loc, E::Exp_::UnresolvedError))) } Some(b) => ES::Bind(b, e), } @@ -2663,16 +2702,15 @@ fn sequence_item(context: &mut Context, sp!(loc, pitem_): P::SequenceItem) -> E: } fn exps(context: &mut Context, pes: Vec) -> Vec { - pes.into_iter().map(|pe| exp_(context, pe)).collect() -} - -fn exp(context: &mut Context, pe: P::Exp) -> Box { - Box::new(exp_(context, pe)) + pes.into_iter() + .map(|pe| *exp(context, Box::new(pe))) + .collect() } -fn exp_(context: &mut Context, sp!(loc, pe_): P::Exp) -> E::Exp { +fn exp(context: &mut Context, pe: Box) -> Box { use E::Exp_ as EE; use P::Exp_ as PE; + let sp!(loc, pe_) = *pe; let e_ = match pe_ { PE::Unit => EE::Unit { trailing: false }, PE::Value(pv) => match value(&mut context.defn_context, pv) { @@ -2718,7 +2756,7 @@ fn exp_(context: &mut Context, sp!(loc, pe_): P::Exp) -> E::Exp { let tys_opt = optional_types(context, ptys_opt); let efields_vec = pfields .into_iter() - .map(|(f, pe)| (f, exp_(context, pe))) + .map(|(f, pe)| (f, *exp(context, Box::new(pe)))) .collect(); let efields = named_fields(context, loc, "construction", "argument", efields_vec); match en_opt { @@ -2735,26 +2773,33 @@ fn exp_(context: &mut Context, sp!(loc, pe_): P::Exp) -> E::Exp { EE::Vector(vec_loc, tys_opt, args) } PE::IfElse(pb, pt, pf_opt) => { - let eb = exp(context, *pb); - let et = exp(context, *pt); + let eb = exp(context, pb); + let et = exp(context, pt); let ef = match pf_opt { None => Box::new(sp(loc, EE::Unit { trailing: false })), - Some(pf) => exp(context, *pf), + Some(pf) => exp(context, pf), }; EE::IfElse(eb, et, ef) } - PE::While(pb, ploop) => { - let (name, body) = maybe_named_loop(context, loc, *ploop); - EE::While(exp(context, *pb), name, body) + PE::Labled(name, pe) => { + let e = exp(context, pe); + return maybe_labeled_exp(context, loc, name, e); } - PE::Loop(ploop) => { - let (name, body) = maybe_named_loop(context, loc, *ploop); - EE::Loop(name, body) + PE::While(pb, ploop) => EE::While(None, exp(context, pb), exp(context, ploop)), + PE::Loop(ploop) => EE::Loop(None, exp(context, ploop)), + PE::Block(seq) => EE::Block(None, sequence(context, loc, seq)), + PE::Lambda(plambda, pty_opt, pe) => { + let elambda_opt = lambda_bind_list(context, plambda); + let ty_opt = pty_opt.map(|t| type_(context, t)); + match elambda_opt { + Some(elambda) => EE::Lambda(elambda, ty_opt, exp(context, pe)), + None => { + assert!(context.env().has_errors()); + EE::UnresolvedError + } + } } - PE::NamedBlock(name, seq) => EE::NamedBlock(name, sequence(context, loc, seq)), - PE::Block(seq) => EE::Block(sequence(context, loc, seq)), - PE::Lambda(..) | PE::Quant(..) => { - // TODO lambdas will be used later by macros + PE::Quant(..) => { context.spec_deprecated(loc, /* is_error */ true); EE::UnresolvedError } @@ -2764,8 +2809,8 @@ fn exp_(context: &mut Context, sp!(loc, pe_): P::Exp) -> E::Exp { } PE::Assign(lvalue, rhs) => { - let l_opt = lvalues(context, *lvalue); - let er = exp(context, *rhs); + let l_opt = lvalues(context, lvalue); + let er = exp(context, rhs); match l_opt { None => { assert!(context.env().has_errors()); @@ -2776,24 +2821,24 @@ fn exp_(context: &mut Context, sp!(loc, pe_): P::Exp) -> E::Exp { Some(LValue::FieldMutate(edotted)) => EE::FieldMutate(edotted, er), } } - PE::Abort(pe) => EE::Abort(exp(context, *pe)), + PE::Abort(pe) => EE::Abort(exp(context, pe)), PE::Return(name_opt, pe_opt) => { let ev = match pe_opt { None => Box::new(sp(loc, EE::Unit { trailing: false })), - Some(pe) => exp(context, *pe), + Some(pe) => exp(context, pe), }; EE::Return(name_opt, ev) } PE::Break(name_opt, pe_opt) => { let ev = match pe_opt { None => Box::new(sp(loc, EE::Unit { trailing: false })), - Some(pe) => exp(context, *pe), + Some(pe) => exp(context, pe), }; EE::Break(name_opt, ev) } PE::Continue(name) => EE::Continue(name), - PE::Dereference(pe) => EE::Dereference(exp(context, *pe)), - PE::UnaryExp(op, pe) => EE::UnaryExp(op, exp(context, *pe)), + PE::Dereference(pe) => EE::Dereference(exp(context, pe)), + PE::UnaryExp(op, pe) => EE::UnaryExp(op, exp(context, pe)), PE::BinopExp(_pl, op, _pr) if op.value.is_spec_only() => { context.spec_deprecated(loc, /* is_error */ true); EE::UnresolvedError @@ -2806,7 +2851,7 @@ fn exp_(context: &mut Context, sp!(loc, pe_): P::Exp) -> E::Exp { e, *e, sp!(loc, PE::BinopExp(lhs, op, rhs)) => { (lhs, (op, loc), rhs) }, - { exp(context, *e) }, + { exp(context, e) }, value_stack, (bop, loc) => { let el = value_stack.pop().expect("ICE binop expansion issue"); @@ -2816,65 +2861,98 @@ fn exp_(context: &mut Context, sp!(loc, pe_): P::Exp) -> E::Exp { ) .value } - PE::Move(loc, pdotted) => move_or_copy_path(context, PathCase::Move(loc), *pdotted), - PE::Copy(loc, pdotted) => move_or_copy_path(context, PathCase::Copy(loc), *pdotted), - PE::Borrow(mut_, pdotted) => match exp_dotted(context, *pdotted) { - Some(edotted) => EE::ExpDotted(E::DottedUsage::Borrow(mut_), Box::new(edotted)), + PE::Move(loc, pdotted) => move_or_copy_path(context, PathCase::Move(loc), pdotted), + PE::Copy(loc, pdotted) => move_or_copy_path(context, PathCase::Copy(loc), pdotted), + PE::Borrow(mut_, pdotted) => match exp_dotted(context, pdotted) { + Some(edotted) => EE::ExpDotted(E::DottedUsage::Borrow(mut_), edotted), None => { assert!(context.env().has_errors()); EE::UnresolvedError } }, - pdotted_ @ PE::Dot(_, _) => match exp_dotted(context, sp(loc, pdotted_)) { - Some(edotted) => EE::ExpDotted(E::DottedUsage::Use, Box::new(edotted)), + pdotted_ @ PE::Dot(_, _) => match exp_dotted(context, Box::new(sp(loc, pdotted_))) { + Some(edotted) => EE::ExpDotted(E::DottedUsage::Use, edotted), None => { assert!(context.env().has_errors()); EE::UnresolvedError } }, - PE::DotCall(pdotted, n, ptys_opt, sp!(rloc, prs)) => match exp_dotted(context, *pdotted) { - Some(edotted) => { - let pkg = context.current_package; - context.env().check_feature(FeatureGate::DotCall, pkg, loc); - let tys_opt = optional_types(context, ptys_opt); - let ers = sp(rloc, exps(context, prs)); - EE::MethodCall(Box::new(edotted), n, tys_opt, ers) - } - None => { - assert!(context.env().has_errors()); - EE::UnresolvedError + PE::DotCall(pdotted, n, is_macro, ptys_opt, sp!(rloc, prs)) => { + match exp_dotted(context, pdotted) { + Some(edotted) => { + let pkg = context.current_package; + context.env().check_feature(FeatureGate::DotCall, pkg, loc); + let tys_opt = optional_types(context, ptys_opt); + let ers = sp(rloc, exps(context, prs)); + EE::MethodCall(edotted, n, is_macro, tys_opt, ers) + } + None => { + assert!(context.env().has_errors()); + EE::UnresolvedError + } } - }, - PE::Cast(e, ty) => EE::Cast(exp(context, *e), type_(context, ty)), + } + PE::Cast(e, ty) => EE::Cast(exp(context, e), type_(context, ty)), PE::Index(..) => { // TODO index syntax will be added context.spec_deprecated(loc, /* is_error */ true); EE::UnresolvedError } - PE::Annotate(e, ty) => EE::Annotate(exp(context, *e), type_(context, ty)), + PE::Annotate(e, ty) => EE::Annotate(exp(context, e), type_(context, ty)), PE::Spec(_) => { context.spec_deprecated(loc, /* is_error */ false); EE::Unit { trailing: false } } PE::UnresolvedError => EE::UnresolvedError, }; - sp(loc, e_) + Box::new(sp(loc, e_)) } -// If the expression is a named block, hand back the name and a normal block. Otherwise, just -// process the expression. This is used to lift names for loop and while to the appropriate form. -fn maybe_named_loop( +// If the expression can take a label, attach the label. Otherwise error +fn maybe_labeled_exp( context: &mut Context, loc: Loc, - body: P::Exp, -) -> (Option, Box) { - if let P::Exp_::NamedBlock(name, seq) = body.value { - ( - Some(name), - Box::new(sp(loc, E::Exp_::Block(sequence(context, loc, seq)))), - ) - } else { - (None, exp(context, body)) + label: BlockLabel, + e: Box, +) -> Box { + let sp!(_eloc, e_) = *e; + let e_ = match e_ { + E::Exp_::While(label_opt, cond, body) => { + ensure_unique_label(context, loc, &label, label_opt); + E::Exp_::While(Some(label), cond, body) + } + E::Exp_::Loop(label_opt, body) => { + ensure_unique_label(context, loc, &label, label_opt); + E::Exp_::Loop(Some(label), body) + } + E::Exp_::Block(label_opt, seq) => { + ensure_unique_label(context, loc, &label, label_opt); + E::Exp_::Block(Some(label), seq) + } + _ => { + let msg = "Invalid label. Labels can only be used on 'while', 'loop', or block '{{}}' \ + expressions"; + context + .env() + .add_diag(diag!(Syntax::InvalidLabel, (loc, msg))); + E::Exp_::UnresolvedError + } + }; + Box::new(sp(loc, e_)) +} + +fn ensure_unique_label( + context: &mut Context, + loc: Loc, + _label: &BlockLabel, + label_opt: Option, +) { + if let Some(old_label) = label_opt { + context.env().add_diag(diag!( + Syntax::InvalidLabel, + (loc, "Multiple labels for a single expression"), + (old_label.0.loc, "Label previously given here"), + )); } } @@ -2898,7 +2976,7 @@ impl PathCase { } } -fn move_or_copy_path(context: &mut Context, case: PathCase, pe: P::Exp) -> E::Exp_ { +fn move_or_copy_path(context: &mut Context, case: PathCase, pe: Box) -> E::Exp_ { match move_or_copy_path_(context, case, pe) { Some(e) => e, None => { @@ -2908,7 +2986,7 @@ fn move_or_copy_path(context: &mut Context, case: PathCase, pe: P::Exp) -> E::Ex } } -fn move_or_copy_path_(context: &mut Context, case: PathCase, pe: P::Exp) -> Option { +fn move_or_copy_path_(context: &mut Context, case: PathCase, pe: Box) -> Option { let e = exp_dotted(context, pe)?; let cloc = case.loc(); match &e.value { @@ -2932,22 +3010,23 @@ fn move_or_copy_path_(context: &mut Context, case: PathCase, pe: P::Exp) -> Opti } } Some(match case { - PathCase::Move(loc) => E::Exp_::ExpDotted(E::DottedUsage::Move(loc), Box::new(e)), - PathCase::Copy(loc) => E::Exp_::ExpDotted(E::DottedUsage::Copy(loc), Box::new(e)), + PathCase::Move(loc) => E::Exp_::ExpDotted(E::DottedUsage::Move(loc), e), + PathCase::Copy(loc) => E::Exp_::ExpDotted(E::DottedUsage::Copy(loc), e), }) } -fn exp_dotted(context: &mut Context, sp!(loc, pdotted_): P::Exp) -> Option { +fn exp_dotted(context: &mut Context, pdotted: Box) -> Option> { use E::ExpDotted_ as EE; use P::Exp_ as PE; + let sp!(loc, pdotted_) = *pdotted; let edotted_ = match pdotted_ { PE::Dot(plhs, field) => { - let lhs = exp_dotted(context, *plhs)?; - EE::Dot(Box::new(lhs), field) + let lhs = exp_dotted(context, plhs)?; + EE::Dot(lhs, field) } - pe_ => EE::Exp(Box::new(exp_(context, sp(loc, pe_)))), + pe_ => EE::Exp(exp(context, Box::new(sp(loc, pe_)))), }; - Some(sp(loc, edotted_)) + Some(Box::new(sp(loc, edotted_))) } fn value(context: &mut DefnContext, sp!(loc, pvalue_): P::Value) -> Option { @@ -3118,15 +3197,31 @@ fn bind(context: &mut Context, sp!(loc, pb_): P::Bind) -> Option { Some(sp(loc, b_)) } +fn lambda_bind_list( + context: &mut Context, + sp!(loc, plambda): P::LambdaBindings, +) -> Option { + let elambda = plambda + .into_iter() + .map(|(pbs, ty_opt)| { + let bs = bind_list(context, pbs)?; + let ety = ty_opt.map(|t| type_(context, t)); + Some((bs, ety)) + }) + .collect::>()?; + Some(sp(loc, elambda)) +} + enum LValue { Assigns(E::LValueList), FieldMutate(Box), Mutate(Box), } -fn lvalues(context: &mut Context, sp!(loc, e_): P::Exp) -> Option { +fn lvalues(context: &mut Context, e: Box) -> Option { use LValue as L; use P::Exp_ as PE; + let sp!(loc, e_) = *e; let al: LValue = match e_ { PE::Unit => L::Assigns(sp(loc, vec![])), PE::ExpList(pes) => { @@ -3135,12 +3230,12 @@ fn lvalues(context: &mut Context, sp!(loc, e_): P::Exp) -> Option { L::Assigns(sp(loc, al_opt?)) } PE::Dereference(pr) => { - let er = exp(context, *pr); + let er = exp(context, pr); L::Mutate(er) } pdotted_ @ PE::Dot(_, _) => { - let dotted = exp_dotted(context, sp(loc, pdotted_))?; - L::FieldMutate(Box::new(dotted)) + let dotted = exp_dotted(context, Box::new(sp(loc, pdotted_)))?; + L::FieldMutate(dotted) } _ => L::Assigns(sp(loc, vec![assign(context, sp(loc, e_))?])), }; @@ -3191,7 +3286,7 @@ fn assign(context: &mut Context, sp!(loc, e_): P::Exp) -> Option { EL::Unpack(en, tys_opt, E::FieldBindings::Named(efields)), )) } - PE::Call(pn, false, ptys_opt, sp!(_, exprs)) => { + PE::Call(pn, None, ptys_opt, sp!(_, exprs)) => { let pkg = context.current_package; context .env() @@ -3263,19 +3358,60 @@ fn check_valid_address_name( use P::LeadingNameAccess_ as LN; match ln_ { LN::AnonymousAddress(_) => Ok(()), - LN::GlobalAddress(n) => check_restricted_name_all_cases(context, NameCase::Address, n), - LN::Name(n) => check_restricted_name_all_cases(context, NameCase::Address, n), + LN::GlobalAddress(n) | LN::Name(n) => { + check_restricted_name_all_cases(context, NameCase::Address, n) + } } } -fn check_valid_local_name(context: &mut Context, v: &Var) { - fn is_valid(s: Symbol) -> bool { - s.starts_with('_') || s.starts_with(|c: char| c.is_ascii_lowercase()) +fn check_valid_function_parameter_name(context: &mut Context, is_macro: Option, v: &Var) { + const SYNTAX_IDENTIFIER_NOTE: &str = + "'macro' parameters start with '$' to indicate that their arguments are not evaluated \ + before the macro is expanded, meaning the entire expression is substituted. \ + This is different from regular function parameters that are evaluated before the \ + function is called."; + let is_syntax_identifier = v.is_syntax_identifier(); + if let Some(macro_loc) = is_macro { + if !is_syntax_identifier && !v.is_underscore() { + let msg = format!( + "Invalid parameter name '{}'. '{}' parameter names must start with '$' (or must be '_')", + v, MACRO_MODIFIER, + ); + let macro_msg = format!("Declared '{}' here", MACRO_MODIFIER); + let mut diag = diag!( + Declarations::InvalidName, + (v.loc(), msg), + (macro_loc, macro_msg), + ); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + context.env().add_diag(diag); + } + } else if is_syntax_identifier { + let msg = format!( + "Invalid parameter name '{}'. Non-'{}' parameter names cannot start with '$'", + v, MACRO_MODIFIER, + ); + let mut diag = diag!(Declarations::InvalidName, (v.loc(), msg)); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + context.env().add_diag(diag); + } else if !is_valid_local_variable_name(v.value()) { + let msg = format!( + "Invalid parameter name '{}'. Local variable names must start with 'a'..'z' or \ + '_'", + v, + ); + context + .env() + .add_diag(diag!(Declarations::InvalidName, (v.loc(), msg))); } - if !is_valid(v.value()) { + let _ = check_restricted_name_all_cases(&mut context.defn_context, NameCase::Variable, &v.0); +} + +fn check_valid_local_name(context: &mut Context, v: &Var) { + if !is_valid_local_variable_name(v.value()) { let msg = format!( - "Invalid local variable name '{}'. Local variable names must start with 'a'..'z' (or \ - '_')", + "Invalid local variable name '{}'. Local variable names must start with 'a'..'z' or \ + '_'", v, ); context @@ -3285,6 +3421,10 @@ fn check_valid_local_name(context: &mut Context, v: &Var) { let _ = check_restricted_name_all_cases(&mut context.defn_context, NameCase::Variable, &v.0); } +fn is_valid_local_variable_name(s: Symbol) -> bool { + Var::is_valid_name(s) && !Var::is_syntax_identifier_name(s) +} + #[derive(Copy, Clone, Debug)] pub enum ModuleMemberKind { Constant, @@ -3315,6 +3455,7 @@ pub enum NameCase { ModuleAlias, Variable, Address, + TypeParameter, } impl NameCase { @@ -3332,6 +3473,7 @@ impl NameCase { NameCase::ModuleAlias => "module alias", NameCase::Variable => "variable", NameCase::Address => "address", + NameCase::TypeParameter => "type parameter", } } } @@ -3429,6 +3571,65 @@ fn check_valid_module_member_name_impl( Ok(()) } +fn check_valid_type_parameter_name( + context: &mut Context, + is_macro: Option, + n: &Name, +) -> Result<(), ()> { + const SYNTAX_IDENTIFIER_NOTE: &str = "Type parameter names starting with '$' indicate that \ + their arguments do not have to satisfy certain constraints before the macro is expanded, \ + meaning types like '&mut u64' or '(bool, u8)' may be used as arguments."; + + let is_syntax_ident = Var::is_syntax_identifier_name(n.value); + if let Some(macro_loc) = is_macro { + if !is_syntax_ident { + let msg = format!( + "Invalid type parameter name. \ + '{} fun' type parameter names must start with '$'", + MACRO_MODIFIER + ); + let macro_msg = format!("Declared '{}' here", MACRO_MODIFIER); + let mut diag = diag!( + Declarations::InvalidName, + (n.loc, msg), + (macro_loc, macro_msg), + ); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + context.env().add_diag(diag); + } + } else if is_syntax_ident { + let msg = format!( + "Invalid type parameter name. \ + Only '{} fun' type parameter names cat start with '$'", + MACRO_MODIFIER + ); + let mut diag = diag!(Declarations::InvalidName, (n.loc, msg)); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + context.env().add_diag(diag); + } + + // TODO move these names to a more central place? + if n.value == symbol!("_") { + let diag = restricted_name_error(NameCase::TypeParameter, n.loc, "_"); + context.env().add_diag(diag); + return Err(()); + } + check_restricted_names( + context, + NameCase::TypeParameter, + n, + crate::naming::ast::BuiltinFunction_::all_names(), + )?; + check_restricted_names( + context, + NameCase::TypeParameter, + n, + crate::naming::ast::BuiltinTypeName_::all_names(), + )?; + + check_restricted_name_all_cases(&mut context.defn_context, NameCase::TypeParameter, n) +} + pub fn is_valid_struct_constant_or_schema_name(s: &str) -> bool { s.starts_with(|c: char| c.is_ascii_uppercase()) } @@ -3440,6 +3641,31 @@ fn check_restricted_name_all_cases( case: NameCase, n: &Name, ) -> Result<(), ()> { + match case { + NameCase::Constant + | NameCase::Function + | NameCase::Struct + | NameCase::Schema + | NameCase::Module + | NameCase::ModuleMemberAlias(_) + | NameCase::ModuleAlias + | NameCase::Address => { + if Var::is_syntax_identifier_name(n.value) { + let msg = format!( + "Invalid {} name '{}'. Identifiers starting with '$' can be used only for \ + parameters and type paramters", + case.name(), + n, + ); + context + .env + .add_diag(diag!(Declarations::InvalidName, (n.loc, msg))); + return Err(()); + } + } + NameCase::Variable | NameCase::TypeParameter => (), + } + let n_str = n.value.as_str(); let can_be_vector = matches!(case, NameCase::Module | NameCase::ModuleAlias); if n_str == ModuleName::SELF_NAME diff --git a/external-crates/move/crates/move-compiler/src/hlir/ast.rs b/external-crates/move/crates/move-compiler/src/hlir/ast.rs index d9c2670796e72..cad3abb66eea5 100644 --- a/external-crates/move/crates/move-compiler/src/hlir/ast.rs +++ b/external-crates/move/crates/move-compiler/src/hlir/ast.rs @@ -372,7 +372,7 @@ impl Var { } pub fn starts_with_underscore(&self) -> bool { - self.0.value.starts_with('_') + self.0.value.starts_with('_') || self.0.value.starts_with("$_") } } diff --git a/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs b/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs index d00fbd01c0cca..148e3d79d81aa 100644 --- a/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs +++ b/external-crates/move/crates/move-compiler/src/hlir/detect_dead_code.rs @@ -536,7 +536,7 @@ fn statement(context: &mut Context, e: &T::Exp) -> Option { } } - E::While(test, _, body) => { + E::While(_, test, body) => { if let Some(test_control_flow) = value(context, test) { context.report_value_error(test_control_flow); already_reported(*eloc) diff --git a/external-crates/move/crates/move-compiler/src/hlir/translate.rs b/external-crates/move/crates/move-compiler/src/hlir/translate.rs index 0dca201e54c6e..94c7db117014c 100644 --- a/external-crates/move/crates/move-compiler/src/hlir/translate.rs +++ b/external-crates/move/crates/move-compiler/src/hlir/translate.rs @@ -48,7 +48,11 @@ fn translate_var(sp!(loc, v_): N::Var) -> H::Var { H::Var(sp(loc, s)) } -fn translate_block_label(N::BlockLabel(sp!(loc, v_)): N::BlockLabel) -> H::BlockLabel { +fn translate_block_label(lbl: N::BlockLabel) -> H::BlockLabel { + let N::BlockLabel { + label: sp!(loc, v_), + .. + } = lbl; let N::Var_ { name, id: depth, @@ -290,7 +294,13 @@ fn module( let structs = tstructs.map(|name, s| struct_def(context, name, s)); let constants = tconstants.map(|name, c| constant(context, name, c)); - let functions = tfunctions.map(|name, f| function(context, name, f)); + let functions = tfunctions.filter_map(|name, f| { + if f.macro_.is_none() { + Some(function(context, name, f)) + } else { + None + } + }); gen_unused_warnings(context, is_source_module, &structs); @@ -325,9 +335,11 @@ fn function(context: &mut Context, _name: FunctionName, f: T::Function) -> H::Fu attributes, visibility: evisibility, entry, + macro_, signature, body, } = f; + assert!(macro_.is_none(), "ICE macros filtered above"); context.env.add_warning_filter_scope(warning_filter.clone()); let signature = function_signature(context, signature); let body = function_body(context, &signature, body); @@ -378,6 +390,7 @@ fn function_body( let (locals, body) = function_body_defined(context, sig, loc, seq); HB::Defined { locals, body } } + TB::Macro => unreachable!("ICE macros filtered above"), }; sp(loc, b_) } @@ -540,7 +553,7 @@ fn base_type(context: &Context, sp!(loc, nb_): N::Type) -> H::BaseType { NT::Param(tp) => HB::Param(tp), NT::UnresolvedError => HB::UnresolvedError, NT::Anything => HB::Unreachable, - NT::Ref(_, _) | NT::Unit => { + NT::Ref(_, _) | NT::Unit | NT::Fun(_, _) => { panic!( "ICE type constraints failed {}:{}-{}", loc.file_hash(), @@ -848,7 +861,7 @@ fn value( // Expansion-y things // These could likely be discharged during expansion instead. // - E::Builtin(bt, arguments) if matches!(&*bt, sp!(_, T::BuiltinFunction_::Assert(false))) => { + E::Builtin(bt, arguments) if matches!(&*bt, sp!(_, T::BuiltinFunction_::Assert(None))) => { use T::ExpListItem as TI; let [cond_item, code_item]: [TI; 2] = match arguments.exp.value { E::ExpList(arg_list) => arg_list.try_into().unwrap(), @@ -874,7 +887,9 @@ fn value( )); unit_exp(eloc) } - E::Builtin(bt, arguments) if matches!(&*bt, sp!(_, T::BuiltinFunction_::Assert(true))) => { + E::Builtin(bt, arguments) + if matches!(&*bt, sp!(_, T::BuiltinFunction_::Assert(Some(_)))) => + { use T::ExpListItem as TI; let [cond_item, code_item]: [TI; 2] = match arguments.exp.value { E::ExpList(arg_list) => arg_list.try_into().unwrap(), @@ -1401,7 +1416,7 @@ fn statement(context: &mut Context, block: &mut Block, e: T::Exp) { }, )); } - E::While(test, name, body) => { + E::While(name, test, body) => { let mut cond_block = make_block!(); let cond_exp = value(context, &mut cond_block, Some(&tbool(eloc)), *test); let cond = (cond_block, Box::new(cond_exp)); diff --git a/external-crates/move/crates/move-compiler/src/lib.rs b/external-crates/move/crates/move-compiler/src/lib.rs index f1ccf3fdeb394..42983490714cf 100644 --- a/external-crates/move/crates/move-compiler/src/lib.rs +++ b/external-crates/move/crates/move-compiler/src/lib.rs @@ -2,7 +2,7 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 -#![forbid(unsafe_code)] +// #![forbid(unsafe_code)] #[macro_use(sp)] extern crate move_ir_types; @@ -10,6 +10,14 @@ extern crate move_ir_types; #[macro_use(symbol)] extern crate move_symbol_pool; +pub const STACK_LIMIT: usize = 42 * 1_000_000_000; + +macro_rules! with_large_stack { + ($e:expr) => { + stacker::grow($crate::STACK_LIMIT, || $e) + }; +} + pub mod cfgir; pub mod command_line; pub mod compiled_unit; diff --git a/external-crates/move/crates/move-compiler/src/naming/ast.rs b/external-crates/move/crates/move-compiler/src/naming/ast.rs index 52171ba98d96a..a70b556b0e988 100644 --- a/external-crates/move/crates/move-compiler/src/naming/ast.rs +++ b/external-crates/move/crates/move-compiler/src/naming/ast.rs @@ -10,8 +10,8 @@ use crate::{ Visibility, }, parser::ast::{ - Ability_, BinOp, ConstantName, Field, FunctionName, Mutability, StructName, UnaryOp, - ENTRY_MODIFIER, + self as P, Ability_, BinOp, ConstantName, Field, FunctionName, Mutability, StructName, + UnaryOp, ENTRY_MODIFIER, MACRO_MODIFIER, NATIVE_MODIFIER, }, shared::{ast_debug::*, program_info::NamingProgramInfo, unique_map::UniqueMap, *}, }; @@ -73,8 +73,12 @@ pub struct UseFun { // Mapping from type to their possible "methods" pub type ResolvedUseFuns = BTreeMap>; +// Color for scopes of use funs and variables +pub type Color = u16; + #[derive(Debug, Clone, Eq, PartialEq)] pub struct UseFuns { + pub color: Color, pub resolved: ResolvedUseFuns, pub implicit_candidates: UniqueMap, } @@ -151,6 +155,7 @@ pub struct Function { pub attributes: Attributes, pub visibility: Visibility, pub entry: Option, + pub macro_: Option, pub signature: FunctionSignature, pub body: FunctionBody, } @@ -229,6 +234,7 @@ pub enum Type_ { Ref(bool, Box), Param(TParam), Apply(Option, TypeName, Vec), + Fun(Vec, Box), Var(TVar), Anything, UnresolvedError, @@ -243,12 +249,15 @@ pub type Type = Spanned; pub struct Var_ { pub name: Symbol, pub id: u16, - pub color: u16, + pub color: Color, } pub type Var = Spanned; #[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] -pub struct BlockLabel(pub Var); +pub struct BlockLabel { + pub label: Var, + pub is_implicit: bool, +} #[derive(Debug, PartialEq, Clone)] #[allow(clippy::large_enum_variant)] @@ -265,6 +274,9 @@ pub type LValue = Spanned; pub type LValueList_ = Vec; pub type LValueList = Spanned; +pub type LambdaLValues_ = Vec<(LValueList, Option)>; +pub type LambdaLValues = Spanned; + #[derive(Debug, PartialEq, Clone)] pub enum ExpDotted_ { Exp(Box), @@ -276,10 +288,39 @@ pub type ExpDotted = Spanned; #[allow(clippy::large_enum_variant)] pub enum BuiltinFunction_ { Freeze(Option), - Assert(/* is_macro */ bool), + Assert(/* is_macro */ Option), } pub type BuiltinFunction = Spanned; +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub enum NominalBlockUsage { + Return, + Break, + Continue, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Lambda { + pub parameters: LambdaLValues, + pub return_type: Option, + pub return_label: BlockLabel, + pub use_fun_color: Color, + pub body: Box, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Block { + pub name: Option, + pub from_macro_argument: Option, + pub seq: Sequence, +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum MacroArgument { + Lambda(Loc), + Substituted(Loc), +} + #[derive(Debug, PartialEq, Clone)] #[allow(clippy::large_enum_variant)] pub enum Exp_ { @@ -290,18 +331,26 @@ pub enum Exp_ { ModuleCall( ModuleIdent, FunctionName, + /* is_macro */ Option, + Option>, + Spanned>, + ), + MethodCall( + ExpDotted, + Name, + /* is_macro */ Option, Option>, Spanned>, ), - MethodCall(ExpDotted, Name, Option>, Spanned>), + VarCall(Var, Spanned>), Builtin(BuiltinFunction, Spanned>), Vector(Loc, Option, Spanned>), IfElse(Box, Box, Box), - While(Box, BlockLabel, Box), + While(BlockLabel, Box, Box), Loop(BlockLabel, Box), - NamedBlock(BlockLabel, Sequence), - Block(Sequence), + Block(Block), + Lambda(Lambda), Assign(LValueList, Box), FieldMutate(ExpDotted, Box), @@ -309,7 +358,7 @@ pub enum Exp_ { Return(Box), Abort(Box), - Give(BlockLabel, Box), + Give(NominalBlockUsage, BlockLabel, Box), Continue(BlockLabel), Dereference(Box), @@ -334,9 +383,9 @@ pub type Exp = Spanned; pub type Sequence = (UseFuns, VecDeque); #[derive(Debug, PartialEq, Clone)] pub enum SequenceItem_ { - Seq(Exp), + Seq(Box), Declare(LValueList, Option), - Bind(LValueList, Exp), + Bind(LValueList, Box), } pub type SequenceItem = Spanned; @@ -368,6 +417,16 @@ impl TName for Var { // impls //************************************************************************************************** +impl UseFuns { + pub fn new(color: Color) -> Self { + Self { + color, + resolved: BTreeMap::new(), + implicit_candidates: UniqueMap::new(), + } + } +} + static BUILTIN_TYPE_ALL_NAMES: Lazy> = Lazy::new(|| { [ BuiltinTypeName_::ADDRESS, @@ -653,6 +712,7 @@ impl Type_ { Type_::Unit => Some(AbilitySet::collection(loc)), Type_::Ref(_, _) => Some(AbilitySet::references(loc)), Type_::Anything | Type_::UnresolvedError => Some(AbilitySet::all(loc)), + Type_::Fun(_, _) => Some(AbilitySet::functions(loc)), Type_::Var(_) => None, } } @@ -664,14 +724,26 @@ impl Type_ { Type_::Unit => Some(AbilitySet::COLLECTION.contains(&ability)), Type_::Ref(_, _) => Some(AbilitySet::REFERENCES.contains(&ability)), Type_::Anything | Type_::UnresolvedError => Some(true), + Type_::Fun(_, _) => Some(AbilitySet::FUNCTIONS.contains(&ability)), Type_::Var(_) => None, } } } -impl Exp_ { - // base symbol to used when making names forunnamed loops - pub const LOOP_NAME_SYMBOL: Symbol = symbol!("loop"); +impl Var_ { + pub fn starts_with_underscore(&self) -> bool { + P::Var::starts_with_underscore_name(self.name) + } + + pub fn is_valid(&self) -> bool { + P::Var::is_valid_name(self.name) + } +} + +impl BlockLabel { + // base symbol to used when making names for unnamed loops or lambdas + pub const IMPLICIT_LABEL_SYMBOL: Symbol = symbol!("%implicit"); + pub const MACRO_RETURN_NAME_SYMBOL: Symbol = symbol!("%macro"); } impl Value_ { @@ -729,6 +801,20 @@ impl fmt::Display for TypeName_ { } } +impl std::fmt::Display for NominalBlockUsage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + NominalBlockUsage::Return => "return", + NominalBlockUsage::Break => "break", + NominalBlockUsage::Continue => "continue", + } + ) + } +} + //************************************************************************************************** // Debug //************************************************************************************************** @@ -808,16 +894,21 @@ impl AstDebug for ResolvedUseFuns { impl AstDebug for UseFuns { fn ast_debug(&self, w: &mut AstWriter) { let Self { + color, resolved, implicit_candidates, } = self; + w.write(&format!("use_funs#{} ", color)); resolved.ast_debug(w); - w.write("unresolved "); - w.block(|w| { - for (_, _, implicit) in implicit_candidates { - implicit.ast_debug(w) - } - }) + if !implicit_candidates.is_empty() { + w.write("unresolved "); + w.block(|w| { + for (_, _, implicit) in implicit_candidates { + implicit.ast_debug(w) + } + }); + } + w.new_line(); } } @@ -908,6 +999,7 @@ impl AstDebug for (FunctionName, &Function) { index, attributes, visibility, + macro_, entry, signature, body, @@ -919,8 +1011,11 @@ impl AstDebug for (FunctionName, &Function) { if entry.is_some() { w.write(&format!("{} ", ENTRY_MODIFIER)); } + if macro_.is_some() { + w.write(&format!("{} ", MACRO_MODIFIER)); + } if let FunctionBody_::Native = &body.value { - w.write("native "); + w.write(&format!("{} ", NATIVE_MODIFIER)); } w.write(&format!("fun#{index} {name}")); signature.ast_debug(w); @@ -970,12 +1065,15 @@ impl AstDebug for Var_ { impl AstDebug for BlockLabel { fn ast_debug(&self, w: &mut AstWriter) { - let Var_ { name, id, color } = self.0.value; + let BlockLabel { + is_implicit: _, + label: sp!(_, Var_ { name, id, color }), + } = self; w.write(&format!("'{name}")); - if id != 0 { + if *id != 0 { w.write(&format!("#{id}")); } - if color != 0 { + if *color != 0 { w.write(&format!("#{color}")); } } @@ -1109,6 +1207,12 @@ impl AstDebug for Type_ { }), } } + Type_::Fun(args, result) => { + w.write("|"); + w.comma(args, |w, ty| ty.ast_debug(w)); + w.write("|"); + result.ast_debug(w); + } Type_::Var(tv) => w.write(&format!("#{}", tv.0)), Type_::Anything => w.write("_"), Type_::UnresolvedError => w.write("_|_"), @@ -1165,8 +1269,11 @@ impl AstDebug for Exp_ { E::Value(v) => v.ast_debug(w), E::Var(v) => v.ast_debug(w), E::Constant(m, c) => w.write(&format!("{}::{}", m, c)), - E::ModuleCall(m, f, tys_opt, sp!(_, rhs)) => { + E::ModuleCall(m, f, is_macro, tys_opt, sp!(_, rhs)) => { w.write(&format!("{}::{}", m, f)); + if is_macro.is_some() { + w.write("!"); + } if let Some(ss) = tys_opt { w.write("<"); ss.ast_debug(w); @@ -1176,9 +1283,12 @@ impl AstDebug for Exp_ { w.comma(rhs, |w, e| e.ast_debug(w)); w.write(")"); } - E::MethodCall(e, f, tys_opt, sp!(_, rhs)) => { + E::MethodCall(e, f, is_macro, tys_opt, sp!(_, rhs)) => { e.ast_debug(w); w.write(&format!(".{}", f)); + if is_macro.is_some() { + w.write("!"); + } if let Some(ss) = tys_opt { w.write("<"); ss.ast_debug(w); @@ -1188,6 +1298,12 @@ impl AstDebug for Exp_ { w.comma(rhs, |w, e| e.ast_debug(w)); w.write(")"); } + E::VarCall(var, sp!(_, rhs)) => { + var.ast_debug(w); + w.write("("); + w.comma(rhs, |w, e| e.ast_debug(w)); + w.write(")"); + } E::Builtin(bf, sp!(_, rhs)) => { bf.ast_debug(w); w.write("("); @@ -1228,27 +1344,23 @@ impl AstDebug for Exp_ { w.write(" else "); f.ast_debug(w); } - E::While(b, name, e) => { + E::While(name, b, e) => { + name.ast_debug(w); + w.write(": "); w.write("while "); w.write(" ("); b.ast_debug(w); w.write(") "); - name.ast_debug(w); - w.write(": "); e.ast_debug(w); } E::Loop(name, e) => { - w.write("loop "); name.ast_debug(w); w.write(": "); + w.write("loop "); e.ast_debug(w); } - E::NamedBlock(name, seq) => { - name.ast_debug(w); - w.write(": "); - seq.ast_debug(w); - } E::Block(seq) => seq.ast_debug(w), + E::Lambda(l) => l.ast_debug(w), E::ExpList(es) => { w.write("("); w.comma(es, |w, e| e.ast_debug(w)); @@ -1280,8 +1392,8 @@ impl AstDebug for Exp_ { w.write("abort "); e.ast_debug(w); } - E::Give(name, e) => { - w.write("give @"); + E::Give(usage, name, e) => { + w.write(&format!("give#{usage} '")); name.ast_debug(w); w.write(" "); e.ast_debug(w); @@ -1336,6 +1448,45 @@ impl AstDebug for Exp_ { } } +impl AstDebug for Lambda { + fn ast_debug(&self, w: &mut AstWriter) { + let Lambda { + parameters: sp!(_, bs), + return_type, + return_label, + use_fun_color, + body: e, + } = self; + return_label.ast_debug(w); + w.write(": "); + bs.ast_debug(w); + if let Some(ty) = return_type { + w.write(" -> "); + ty.ast_debug(w); + } + w.write(&format!("use_funs#{}", use_fun_color)); + e.ast_debug(w); + } +} + +impl AstDebug for Block { + fn ast_debug(&self, w: &mut AstWriter) { + let Block { + name, + from_macro_argument, + seq, + } = self; + if let Some(name) = name { + name.ast_debug(w); + w.write(": "); + } + if from_macro_argument.is_some() { + w.write("substituted_macro_arg#"); + } + seq.ast_debug(w); + } +} + impl AstDebug for BuiltinFunction_ { fn ast_debug(&self, w: &mut AstWriter) { use BuiltinFunction_ as F; @@ -1414,3 +1565,17 @@ impl AstDebug for LValue_ { } } } + +impl AstDebug for LambdaLValues_ { + fn ast_debug(&self, w: &mut AstWriter) { + w.write("|"); + w.comma(self, |w, (lv, ty_opt)| { + lv.ast_debug(w); + if let Some(ty) = ty_opt { + w.write(": "); + ty.ast_debug(w); + } + }); + w.write("| "); + } +} diff --git a/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs b/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs index 4c8a8ba1b5bac..b8abb7e021f49 100644 --- a/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs +++ b/external-crates/move/crates/move-compiler/src/naming/resolve_use_funs.rs @@ -48,6 +48,7 @@ pub fn program(env: &mut CompilationEnv, info: &mut NamingProgramInfo, inner: &m let N::UseFuns { resolved, implicit_candidates, + color: _, } = &mdef.use_funs; assert!(implicit_candidates.is_empty()); (mident, resolved.clone()) @@ -102,6 +103,7 @@ fn use_funs(context: &mut Context, uf: &mut N::UseFuns) { let N::UseFuns { resolved, implicit_candidates, + color: _, } = uf; // remove any incorrect resolved functions for (tn, methods) in &mut *resolved { @@ -310,24 +312,34 @@ fn exp(context: &mut Context, sp!(_, e_): &mut N::Exp) { | N::Exp_::UnresolvedError => (), N::Exp_::Return(e) | N::Exp_::Abort(e) - | N::Exp_::Give(_, e) + | N::Exp_::Give(_, _, e) | N::Exp_::Dereference(e) | N::Exp_::UnaryExp(_, e) | N::Exp_::Cast(e, _) | N::Exp_::Assign(_, e) | N::Exp_::Loop(_, e) - | N::Exp_::Annotate(e, _) => exp(context, e), + | N::Exp_::Annotate(e, _) + | N::Exp_::Lambda(N::Lambda { + parameters: _, + return_type: _, + return_label: _, + use_fun_color: _, + body: e, + }) => exp(context, e), N::Exp_::IfElse(econd, et, ef) => { exp(context, econd); exp(context, et); exp(context, ef); } - N::Exp_::While(econd, _, ebody) => { + N::Exp_::While(_, econd, ebody) => { exp(context, econd); exp(context, ebody) } - N::Exp_::NamedBlock(_, s) => sequence(context, s), - N::Exp_::Block(s) => sequence(context, s), + N::Exp_::Block(N::Block { + name: _, + from_macro_argument: _, + seq, + }) => sequence(context, seq), N::Exp_::FieldMutate(ed, e) => { exp_dotted(context, ed); exp(context, e) @@ -343,13 +355,14 @@ fn exp(context: &mut Context, sp!(_, e_): &mut N::Exp) { } N::Exp_::Builtin(_, sp!(_, es)) | N::Exp_::Vector(_, _, sp!(_, es)) - | N::Exp_::ModuleCall(_, _, _, sp!(_, es)) + | N::Exp_::ModuleCall(_, _, _, _, sp!(_, es)) + | N::Exp_::VarCall(_, sp!(_, es)) | N::Exp_::ExpList(es) => { for e in es { exp(context, e) } } - N::Exp_::MethodCall(ed, _, _, sp!(_, es)) => { + N::Exp_::MethodCall(ed, _, _, _, sp!(_, es)) => { exp_dotted(context, ed); for e in es { exp(context, e) diff --git a/external-crates/move/crates/move-compiler/src/naming/translate.rs b/external-crates/move/crates/move-compiler/src/naming/translate.rs index 7ee8acbdde71b..2617cff403e96 100644 --- a/external-crates/move/crates/move-compiler/src/naming/translate.rs +++ b/external-crates/move/crates/move-compiler/src/naming/translate.rs @@ -4,14 +4,14 @@ use crate::{ diag, - diagnostics::codes::*, + diagnostics::{self, codes::*}, editions::FeatureGate, expansion::{ ast::{self as E, AbilitySet, ModuleIdent, Visibility}, translate::is_valid_struct_constant_or_schema_name as is_constant_name, }, - naming::ast::{self as N, BlockLabel}, - parser::ast::{self as P, ConstantName, Field, FunctionName, StructName}, + naming::ast::{self as N, BlockLabel, NominalBlockUsage}, + parser::ast::{self as P, ConstantName, Field, FunctionName, StructName, MACRO_MODIFIER}, shared::{program_info::NamingProgramInfo, unique_map::UniqueMap, *}, FullyCompiledProgram, }; @@ -52,6 +52,7 @@ struct ModuleType { enum ResolvedFunction { Builtin(N::BuiltinFunction), Module(Box), + Var(N::Var), Unbound, } @@ -62,10 +63,24 @@ struct ResolvedModuleFunction { ty_args: Option>, } -#[derive(PartialEq)] -enum NominalBlockType { +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ResolveFunctionCase { + UseFun, + Call, +} + +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +enum LoopType { + While, Loop, +} + +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +enum NominalBlockType { + Loop(LoopType), Block, + LambdaReturn, + LambdaLoopCapture, } struct Context<'env> { @@ -78,7 +93,7 @@ struct Context<'env> { local_scopes: Vec>, local_count: BTreeMap, used_locals: BTreeSet, - nominal_blocks: Vec<(Symbol, u16, NominalBlockType)>, + nominal_blocks: Vec<(Option, BlockLabel, NominalBlockType)>, nominal_block_id: u16, /// Type parameters used in a function (they have to be cleared after processing each function). used_fun_tparams: BTreeSet, @@ -435,6 +450,7 @@ impl<'env> Context<'env> { fn resolve_local( &mut self, loc: Loc, + code: diagnostics::codes::NameResolution, variable_msg: impl FnOnce(Symbol) -> S, sp!(vloc, name): Name, ) -> Option { @@ -442,8 +458,7 @@ impl<'env> Context<'env> { match id_opt { None => { let msg = variable_msg(name); - self.env - .add_diag(diag!(NameResolution::UnboundVariable, (loc, msg))); + self.env.add_diag(diag!(code, (loc, msg))); None } Some(id) => { @@ -456,90 +471,184 @@ impl<'env> Context<'env> { } } - fn enter_nominal_block(&mut self, name: Option, name_type: NominalBlockType) { + fn enter_nominal_block( + &mut self, + loc: Loc, + name: Option, + name_type: NominalBlockType, + ) { debug_assert!( self.nominal_blocks.len() < 100, "Nominal block list exceeded 100." ); - let sym = if let Some(name) = name { - name.value() - } else { - // all named blocks have names, so a non-named block must be a loop - N::Exp_::LOOP_NAME_SYMBOL - }; let id = self.nominal_block_id; self.nominal_block_id += 1; - self.nominal_blocks.push((sym, id, name_type)); + let name = name.map(|n| n.value()); + let block_label = block_label(loc, name, id); + self.nominal_blocks.push((name, block_label, name_type)); + } + + fn current_loop(&mut self, loc: Loc, usage: NominalBlockUsage) -> Option { + let Some((_name, label, name_type)) = + self.nominal_blocks.iter().rev().find(|(_, _, name_type)| { + matches!( + name_type, + NominalBlockType::Loop(_) | NominalBlockType::LambdaLoopCapture + ) + }) + else { + let msg = format!( + "Invalid usage of '{usage}'. \ + '{usage}' can only be used inside a loop body or lambda", + ); + self.env + .add_diag(diag!(TypeSafety::InvalidLoopControl, (loc, msg))); + return None; + }; + if *name_type == NominalBlockType::LambdaLoopCapture { + // lambdas capture break/continue even though it is not yet supported + let msg = + format!("Invalid '{usage}'. This usage is not yet supported for lambdas or macros"); + let mut diag = diag!( + TypeSafety::InvalidLoopControl, + (loc, msg), + (label.label.loc, "Inside this lambda") + ); + // suggest adding a label to the loop + let most_recent_loop_opt = + self.nominal_blocks + .iter() + .rev() + .find_map(|(name, label, name_type)| { + if let NominalBlockType::Loop(loop_type) = name_type { + Some((name, label, *loop_type)) + } else { + None + } + }); + if let Some((name, loop_label, loop_type)) = most_recent_loop_opt { + let msg = if let Some(loop_label) = name { + format!( + "To '{usage}' to this loop, specify the label, \ + e.g. `{usage} '{loop_label}`", + ) + } else { + format!( + "To '{usage}' to this loop, add a label, \ + e.g. `'label: {loop_type}` and `{usage} 'label`", + ) + }; + diag.add_secondary_label((loop_label.label.loc, msg)); + } + self.env.add_diag(diag); + return None; + } + Some(*label) + } + + fn current_continue(&mut self, loc: Loc) -> Option { + self.current_loop(loc, NominalBlockUsage::Continue) } - fn current_loop(&self, loc: Loc) -> Option { + fn current_break(&mut self, loc: Loc) -> Option { + self.current_loop(loc, NominalBlockUsage::Break) + } + + fn current_return(&self, _loc: Loc) -> Option { self.nominal_blocks .iter() .rev() - .find(|(_, _, name_type)| *name_type == NominalBlockType::Loop) - .map(|(name, id, _)| { - BlockLabel(sp( - loc, - N::Var_ { - name: *name, - id: *id, - color: 0, - }, - )) - }) + .find(|(_, _, name_type)| matches!(name_type, NominalBlockType::LambdaReturn)) + .map(|(_, label, _)| *label) } fn resolve_nominal_label( &mut self, - verb: &str, - expected_block_type: NominalBlockType, + usage: NominalBlockUsage, label: P::BlockLabel, ) -> Option { let loc = label.loc(); let name = label.value(); - let id_opt = self + let label_opt = self .nominal_blocks .iter() .rev() - .find(|(block_name, _, _)| name == *block_name) - .map(|(_, id, block_type)| (id, block_type)); - if let Some((id, block_type)) = id_opt { - if *block_type == expected_block_type { - let nvar_ = N::Var_ { - name, - id: *id, - color: 0, - }; - Some(BlockLabel(sp(loc, nvar_))) + .find(|(block_name, _, _)| block_name.is_some_and(|n| n == name)) + .map(|(_, label, block_type)| (label, block_type)); + if let Some((label, block_type)) = label_opt { + let block_type = *block_type; + if block_type.is_acceptable_usage(usage) { + Some(*label) } else { - let msg = format!( - "Invalid usage of '{}' with a {} block label", - verb, block_type - ); + let msg = format!("Invalid usage of '{usage}' with a {block_type} block label",); let mut diag = diag!(NameResolution::InvalidLabel, (loc, msg)); - match expected_block_type { - NominalBlockType::Loop => { - diag.add_note("Loop labels may only be used with 'break' and 'continue', not 'return'"); + diag.add_note(match block_type { + NominalBlockType::Loop(_) => { + "Loop labels may only be used with 'break' and 'continue', \ + not 'return'" } NominalBlockType::Block => { - diag.add_note("Named block labels may only be used with 'return', not 'break' or 'continue'."); + "Named block labels may only be used with 'return', \ + not 'break' or 'continue'." } - } + NominalBlockType::LambdaReturn | NominalBlockType::LambdaLoopCapture => { + "Lambda block labels may only be used with 'return' or 'break', \ + not 'continue'." + } + }); self.env.add_diag(diag); None } } else { - let msg = format!("Invalid {}. Unbound label '{}", verb, name); + let msg = format!("Invalid {usage}. Unbound label '{name}"); self.env - .add_diag(diag!(NameResolution::UnboundVariable, (loc, msg))); + .add_diag(diag!(NameResolution::UnboundLabel, (loc, msg))); None } } - fn exit_nominal_block(&mut self, loc: Loc) -> BlockLabel { - let (name, id, _) = self.nominal_blocks.pop().unwrap(); - let nvar_ = N::Var_ { name, id, color: 0 }; - BlockLabel(sp(loc, nvar_)) + fn exit_nominal_block(&mut self) -> (BlockLabel, NominalBlockType) { + let (_name, label, name_type) = self.nominal_blocks.pop().unwrap(); + (label, name_type) + } +} + +fn block_label(loc: Loc, name: Option, id: u16) -> BlockLabel { + let is_implicit = name.is_none(); + let name = name.unwrap_or(BlockLabel::IMPLICIT_LABEL_SYMBOL); + let var_ = N::Var_ { name, id, color: 0 }; + let label = sp(loc, var_); + BlockLabel { label, is_implicit } +} + +impl NominalBlockType { + // loops can have break or continue + // blocks can have return + // lambdas can have return or break + fn is_acceptable_usage(self, usage: NominalBlockUsage) -> bool { + match (self, usage) { + (NominalBlockType::Loop(_), NominalBlockUsage::Break) + | (NominalBlockType::Loop(_), NominalBlockUsage::Continue) + | (NominalBlockType::Block, NominalBlockUsage::Return) + | (NominalBlockType::LambdaReturn, NominalBlockUsage::Return) + | (NominalBlockType::LambdaLoopCapture, NominalBlockUsage::Break) + | (NominalBlockType::LambdaLoopCapture, NominalBlockUsage::Continue) => true, + (NominalBlockType::Loop(_), NominalBlockUsage::Return) + | (NominalBlockType::Block, NominalBlockUsage::Break) + | (NominalBlockType::Block, NominalBlockUsage::Continue) + | (NominalBlockType::LambdaReturn, NominalBlockUsage::Break) + | (NominalBlockType::LambdaReturn, NominalBlockUsage::Continue) + | (NominalBlockType::LambdaLoopCapture, NominalBlockUsage::Return) => false, + } + } +} + +impl std::fmt::Display for LoopType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LoopType::While => write!(f, "while"), + LoopType::Loop => write!(f, "loop"), + } } } @@ -549,8 +658,9 @@ impl std::fmt::Display for NominalBlockType { f, "{}", match self { - NominalBlockType::Loop => "loop", + NominalBlockType::Loop(_) => "loop", NominalBlockType::Block => "named", + NominalBlockType::LambdaReturn | NominalBlockType::LambdaLoopCapture => "lambda", } ) } @@ -602,7 +712,7 @@ fn module( context.current_package = package_name; context.env.add_warning_filter_scope(warning_filter.clone()); let unscoped = context.save_unscoped(); - let use_funs = use_funs(context, euse_funs); + let mut use_funs = use_funs(context, euse_funs); let friends = efriends.filter_map(|mident, f| friend(context, mident, f)); let structs = estructs.map(|name, s| { context.restore_unscoped(unscoped.clone()); @@ -616,6 +726,17 @@ fn module( context.restore_unscoped(unscoped.clone()); constant(context, name, c) }); + // Silence unused use fun warnings if a module has macros. + // For public macros, the macro will pull in the use fun, and we will which case we will be + // unable to tell if it is used or not + // For private macros, we duplicate the scope of the module and when resolving the method + // fail to mark the outer scope as used (instead we only mark the modules scope cloned + // into the macro) + // TODO we should approximate this by just checking for the name, regardless of the type + let has_macro = functions.iter().any(|(_, _, f)| f.macro_.is_some()); + if has_macro { + mark_all_use_funs_as_used(&mut use_funs); + } context.restore_unscoped(unscoped); context.env.pop_warning_filter_scope(); context.current_package = None; @@ -660,6 +781,7 @@ fn use_funs(context: &mut Context, eufs: E::UseFuns) -> N::UseFuns { } } N::UseFuns { + color: 0, // used for macro substitution resolved, implicit_candidates: eimplicit, } @@ -677,7 +799,8 @@ fn explicit_use_fun( ty, method, } = e; - let m_f_opt = match resolve_function(context, loc, function, None) { + let m_f_opt = match resolve_function(context, ResolveFunctionCase::UseFun, loc, function, None) + { ResolvedFunction::Module(mf) => { let ResolvedModuleFunction { module, @@ -694,6 +817,9 @@ fn explicit_use_fun( .add_diag(diag!(Declarations::InvalidUseFun, (loc, msg))); None } + ResolvedFunction::Var(_) => { + unreachable!("ICE this case should be excluded from ResolveFunctionCase::UseFun") + } ResolvedFunction::Unbound => { assert!(context.env.has_errors()); None @@ -794,6 +920,25 @@ fn use_fun_module_defines( } } +fn mark_all_use_funs_as_used(use_funs: &mut N::UseFuns) { + let N::UseFuns { + color: _, + resolved, + implicit_candidates, + } = use_funs; + for methods in resolved.values_mut() { + for (_, _, uf) in methods { + uf.used = true; + } + } + for (_, _, uf) in implicit_candidates { + match &mut uf.kind { + E::ImplicitUseFunKind::UseAlias { used } => *used = true, + E::ImplicitUseFunKind::FunctionDeclaration => (), + } + } +} + //************************************************************************************************** // Friends //************************************************************************************************** @@ -842,6 +987,7 @@ fn function( attributes, loc: _, visibility, + macro_, entry, signature, body, @@ -876,6 +1022,7 @@ fn function( index, attributes, visibility, + macro_, entry, signature, body, @@ -900,9 +1047,31 @@ fn function_signature(context: &mut Context, sig: E::FunctionSignature) -> N::Fu let parameters = sig .parameters .into_iter() - .map(|(mut_, param, param_ty)| { + .map(|(mut mut_, param, param_ty)| { + let is_underscore = param.is_underscore(); + if is_underscore { + check_mut_underscore(context, mut_); + mut_ = None + }; + if param.is_syntax_identifier() + && context + .env + .supports_feature(context.current_package, FeatureGate::LetMut) + { + if let Some(mutloc) = mut_ { + let msg = format!( + "Invalid 'mut' parameter. \ + '{}' parameters cannot be declared as mutable", + MACRO_MODIFIER + ); + let mut diag = diag!(NameResolution::InvalidMacroParameter, (mutloc, msg)); + diag.add_note(ASSIGN_SYNTAX_IDENTIFIER_NOTE); + context.env.add_diag(diag); + mut_ = None + } + } if let Err((param, prev_loc)) = declared.add(param, ()) { - if !param.is_underscore() { + if !is_underscore { let msg = format!("Duplicate parameter with name '{}'", param); context.env.add_diag(diag!( Declarations::DuplicateItem, @@ -932,6 +1101,9 @@ fn function_body(context: &mut Context, sp!(loc, b_): E::FunctionBody) -> N::Fun } } +const ASSIGN_SYNTAX_IDENTIFIER_NOTE: &str = "'macro' parameters are substituted without \ + being evaluated. There is no local variable to assign to"; + //************************************************************************************************** // Structs //************************************************************************************************** @@ -1007,7 +1179,7 @@ fn constant(context: &mut Context, _name: ConstantName, econstant: E::Constant) context.env.add_warning_filter_scope(warning_filter.clone()); context.local_scopes = vec![BTreeMap::new()]; let signature = type_(context, esignature); - let value = exp_(context, evalue); + let value = *exp(context, Box::new(evalue)); context.local_scopes = vec![]; context.local_count = BTreeMap::new(); context.used_locals = BTreeSet::new(); @@ -1141,7 +1313,11 @@ fn type_(context: &mut Context, sp!(loc, ety_): E::Type) -> N::Type { NT::Apply(None, tn, tys) } }, - ET::Fun(_, _) => panic!("ICE only allowed in spec context"), + ET::Fun(tys, ty) => { + let tys = types(context, tys); + let ty = Box::new(type_(context, *ty)); + NT::Fun(tys, ty) + } }; sp(loc, ty_) } @@ -1197,25 +1373,25 @@ fn sequence_item(context: &mut Context, sp!(loc, ns_): E::SequenceItem) -> N::Se use N::SequenceItem_ as NS; let s_ = match ns_ { - ES::Seq(e) => NS::Seq(exp_(context, e)), + ES::Seq(e) => NS::Seq(exp(context, e)), ES::Declare(b, ty_opt) => { let bind_opt = bind_list(context, b); let tys = ty_opt.map(|t| type_(context, t)); match bind_opt { None => { assert!(context.env.has_errors()); - NS::Seq(sp(loc, N::Exp_::UnresolvedError)) + NS::Seq(Box::new(sp(loc, N::Exp_::UnresolvedError))) } Some(bind) => NS::Declare(bind, tys), } } ES::Bind(b, e) => { - let e = exp_(context, e); + let e = exp(context, e); let bind_opt = bind_list(context, b); match bind_opt { None => { assert!(context.env.has_errors()); - NS::Seq(sp(loc, N::Exp_::UnresolvedError)) + NS::Seq(Box::new(sp(loc, N::Exp_::UnresolvedError))) } Some(bind) => NS::Bind(bind, e), } @@ -1229,17 +1405,13 @@ fn call_args(context: &mut Context, sp!(loc, es): Spanned>) -> Spann } fn exps(context: &mut Context, es: Vec) -> Vec { - es.into_iter().map(|e| exp_(context, e)).collect() -} - -fn exp(context: &mut Context, e: E::Exp) -> Box { - Box::new(exp_(context, e)) + es.into_iter().map(|e| *exp(context, Box::new(e))).collect() } -fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { +fn exp(context: &mut Context, e: Box) -> Box { use E::Exp_ as EE; use N::Exp_ as NE; - let sp!(eloc, e_) = e; + let sp!(eloc, e_) = *e; let ne_ = match e_ { EE::Unit { trailing } => NE::Unit { trailing }, EE::Value(val) => NE::Value(val), @@ -1247,7 +1419,12 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { if is_constant_name(&v.value) { access_constant(context, sp(aloc, E::ModuleAccess_::Name(v))) } else { - match context.resolve_local(eloc, |name| format!("Unbound variable '{name}'"), v) { + match context.resolve_local( + eloc, + NameResolution::UnboundVariable, + |name| format!("Unbound variable '{name}'"), + v, + ) { None => { debug_assert!(context.env.has_errors()); NE::UnresolvedError @@ -1258,30 +1435,68 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { } EE::Name(ma, None) => access_constant(context, ma), - EE::IfElse(eb, et, ef) => { - NE::IfElse(exp(context, *eb), exp(context, *et), exp(context, *ef)) - } - EE::While(eb, name_opt, el) => { - let cond = exp(context, *eb); - context.enter_nominal_block(name_opt, NominalBlockType::Loop); - let body = exp(context, *el); - NE::While(cond, context.exit_nominal_block(eloc), body) + EE::IfElse(eb, et, ef) => NE::IfElse(exp(context, eb), exp(context, et), exp(context, ef)), + EE::While(name_opt, eb, el) => { + let cond = exp(context, eb); + context.enter_nominal_block(eloc, name_opt, NominalBlockType::Loop(LoopType::While)); + let body = exp(context, el); + let (label, name_type) = context.exit_nominal_block(); + assert_eq!(name_type, NominalBlockType::Loop(LoopType::While)); + NE::While(label, cond, body) } EE::Loop(name_opt, el) => { - context.enter_nominal_block(name_opt, NominalBlockType::Loop); - let body = exp(context, *el); - NE::Loop(context.exit_nominal_block(eloc), body) + context.enter_nominal_block(eloc, name_opt, NominalBlockType::Loop(LoopType::Loop)); + let body = exp(context, el); + let (label, name_type) = context.exit_nominal_block(); + assert_eq!(name_type, NominalBlockType::Loop(LoopType::Loop)); + NE::Loop(label, body) + } + EE::Block(Some(name), eseq) => { + context.enter_nominal_block(eloc, Some(name), NominalBlockType::Block); + let seq = sequence(context, eseq); + let (label, name_type) = context.exit_nominal_block(); + assert_eq!(name_type, NominalBlockType::Block); + NE::Block(N::Block { + name: Some(label), + from_macro_argument: None, + seq, + }) } - EE::NamedBlock(name, seq) => { - context.enter_nominal_block(Some(name), NominalBlockType::Block); - let body = sequence(context, seq); - NE::NamedBlock(context.exit_nominal_block(eloc), body) + EE::Block(None, eseq) => NE::Block(N::Block { + name: None, + from_macro_argument: None, + seq: sequence(context, eseq), + }), + EE::Lambda(elambda_binds, ety_opt, body) => { + context.new_local_scope(); + let nlambda_binds_opt = lambda_bind_list(context, elambda_binds); + let return_type = ety_opt.map(|t| type_(context, t)); + context.enter_nominal_block(eloc, None, NominalBlockType::LambdaLoopCapture); + context.enter_nominal_block(eloc, None, NominalBlockType::LambdaReturn); + let body = exp(context, body); + context.close_local_scope(); + let (return_label, return_name_type) = context.exit_nominal_block(); + assert_eq!(return_name_type, NominalBlockType::LambdaReturn); + let (_, loop_name_type) = context.exit_nominal_block(); + assert_eq!(loop_name_type, NominalBlockType::LambdaLoopCapture); + match nlambda_binds_opt { + None => { + assert!(context.env.has_errors()); + N::Exp_::UnresolvedError + } + Some(parameters) => NE::Lambda(N::Lambda { + parameters, + return_type, + return_label, + use_fun_color: 0, // used in macro expansion + body, + }), + } } - EE::Block(seq) => NE::Block(sequence(context, seq)), EE::Assign(a, e) => { let na_opt = assign_list(context, a); - let ne = exp(context, *e); + let ne = exp(context, e); match na_opt { None => { assert!(context.env.has_errors()); @@ -1292,7 +1507,7 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { } EE::FieldMutate(edotted, er) => { let ndot_opt = dotted(context, *edotted); - let ner = exp(context, *er); + let ner = exp(context, er); match ndot_opt { None => { assert!(context.env.has_errors()); @@ -1302,70 +1517,57 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { } } EE::Mutate(el, er) => { - let nel = exp(context, *el); - let ner = exp(context, *er); + let nel = exp(context, el); + let ner = exp(context, er); NE::Mutate(nel, ner) } - EE::Abort(es) => NE::Abort(exp(context, *es)), - EE::Return(name_opt, es) => { - let out_rhs = exp(context, *es); - if let Some(block_name) = name_opt { - if let Some(return_name) = - context.resolve_nominal_label("return", NominalBlockType::Block, block_name) - { - NE::Give(return_name, out_rhs) - } else { - NE::UnresolvedError - } + EE::Abort(es) => NE::Abort(exp(context, es)), + EE::Return(Some(block_name), es) => { + let out_rhs = exp(context, es); + context + .resolve_nominal_label(NominalBlockUsage::Return, block_name) + .map(|name| NE::Give(NominalBlockUsage::Return, name, out_rhs)) + .unwrap_or_else(|| NE::UnresolvedError) + } + EE::Return(None, es) => { + let out_rhs = exp(context, es); + if let Some(return_name) = context.current_return(eloc) { + NE::Give(NominalBlockUsage::Return, return_name, out_rhs) } else { NE::Return(out_rhs) } } EE::Break(name_opt, rhs) => { - let out_rhs = exp(context, *rhs); + let out_rhs = exp(context, rhs); if let Some(loop_name) = name_opt { context - .resolve_nominal_label("break", NominalBlockType::Loop, loop_name) - .map(|name| NE::Give(name, out_rhs)) + .resolve_nominal_label(NominalBlockUsage::Break, loop_name) + .map(|name| NE::Give(NominalBlockUsage::Break, name, out_rhs)) .unwrap_or_else(|| NE::UnresolvedError) } else { context - .current_loop(eloc) - .map(|name| NE::Give(name, out_rhs)) - .unwrap_or_else(|| { - let msg = "Invalid usage of 'break'. \ - 'break' can only be used inside a loop body"; - context - .env - .add_diag(diag!(TypeSafety::InvalidLoopControl, (eloc, msg))); - NE::UnresolvedError - }) + .current_break(eloc) + .map(|name| NE::Give(NominalBlockUsage::Break, name, out_rhs)) + .unwrap_or_else(|| NE::UnresolvedError) } } EE::Continue(name_opt) => { if let Some(loop_name) = name_opt { context - .resolve_nominal_label("continue", NominalBlockType::Loop, loop_name) + .resolve_nominal_label(NominalBlockUsage::Continue, loop_name) .map(NE::Continue) .unwrap_or_else(|| NE::UnresolvedError) } else { context - .current_loop(eloc) + .current_continue(eloc) .map(NE::Continue) - .unwrap_or_else(|| { - let msg = "Invalid usage of 'continue'. \ - 'continue' can only be used inside a loop body"; - context - .env - .add_diag(diag!(TypeSafety::InvalidLoopControl, (eloc, msg))); - NE::UnresolvedError - }) + .unwrap_or_else(|| NE::UnresolvedError) } } - EE::Dereference(e) => NE::Dereference(exp(context, *e)), - EE::UnaryExp(uop, e) => NE::UnaryExp(uop, exp(context, *e)), + EE::Dereference(e) => NE::Dereference(exp(context, e)), + EE::UnaryExp(uop, e) => NE::UnaryExp(uop, exp(context, e)), e_ @ EE::BinopExp(..) => { process_binops!( @@ -1375,7 +1577,7 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { e, *e, sp!(loc, EE::BinopExp(lhs, op, rhs)) => { (lhs, (op, loc), rhs) }, - { exp(context, *e) }, + { exp(context, e) }, value_stack, (bop, loc) => { let el = value_stack.pop().expect("ICE binop naming issue"); @@ -1404,7 +1606,7 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { m, sn, tys_opt, - efields.map(|_, (idx, e)| (idx, exp_(context, e))), + efields.map(|_, (idx, e)| (idx, *exp(context, Box::new(e)))), ) } } @@ -1422,31 +1624,19 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { Some(d) => NE::ExpDotted(case, d), }, - EE::Cast(e, t) => NE::Cast(exp(context, *e), type_(context, t)), - EE::Annotate(e, t) => NE::Annotate(exp(context, *e), type_(context, t)), + EE::Cast(e, t) => NE::Cast(exp(context, e), type_(context, t)), + EE::Annotate(e, t) => NE::Annotate(exp(context, e), type_(context, t)), - EE::Call(sp!(mloc, ma_), true, tys_opt, rhs) => { - use E::ModuleAccess_ as EA; - use N::BuiltinFunction_ as BF; - assert!(tys_opt.is_none(), "ICE macros do not have type arguments"); - let nes = call_args(context, rhs); - match ma_ { - EA::Name(n) if n.value.as_str() == BF::ASSERT_MACRO => { - NE::Builtin(sp(mloc, BF::Assert(true)), nes) - } - ma_ => { - context.env.add_diag(diag!( - NameResolution::UnboundMacro, - (mloc, format!("Unbound macro '{}'", ma_)), - )); - NE::UnresolvedError - } - } - } - EE::Call(ma, false, tys_opt, rhs) if context.resolves_to_struct(&ma) => { + EE::Call(ma, is_macro, tys_opt, rhs) if context.resolves_to_struct(&ma) => { context .env .check_feature(FeatureGate::PositionalFields, context.current_package, eloc); + if let Some(mloc) = is_macro { + let msg = "Unexpected macro invocation. Structs cannot be invoked as macros"; + context + .env + .add_diag(diag!(NameResolution::PositionalCallMismatch, (mloc, msg))); + } let nes = call_args(context, rhs); match context.resolve_struct_name(eloc, "construction", ma, tys_opt) { None => { @@ -1476,18 +1666,70 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { } } } - EE::Call(ma, false, tys_opt, rhs) => { + EE::Call(ma, is_macro, tys_opt, rhs) => { + use N::BuiltinFunction_ as BF; let ty_args = tys_opt.map(|tys| types(context, tys)); let nes = call_args(context, rhs); - match resolve_function(context, eloc, ma, ty_args) { - ResolvedFunction::Builtin(f) => NE::Builtin(f, nes), + match resolve_function(context, ResolveFunctionCase::Call, eloc, ma, ty_args) { + ResolvedFunction::Builtin(sp!(bloc, BF::Assert(_))) => { + if is_macro.is_none() { + let dep_msg = format!( + "'{}' function syntax has been deprecated and will be removed", + BF::ASSERT_MACRO + ); + // TODO make this a tip/hint? + let help_msg = format!( + "Replace with '{0}!'. '{0}' has been replaced with a '{0}!' built-in \ + macro so that arguments are no longer eagerly evaluated", + BF::ASSERT_MACRO + ); + context.env.add_diag(diag!( + Uncategorized::DeprecatedWillBeRemoved, + (bloc, dep_msg), + (bloc, help_msg), + )); + } + NE::Builtin(sp(bloc, BF::Assert(is_macro)), nes) + } + ResolvedFunction::Builtin(bf @ sp!(_, BF::Freeze(_))) => { + if let Some(mloc) = is_macro { + let msg = format!( + "Unexpected macro invocation. '{}' cannot be invoked as a \ + macro", + bf.value.display_name() + ); + context + .env + .add_diag(diag!(TypeSafety::InvalidCallTarget, (mloc, msg))); + } + NE::Builtin(bf, nes) + } + ResolvedFunction::Module(mf) => { + if let Some(mloc) = is_macro { + context.env.check_feature( + FeatureGate::MacroFuns, + context.current_package, + mloc, + ); + } let ResolvedModuleFunction { module, function, ty_args, } = *mf; - NE::ModuleCall(module, function, ty_args, nes) + NE::ModuleCall(module, function, is_macro, ty_args, nes) + } + ResolvedFunction::Var(v) => { + if let Some(mloc) = is_macro { + let msg = + "Unexpected macro invocation. Bound lambdas cannot be invoked as \ + a macro"; + context + .env + .add_diag(diag!(TypeSafety::InvalidCallTarget, (mloc, msg))); + } + NE::VarCall(v, nes) } ResolvedFunction::Unbound => { assert!(context.env.has_errors()); @@ -1495,7 +1737,7 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { } } } - EE::MethodCall(edot, n, tys_opt, rhs) => match dotted(context, *edot) { + EE::MethodCall(edot, n, is_macro, tys_opt, rhs) => match dotted(context, *edot) { None => { assert!(context.env.has_errors()); NE::UnresolvedError @@ -1503,7 +1745,14 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { Some(d) => { let ty_args = tys_opt.map(|tys| types(context, tys)); let nes = call_args(context, rhs); - NE::MethodCall(d, n, ty_args, nes) + if is_macro.is_some() { + context.env.check_feature( + FeatureGate::MacroFuns, + context.current_package, + eloc, + ); + } + NE::MethodCall(d, n, is_macro, ty_args, nes) } }, EE::Vector(vec_loc, tys_opt, rhs) => { @@ -1529,11 +1778,11 @@ fn exp_(context: &mut Context, e: E::Exp) -> N::Exp { NE::UnresolvedError } // `Name` matches name variants only allowed in specs (we handle the allowed ones above) - EE::Index(..) | EE::Lambda(..) | EE::Quant(..) | EE::Name(_, Some(_)) => { + EE::Index(..) | EE::Quant(..) | EE::Name(_, Some(_)) => { panic!("ICE unexpected specification construct") } }; - sp(eloc, ne_) + Box::new(sp(eloc, ne_)) } fn access_constant(context: &mut Context, ma: E::ModuleAccess) -> N::Exp_ { @@ -1550,7 +1799,7 @@ fn dotted(context: &mut Context, edot: E::ExpDotted) -> Option { let sp!(loc, edot_) = edot; let nedot_ = match edot_ { E::ExpDotted_::Exp(e) => { - let ne = exp(context, *e); + let ne = exp(context, e); match &ne.value { N::Exp_::UnresolvedError => return None, _ => N::ExpDotted_::Exp(ne), @@ -1580,6 +1829,7 @@ fn lvalue( EL::Var(mut_, sp!(_, E::ModuleAccess_::Name(n)), None) => { let v = P::Var(n); if v.is_underscore() { + check_mut_underscore(context, mut_); NL::Ignore } else { if let Err((var, prev_loc)) = seen_locals.add(n, ()) { @@ -1603,6 +1853,21 @@ fn lvalue( .env .add_diag(diag!(Declarations::DuplicateItem, primary, secondary)); } + if v.is_syntax_identifier() { + debug_assert!( + matches!(case, C::Assign), + "ICE this should fail during parsing" + ); + let msg = format!( + "Cannot assign to argument for parameter '{}'. \ + Arguments must be used in value positions", + v.0 + ); + let mut diag = diag!(TypeSafety::CannotExpandMacro, (loc, msg)); + diag.add_note(ASSIGN_SYNTAX_IDENTIFIER_NOTE); + context.env.add_diag(diag); + return None; + } let nv = match case { C::Bind => { let is_parameter = false; @@ -1610,6 +1875,7 @@ fn lvalue( } C::Assign => context.resolve_local( loc, + NameResolution::UnboundVariable, |name| format!("Invalid assignment. Unbound variable '{name}'"), n, )?, @@ -1670,10 +1936,42 @@ fn lvalue( Some(sp(loc, nl_)) } +fn check_mut_underscore(context: &mut Context, mut_: Option) { + // no error if not a mut declaration + let Some(mut_) = mut_ else { return }; + // no error if let-mut is not supported + // (we mark all locals as having mut if the feature is off) + if !context + .env + .supports_feature(context.current_package, FeatureGate::LetMut) + { + return; + } + let msg = "Invalid 'mut' declaration. 'mut' is applied to variables and cannot be applied to the '_' pattern"; + context + .env + .add_diag(diag!(NameResolution::InvalidMut, (mut_, msg))); +} + fn bind_list(context: &mut Context, ls: E::LValueList) -> Option { lvalue_list(context, &mut UniqueMap::new(), LValueCase::Bind, ls) } +fn lambda_bind_list( + context: &mut Context, + sp!(loc, elambda): E::LambdaLValues, +) -> Option { + let nlambda = elambda + .into_iter() + .map(|(pbs, ty_opt)| { + let bs = bind_list(context, pbs)?; + let ety = ty_opt.map(|t| type_(context, t)); + Some((bs, ety)) + }) + .collect::>()?; + Some(sp(loc, nlambda)) +} + fn assign_list(context: &mut Context, ls: E::LValueList) -> Option { lvalue_list(context, &mut UniqueMap::new(), LValueCase::Assign, ls) } @@ -1694,13 +1992,25 @@ fn lvalue_list( fn resolve_function( context: &mut Context, + case: ResolveFunctionCase, loc: Loc, sp!(mloc, ma_): E::ModuleAccess, ty_args: Option>, ) -> ResolvedFunction { use E::ModuleAccess_ as EA; - match ma_ { - EA::Name(n) if N::BuiltinFunction_::all_names().contains(&n.value) => { + match (ma_, case) { + (EA::ModuleAccess(m, n), _) => match context.resolve_module_function(mloc, &m, &n) { + None => { + assert!(context.env.has_errors()); + ResolvedFunction::Unbound + } + Some(_) => ResolvedFunction::Module(Box::new(ResolvedModuleFunction { + module: m, + function: FunctionName(n), + ty_args, + })), + }, + (EA::Name(n), _) if N::BuiltinFunction_::all_names().contains(&n.value) => { match resolve_builtin_function(context, loc, &n, ty_args) { None => { assert!(context.env.has_errors()); @@ -1709,24 +2019,35 @@ fn resolve_function( Some(f) => ResolvedFunction::Builtin(sp(mloc, f)), } } - EA::Name(n) => { + (EA::Name(n), ResolveFunctionCase::UseFun) => { context.env.add_diag(diag!( NameResolution::UnboundUnscopedName, (n.loc, format!("Unbound function '{}' in current scope", n)), )); ResolvedFunction::Unbound } - EA::ModuleAccess(m, n) => match context.resolve_module_function(mloc, &m, &n) { - None => { - assert!(context.env.has_errors()); - ResolvedFunction::Unbound + (EA::Name(n), ResolveFunctionCase::Call) => { + match context.resolve_local( + n.loc, + NameResolution::UnboundUnscopedName, + |n| format!("Unbound function '{}' in current scope", n), + n, + ) { + None => { + assert!(context.env.has_errors()); + ResolvedFunction::Unbound + } + Some(v) => { + if ty_args.is_some() { + context.env.add_diag(diag!( + NameResolution::TooManyTypeArguments, + (mloc, "Invalid lambda call. Expected zero type arguments"), + )); + } + ResolvedFunction::Var(v) + } } - Some(_) => ResolvedFunction::Module(Box::new(ResolvedModuleFunction { - module: m, - function: FunctionName(n), - ty_args, - })), - }, + } } } @@ -1740,23 +2061,8 @@ fn resolve_builtin_function( Some(match b.value.as_str() { B::FREEZE => Freeze(check_builtin_ty_arg(context, loc, b, ty_args)), B::ASSERT_MACRO => { - let dep_msg = format!( - "'{}' function syntax has been deprecated and will be removed", - B::ASSERT_MACRO - ); - // TODO make this a tip/hint? - let help_msg = format!( - "Replace with '{0}!'. '{0}' has been replaced with a '{0}!' built-in macro so \ - that arguments are no longer eagerly evaluated", - B::ASSERT_MACRO - ); - context.env.add_diag(diag!( - Uncategorized::DeprecatedWillBeRemoved, - (b.loc, dep_msg), - (b.loc, help_msg), - )); check_builtin_ty_args(context, loc, b, 0, ty_args); - Assert(false) + Assert(/* is_macro, set by caller */ None) } _ => { context.env.add_diag(diag!( @@ -1943,19 +2249,34 @@ fn remove_unused_bindings_exp( | N::Exp_::Cast(e, _) | N::Exp_::Assign(_, e) | N::Exp_::Loop(_, e) - | N::Exp_::Give(_, e) + | N::Exp_::Give(_, _, e) | N::Exp_::Annotate(e, _) => remove_unused_bindings_exp(context, used, e), N::Exp_::IfElse(econd, et, ef) => { remove_unused_bindings_exp(context, used, econd); remove_unused_bindings_exp(context, used, et); remove_unused_bindings_exp(context, used, ef); } - N::Exp_::While(econd, _, ebody) => { + N::Exp_::While(_, econd, ebody) => { remove_unused_bindings_exp(context, used, econd); remove_unused_bindings_exp(context, used, ebody) } - N::Exp_::NamedBlock(_, s) => remove_unused_bindings_seq(context, used, s), - N::Exp_::Block(s) => remove_unused_bindings_seq(context, used, s), + N::Exp_::Block(N::Block { + name: _, + from_macro_argument: _, + seq, + }) => remove_unused_bindings_seq(context, used, seq), + N::Exp_::Lambda(N::Lambda { + parameters: sp!(_, parameters), + return_label: _, + return_type: _, + use_fun_color: _, + body, + }) => { + for (lvs, _) in parameters { + remove_unused_bindings_lvalues(context, used, lvs, /* report unused */ false) + } + remove_unused_bindings_exp(context, used, body) + } N::Exp_::FieldMutate(ed, e) => { remove_unused_bindings_exp_dotted(context, used, ed); remove_unused_bindings_exp(context, used, e) @@ -1971,13 +2292,14 @@ fn remove_unused_bindings_exp( } N::Exp_::Builtin(_, sp!(_, es)) | N::Exp_::Vector(_, _, sp!(_, es)) - | N::Exp_::ModuleCall(_, _, _, sp!(_, es)) + | N::Exp_::ModuleCall(_, _, _, _, sp!(_, es)) + | N::Exp_::VarCall(_, sp!(_, es)) | N::Exp_::ExpList(es) => { for e in es { remove_unused_bindings_exp(context, used, e) } } - N::Exp_::MethodCall(ed, _, _, sp!(_, es)) => { + N::Exp_::MethodCall(ed, _, _, _, sp!(_, es)) => { remove_unused_bindings_exp_dotted(context, used, ed); for e in es { remove_unused_bindings_exp(context, used, e) @@ -2000,7 +2322,7 @@ fn remove_unused_bindings_exp_dotted( } fn report_unused_local(context: &mut Context, sp!(loc, unused_): &N::Var) { - if !unused_.name.starts_with(|c: char| c.is_ascii_lowercase()) { + if unused_.starts_with_underscore() || !unused_.is_valid() { return; } let N::Var_ { name, id, color } = unused_; diff --git a/external-crates/move/crates/move-compiler/src/parser/ast.rs b/external-crates/move/crates/move-compiler/src/parser/ast.rs index 84f4db61530b5..159d09d76d86f 100644 --- a/external-crates/move/crates/move-compiler/src/parser/ast.rs +++ b/external-crates/move/crates/move-compiler/src/parser/ast.rs @@ -242,6 +242,7 @@ new_name!(FunctionName); pub const NATIVE_MODIFIER: &str = "native"; pub const ENTRY_MODIFIER: &str = "entry"; +pub const MACRO_MODIFIER: &str = "macro"; #[derive(PartialEq, Clone, Debug)] pub struct FunctionSignature { @@ -275,6 +276,7 @@ pub struct Function { pub loc: Loc, pub visibility: Visibility, pub entry: Option, + pub macro_: Option, pub signature: FunctionSignature, pub name: FunctionName, pub body: FunctionBody, @@ -495,6 +497,9 @@ pub type BindList = Spanned>; pub type BindWithRange = Spanned<(Bind, Exp)>; pub type BindWithRangeList = Spanned>; +pub type LambdaBindings_ = Vec<(BindList, Option)>; +pub type LambdaBindings = Spanned; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Value_ { // @ @@ -593,7 +598,12 @@ pub enum Exp_ { // f(earg,*) // f!(earg,*) - Call(NameAccessChain, bool, Option>, Spanned>), + Call( + NameAccessChain, + Option, + Option>, + Spanned>, + ), // tn {f1: e1, ... , f_n: e_n } Pack(NameAccessChain, Option>, Vec<(Field, Exp)>), @@ -613,12 +623,13 @@ pub enum Exp_ { // loop eloop Loop(Box), - // 'label: { seq } - NamedBlock(BlockLabel, Sequence), + // 'label: e + Labled(BlockLabel, Box), // { seq } Block(Sequence), - // fun (x1, ..., xn) e - Lambda(BindList, Box), // spec only + // |lv1, ..., lvn| e + // |lv1, ..., lvn| -> { e } + Lambda(LambdaBindings, Option, Box), // forall/exists x1 : e1, ..., xn [{ t1, .., tk } *] [where cond]: en. Quant( QuantKind, @@ -658,7 +669,13 @@ pub enum Exp_ { // e.f Dot(Box, Name), // e.f(earg,*) - DotCall(Box, Name, Option>, Spanned>), + DotCall( + Box, + Name, + /* is_macro */ Option, + Option>, + Spanned>, + ), // e[e'] Index(Box, Box), // spec only @@ -768,12 +785,36 @@ impl ModuleName { } impl Var { + pub const SYNTAX_IDENT_START: char = '$'; + pub fn is_underscore(&self) -> bool { self.0.value == symbol!("_") } pub fn starts_with_underscore(&self) -> bool { - self.0.value.starts_with('_') + Self::starts_with_underscore_name(self.0.value) + } + + pub fn starts_with_underscore_name(n: Symbol) -> bool { + n.starts_with('_') || n.starts_with("$_") + } + + pub fn is_syntax_identifier(&self) -> bool { + Self::is_syntax_identifier_name(self.0.value) + } + + pub fn is_syntax_identifier_name(n: Symbol) -> bool { + n.starts_with(Self::SYNTAX_IDENT_START) + } + + pub fn is_valid(&self) -> bool { + Self::is_valid_name(self.0.value) + } + + pub fn is_valid_name(s: Symbol) -> bool { + s.starts_with('_') + || s.starts_with(|c: char| c.is_ascii_lowercase()) + || Self::is_syntax_identifier_name(s) } } @@ -1498,6 +1539,7 @@ impl AstDebug for Function { loc: _loc, visibility, entry, + macro_, signature, name, body, @@ -1507,8 +1549,11 @@ impl AstDebug for Function { if entry.is_some() { w.write(&format!("{} ", ENTRY_MODIFIER)); } + if macro_.is_some() { + w.write(&format!("{} ", MACRO_MODIFIER)); + } if let FunctionBody_::Native = &body.value { - w.write("native "); + w.write(&format!("{} ", NATIVE_MODIFIER)); } w.write(&format!("fun {}", name)); signature.ast_debug(w); @@ -1743,7 +1788,7 @@ impl AstDebug for Exp_ { } E::Call(ma, is_macro, tys_opt, sp!(_, rhs)) => { ma.ast_debug(w); - if *is_macro { + if is_macro.is_some() { w.write("!"); } if let Some(ss) = tys_opt { @@ -1800,15 +1845,17 @@ impl AstDebug for Exp_ { w.write("loop "); e.ast_debug(w); } - E::NamedBlock(name, seq) => { - w.write(format!("'{}: ", name)); - w.block(|w| seq.ast_debug(w)) + E::Labled(name, e) => { + w.write(format!("'{name}: ")); + e.ast_debug(w) } E::Block(seq) => w.block(|w| seq.ast_debug(w)), - E::Lambda(sp!(_, bs), e) => { - w.write("fun "); + E::Lambda(sp!(_, bs), ty_opt, e) => { bs.ast_debug(w); - w.write(" "); + if let Some(ty) = ty_opt { + w.write(" -> "); + ty.ast_debug(w); + } e.ast_debug(w); } E::Quant(kind, sp!(_, rs), trs, c_opt, e) => { @@ -1884,9 +1931,12 @@ impl AstDebug for Exp_ { e.ast_debug(w); w.write(&format!(".{}", n)); } - E::DotCall(e, n, tys_opt, sp!(_, rhs)) => { + E::DotCall(e, n, is_macro, tys_opt, sp!(_, rhs)) => { e.ast_debug(w); w.write(&format!(".{}", n)); + if is_macro.is_some() { + w.write("!"); + } if let Some(ss) = tys_opt { w.write("<"); ss.ast_debug(w); @@ -2029,6 +2079,20 @@ impl AstDebug for Bind_ { } } +impl AstDebug for LambdaBindings_ { + fn ast_debug(&self, w: &mut AstWriter) { + w.write("|"); + w.comma(self, |w, (b, ty_opt)| { + b.ast_debug(w); + if let Some(ty) = ty_opt { + w.write(": "); + ty.ast_debug(w); + } + }); + w.write("| "); + } +} + impl AstDebug for FieldBindings { fn ast_debug(&self, w: &mut AstWriter) { match self { diff --git a/external-crates/move/crates/move-compiler/src/parser/lexer.rs b/external-crates/move/crates/move-compiler/src/parser/lexer.rs index 9a2f23f16517b..a099a647e16ab 100644 --- a/external-crates/move/crates/move-compiler/src/parser/lexer.rs +++ b/external-crates/move/crates/move-compiler/src/parser/lexer.rs @@ -21,6 +21,7 @@ pub enum Tok { NumTypedValue, ByteStringValue, Identifier, + SyntaxIdentifier, Exclaim, ExclaimEqual, Percent, @@ -89,6 +90,7 @@ pub enum Tok { Type, Match, BlockLabel, + MinusGreater, } impl fmt::Display for Tok { @@ -100,6 +102,7 @@ impl fmt::Display for Tok { NumTypedValue => "[NumTyped]", ByteStringValue => "[ByteString]", Identifier => "[Identifier]", + SyntaxIdentifier => "[SyntaxIdentifier]", Exclaim => "!", ExclaimEqual => "!=", Percent => "%", @@ -168,6 +171,7 @@ impl fmt::Display for Tok { Type => "type", Match => "match", BlockLabel => "'[Identifier]", + MinusGreater => "->", }; fmt::Display::fmt(s, formatter) } @@ -224,6 +228,10 @@ impl<'input> Lexer<'input> { make_loc(self.file_hash(), self.cur_start, self.cur_end) } + pub fn edition(&self) -> Edition { + self.edition + } + /// Strips line and block comments from input source, and collects documentation comments, /// putting them into a map indexed by the span of the comment region. Comments in the original /// source will be replaced by spaces, such that positions of source items stay unchanged. @@ -667,6 +675,20 @@ fn find_token( (Ok(get_name_token(edition, &text[..len])), len) } } + '$' => { + if text.len() > 1 && text[1..].starts_with(|c| matches!(c,'A'..='Z' | 'a'..='z' | '_')) + { + let len = get_name_len(&text[1..]); + (Ok(Tok::SyntaxIdentifier), len + 1) + } else { + let loc = make_loc(file_hash, start_offset, start_offset); + let diag = maybe_diag! { Box::new(diag!( + Syntax::UnexpectedToken, + (loc, "Expected an identifier following '$', e.g. '$x'"), + )) }; + (Err(diag), 1) + } + } '&' => { if text.starts_with("&mut ") { (Ok(Tok::AmpMut), 5) @@ -726,6 +748,20 @@ fn find_token( (Ok(Tok::Colon), 1) } } + '.' => { + if text.starts_with("..") { + (Ok(Tok::PeriodPeriod), 2) + } else { + (Ok(Tok::Period), 1) + } + } + '-' => { + if text.starts_with("->") { + (Ok(Tok::MinusGreater), 2) + } else { + (Ok(Tok::Minus), 1) + } + } '%' => (Ok(Tok::Percent), 1), '(' => (Ok(Tok::LParen), 1), ')' => (Ok(Tok::RParen), 1), @@ -734,14 +770,6 @@ fn find_token( '*' => (Ok(Tok::Star), 1), '+' => (Ok(Tok::Plus), 1), ',' => (Ok(Tok::Comma), 1), - '-' => (Ok(Tok::Minus), 1), - '.' => { - if text.starts_with("..") { - (Ok(Tok::PeriodPeriod), 2) - } else { - (Ok(Tok::Period), 1) - } - } '/' => (Ok(Tok::Slash), 1), ';' => (Ok(Tok::Semicolon), 1), '^' => (Ok(Tok::Caret), 1), diff --git a/external-crates/move/crates/move-compiler/src/parser/syntax.rs b/external-crates/move/crates/move-compiler/src/parser/syntax.rs index 8cefb019ad622..5446dcd2939c6 100644 --- a/external-crates/move/crates/move-compiler/src/parser/syntax.rs +++ b/external-crates/move/crates/move-compiler/src/parser/syntax.rs @@ -381,6 +381,24 @@ fn parse_identifier(context: &mut Context) -> Result> { Ok(spanned(context.tokens.file_hash(), start_loc, end_loc, id)) } +// Parse a macro parameter identifier. +// The name, SyntaxIdentifier, comes from the usage of the identifier to perform expression +// substitution in a macro invocation, i.e. a syntactic substitution. +// SyntaxIdentifier = +fn parse_syntax_identifier(context: &mut Context) -> Result> { + if context.tokens.peek() != Tok::SyntaxIdentifier { + return Err(unexpected_token_error( + context.tokens, + "an identifier prefixed by '$'", + )); + } + let start_loc = context.tokens.start_loc(); + let id = context.tokens.content().into(); + context.tokens.advance()?; + let end_loc = context.tokens.previous_end_loc(); + Ok(spanned(context.tokens.file_hash(), start_loc, end_loc, id)) +} + // Parse a numerical address value // NumericalAddress = fn parse_address_bytes( @@ -402,7 +420,7 @@ fn parse_address_bytes( } // Parse the beginning of an access, either an address or an identifier: -// LeadingNameAccess = | +// LeadingNameAccess = | | fn parse_leading_name_access(context: &mut Context) -> Result> { parse_leading_name_access_(context, false, || "an address or an identifier") } @@ -424,6 +442,16 @@ fn parse_leading_name_access_<'a, F: FnOnce() -> &'a str>( }; Ok(sp(loc, name)) } + Tok::SyntaxIdentifier => { + let loc = current_token_loc(context.tokens); + let n = parse_syntax_identifier(context)?; + let name = if global_name { + LeadingNameAccess_::GlobalAddress(n) + } else { + LeadingNameAccess_::Name(n) + }; + Ok(sp(loc, name)) + } Tok::NumValue => { let sp!(loc, addr) = parse_address_bytes(context)?; Ok(sp(loc, LeadingNameAccess_::AnonymousAddress(addr))) @@ -433,9 +461,12 @@ fn parse_leading_name_access_<'a, F: FnOnce() -> &'a str>( } // Parse a variable name: -// Var = +// Var = | fn parse_var(context: &mut Context) -> Result> { - Ok(Var(parse_identifier(context)?)) + Ok(Var(match context.tokens.peek() { + Tok::SyntaxIdentifier => parse_syntax_identifier(context)?, + _ => parse_identifier(context)?, + })) } // Parse a field name: @@ -537,6 +568,7 @@ struct Modifiers { visibility: Option, entry: Option, native: Option, + macro_: Option, } impl Modifiers { @@ -545,6 +577,7 @@ impl Modifiers { visibility: None, entry: None, native: None, + macro_: None, } } } @@ -555,19 +588,33 @@ impl Modifiers { // ModuleMemberModifiers checks for uniqueness, meaning each individual ModuleMemberModifier can // appear only once fn parse_module_member_modifiers(context: &mut Context) -> Result> { + fn duplicate_modifier_error( + context: &mut Context, + loc: Loc, + prev_loc: Loc, + modifier_name: &'static str, + ) { + let msg = format!("Duplicate '{modifier_name}' modifier"); + let prev_msg = format!("'{modifier_name}' modifier previously given here"); + context.env.add_diag(diag!( + Declarations::DuplicateItem, + (loc, msg), + (prev_loc, prev_msg), + )); + } + let mut mods = Modifiers::empty(); loop { match context.tokens.peek() { Tok::Public => { let vis = parse_visibility(context)?; if let Some(prev_vis) = mods.visibility { - let msg = "Duplicate visibility modifier".to_string(); - let prev_msg = "Visibility modifier previously given here".to_string(); - context.env.add_diag(diag!( - Declarations::DuplicateItem, - (vis.loc().unwrap(), msg), - (prev_vis.loc().unwrap(), prev_msg), - )); + duplicate_modifier_error( + context, + vis.loc().unwrap(), + prev_vis.loc().unwrap(), + Visibility::PUBLIC, + ) } mods.visibility = Some(vis) } @@ -575,13 +622,7 @@ fn parse_module_member_modifiers(context: &mut Context) -> Result Result { + let loc = current_token_loc(context.tokens); + context.tokens.advance()?; + if let Some(prev_loc) = mods.macro_ { + duplicate_modifier_error(context, loc, prev_loc, MACRO_MODIFIER) + } + mods.macro_ = Some(loc) + } _ => break, } } Ok(mods) } +fn check_no_modifier( + context: &mut Context, + modifier_name: &'static str, + modifier_loc: Option, + module_member: &str, +) { + const LOCATIONS: &[(&str, &str)] = &[ + (NATIVE_MODIFIER, "functions or structs"), + (ENTRY_MODIFIER, "functions"), + (MACRO_MODIFIER, "functions"), + ]; + let Some(loc) = modifier_loc else { return }; + let location = LOCATIONS + .iter() + .find(|(name, _)| *name == modifier_name) + .unwrap() + .1; + let msg = format!( + "Invalid {module_member} declaration. '{modifier_name}' is used only on {location}", + ); + context + .env + .add_diag(diag!(Syntax::InvalidModifier, (loc, msg))); +} + // Parse a function visibility modifier: // Visibility = "public" ( "( "friend" | "package" ")" )? fn parse_visibility(context: &mut Context) -> Result> { @@ -854,15 +922,23 @@ fn parse_bind_list(context: &mut Context) -> Result> { // Parse a list of bindings for lambda. // LambdaBindList = -// "|" Comma "|" -fn parse_lambda_bind_list(context: &mut Context) -> Result> { +// "|" Comma "|" +fn parse_lambda_bind_list(context: &mut Context) -> Result> { let start_loc = context.tokens.start_loc(); let b = parse_comma_list( context, Tok::Pipe, Tok::Pipe, - parse_bind, - "a variable or structure binding", + |context| { + let b = parse_bind_list(context)?; + let ty_opt = if match_token(context.tokens, Tok::Colon)? { + Some(parse_type(context)?) + } else { + None + }; + Ok((b, ty_opt)) + }, + "a binding", )?; let end_loc = context.tokens.previous_end_loc(); Ok(spanned(context.tokens.file_hash(), start_loc, end_loc, b)) @@ -1114,14 +1190,8 @@ fn parse_term(context: &mut Context) -> Result> { )); } } - Tok::Identifier | Tok::RestrictedIdentifier => parse_name_exp(context)?, - - Tok::BlockLabel => { - // TODO: improve error messages around this. - let label = parse_block_label(context)?; - consume_token(context.tokens, Tok::Colon)?; - consume_token(context.tokens, Tok::LBrace)?; - Exp_::NamedBlock(label, parse_sequence(context)?) + Tok::Identifier | Tok::RestrictedIdentifier | Tok::SyntaxIdentifier => { + parse_name_exp(context)? } Tok::NumValue => { @@ -1206,7 +1276,14 @@ fn parse_term(context: &mut Context) -> Result> { fn is_control_exp(tok: Tok) -> bool { matches!( tok, - Tok::Break | Tok::Continue | Tok::If | Tok::While | Tok::Loop | Tok::Return | Tok::Abort + Tok::Break + | Tok::Continue + | Tok::If + | Tok::While + | Tok::Loop + | Tok::Return + | Tok::Abort + | Tok::BlockLabel ) } @@ -1358,6 +1435,12 @@ fn parse_control_exp(context: &mut Context) -> Result<(Exp, bool), Box { + let name = parse_block_label(context)?; + consume_token(context.tokens, Tok::Colon)?; + let (e, ends_in_block) = parse_exp_or_sequence(context)?; + (Exp_::Labled(name, Box::new(e)), ends_in_block) + } _ => unreachable!(), }; let end_loc = context.tokens.previous_end_loc(); @@ -1369,33 +1452,42 @@ fn parse_control_exp(context: &mut Context) -> Result<(Exp, bool), Box "{" Comma "}" // | "(" Comma ")" -// | "!" "(" Comma ")" +// | "!" "(" Comma ")" // | fn parse_name_exp(context: &mut Context) -> Result> { let name = parse_name_access_chain(context, || { panic!("parse_name_exp with something other than a ModuleAccess") })?; - // There's an ambiguity if the name is followed by a '<'. If there is no whitespace - // after the name, treat it as the start of a list of type arguments. Otherwise - // assume that the '<' is a boolean operator. - let mut tys = None; - if context.tokens.peek() == Tok::Exclaim { - // TODO(macro) handle type arguments + let is_macro = if let Tok::Exclaim = context.tokens.peek() { + let loc = current_token_loc(context.tokens); context.tokens.advance()?; - let is_macro = true; - let rhs = parse_call_args(context)?; - return Ok(Exp_::Call(name, is_macro, tys, rhs)); - } - let start_loc = context.tokens.start_loc(); + Some(loc) + } else { + None + }; - if context.tokens.peek() == Tok::Less && name.loc.end() as usize == start_loc { + // There's an ambiguity if the name is followed by a '<'. + // If there is no whitespace after the name or if a macro call has been started, + // treat it as the start of a list of type arguments. + // Otherwise, assume that the '<' is a boolean operator. + let mut tys = None; + let start_loc = context.tokens.start_loc(); + if context.tokens.peek() == Tok::Less + && (name.loc.end() as usize == start_loc || is_macro.is_some()) + { let loc = make_loc(context.tokens.file_hash(), start_loc, start_loc); tys = parse_optional_type_args(context) .map_err(|diag| add_type_args_ambiguity_label(loc, diag))?; } match context.tokens.peek() { + _ if is_macro.is_some() => { + // if in a macro, we must have a call + let rhs = parse_call_args(context)?; + Ok(Exp_::Call(name, is_macro, tys, rhs)) + } + // Pack: "{" Comma "}" Tok::LBrace => { let fs = parse_comma_list( @@ -1409,10 +1501,10 @@ fn parse_name_exp(context: &mut Context) -> Result> { } // Call: "(" Comma ")" - Tok::Exclaim | Tok::LParen => { - let is_macro = false; + Tok::LParen => { + debug_assert!(is_macro.is_none()); let rhs = parse_call_args(context)?; - Ok(Exp_::Call(name, is_macro, tys, rhs)) + Ok(Exp_::Call(name, None, tys, rhs)) } // Other name reference... @@ -1479,22 +1571,41 @@ fn at_start_of_exp(context: &mut Context) -> bool { | Tok::Loop | Tok::Return | Tok::While + | Tok::BlockLabel ) } // Parse an expression: // Exp = -// spec only +// +// | "->" "{" // | spec only // | // | "=" fn parse_exp(context: &mut Context) -> Result> { let start_loc = context.tokens.start_loc(); let exp = match context.tokens.peek() { - Tok::Pipe => { - let bindings = parse_lambda_bind_list(context)?; - let body = Box::new(parse_exp(context)?); - Exp_::Lambda(bindings, body) + tok @ Tok::PipePipe | tok @ Tok::Pipe => { + let bindings = if tok == Tok::PipePipe { + let loc = current_token_loc(context.tokens); + consume_token(context.tokens, Tok::PipePipe)?; + sp(loc, vec![]) + } else { + parse_lambda_bind_list(context)? + }; + let (ret_ty_opt, body) = if context.tokens.peek() == Tok::MinusGreater { + context.tokens.advance()?; + let ret_ty = parse_type(context)?; + let start_loc = context.tokens.start_loc(); + consume_token(context.tokens, Tok::LBrace)?; + let block_ = Exp_::Block(parse_sequence(context)?); + let end_loc = context.tokens.previous_end_loc(); + let block = spanned(context.tokens.file_hash(), start_loc, end_loc, block_); + (Some(ret_ty), block) + } else { + (None, parse_exp(context)?) + }; + Exp_::Lambda(bindings, ret_ty_opt, Box::new(body)) } Tok::Identifier if is_quant(context) => parse_quant(context)?, _ => { @@ -1749,6 +1860,13 @@ fn parse_dot_or_index_chain(context: &mut Context) -> Result Result Result bool { - // TODO(macro) consider macro Tok::Exlaim let call_start = context.tokens.start_loc(); let peeked = context.tokens.peek(); - (peeked == Tok::Less && n.loc.end() as usize == call_start) || peeked == Tok::LParen + (peeked == Tok::Less && n.loc.end() as usize == call_start) + || peeked == Tok::LParen + || peeked == Tok::Exclaim } // Lookahead to determine whether this is a quantifier. This matches @@ -1960,7 +2079,7 @@ fn parse_quant_binding(context: &mut Context) -> Result, Bo fn make_builtin_call(loc: Loc, name: Symbol, type_args: Option>, args: Vec) -> Exp { let maccess = sp(loc, NameAccessChain_::One(sp(loc, name))); - sp(loc, Exp_::Call(maccess, false, type_args, sp(loc, args))) + sp(loc, Exp_::Call(maccess, None, type_args, sp(loc, args))) } //************************************************************************************************** @@ -1997,9 +2116,34 @@ fn parse_type(context: &mut Context) -> Result> { let t = parse_type(context)?; Type_::Ref(true, Box::new(t)) } - Tok::Pipe => { - let args = parse_comma_list(context, Tok::Pipe, Tok::Pipe, parse_type, "a type")?; - let result = parse_type(context)?; + tok @ Tok::PipePipe | tok @ Tok::Pipe => { + let args = if tok == Tok::PipePipe { + context.tokens.advance()?; + vec![] + } else { + parse_comma_list(context, Tok::Pipe, Tok::Pipe, parse_type, "a type")? + }; + let result = if context + .tokens + .edition() + .supports(FeatureGate::Move2024Keywords) + { + // 2024 syntax + if context.tokens.peek() == Tok::MinusGreater { + context.tokens.advance()?; + parse_type(context)? + } else { + spanned( + context.tokens.file_hash(), + start_loc, + context.tokens.start_loc(), + Type_::Unit, + ) + } + } else { + // legacy spec syntax + parse_type(context)? + }; return Ok(spanned( context.tokens.file_hash(), start_loc, @@ -2072,11 +2216,16 @@ fn parse_ability(context: &mut Context) -> Result> { // Parse a type parameter: // TypeParameter = -// ? +// ? +// | ? // Constraint = // ":" (+ )* fn parse_type_parameter(context: &mut Context) -> Result<(Name, Vec), Box> { - let n = parse_identifier(context)?; + let n = if context.tokens.peek() == Tok::SyntaxIdentifier { + parse_syntax_identifier(context)? + } else { + parse_identifier(context)? + }; let ability_constraints = if match_token(context.tokens, Tok::Colon)? { parse_list( @@ -2183,6 +2332,7 @@ fn parse_function_decl( visibility, entry, native, + macro_, } = modifiers; // "fun" @@ -2257,6 +2407,7 @@ fn parse_function_decl( loc, visibility: visibility.unwrap_or(Visibility::Internal), entry, + macro_, signature, name, body, @@ -2302,19 +2453,13 @@ fn parse_struct_decl( visibility, entry, native, + macro_, } = modifiers; check_struct_visibility(visibility, context); - if let Some(loc) = entry { - let msg = format!( - "Invalid constant declaration. '{}' is used only on functions", - ENTRY_MODIFIER - ); - context - .env - .add_diag(diag!(Syntax::InvalidModifier, (loc, msg))); - } + check_no_modifier(context, ENTRY_MODIFIER, entry, "struct"); + check_no_modifier(context, MACRO_MODIFIER, macro_, "struct"); consume_token(context.tokens, Tok::Struct)?; @@ -2552,6 +2697,7 @@ fn parse_constant_decl( visibility, entry, native, + macro_, } = modifiers; if let Some(vis) = visibility { let msg = "Invalid constant declaration. Constants cannot have visibility modifiers as \ @@ -2560,21 +2706,9 @@ fn parse_constant_decl( .env .add_diag(diag!(Syntax::InvalidModifier, (vis.loc().unwrap(), msg))); } - if let Some(loc) = entry { - let msg = format!( - "Invalid constant declaration. '{}' is used only on functions", - ENTRY_MODIFIER - ); - context - .env - .add_diag(diag!(Syntax::InvalidModifier, (loc, msg))); - } - if let Some(loc) = native { - let msg = "Invalid constant declaration. 'native' constants are not supported"; - context - .env - .add_diag(diag!(Syntax::InvalidModifier, (loc, msg))); - } + check_no_modifier(context, NATIVE_MODIFIER, native, "constant"); + check_no_modifier(context, ENTRY_MODIFIER, entry, "constant"); + check_no_modifier(context, MACRO_MODIFIER, macro_, "constant"); consume_token(context.tokens, Tok::Const)?; let name = ConstantName(parse_identifier(context)?); expect_token!( @@ -2726,22 +2860,11 @@ fn parse_use_decl( visibility, entry, native, + macro_, } = modifiers; - if let Some(loc) = entry { - let msg = format!( - "Invalid use declaration. '{}' is used only on functions", - ENTRY_MODIFIER - ); - context - .env - .add_diag(diag!(Syntax::InvalidModifier, (loc, msg))); - } - if let Some(loc) = native { - let msg = "Invalid use declaration. Unexpected 'native' modifier"; - context - .env - .add_diag(diag!(Syntax::InvalidModifier, (loc, msg))); - } + check_no_modifier(context, NATIVE_MODIFIER, native, "use"); + check_no_modifier(context, ENTRY_MODIFIER, entry, "use"); + check_no_modifier(context, MACRO_MODIFIER, macro_, "use"); let use_ = match context.tokens.peek() { Tok::Fun => { consume_token(context.tokens, Tok::Fun).unwrap(); diff --git a/external-crates/move/crates/move-compiler/src/shared/mod.rs b/external-crates/move/crates/move-compiler/src/shared/mod.rs index 672bd4328cdbe..4ee87ac3b2f28 100644 --- a/external-crates/move/crates/move-compiler/src/shared/mod.rs +++ b/external-crates/move/crates/move-compiler/src/shared/mod.rs @@ -35,6 +35,8 @@ pub mod remembering_unique_map; pub mod unique_map; pub mod unique_set; +pub use ast_debug::AstDebug; + //************************************************************************************************** // Numbers //************************************************************************************************** @@ -389,6 +391,10 @@ impl CompilationEnv { self.diags.len() } + pub fn count_diags_at_or_above_severity(&self, threshold: Severity) -> usize { + self.diags.count_diags_at_or_above_severity(threshold) + } + pub fn has_diags_at_or_above_severity(&self, threshold: Severity) -> bool { match self.diags.max_severity() { Some(max) if max >= threshold => true, diff --git a/external-crates/move/crates/move-compiler/src/shared/program_info.rs b/external-crates/move/crates/move-compiler/src/shared/program_info.rs index 829081b56c6fd..d285431c30a3a 100644 --- a/external-crates/move/crates/move-compiler/src/shared/program_info.rs +++ b/external-crates/move/crates/move-compiler/src/shared/program_info.rs @@ -24,6 +24,7 @@ pub struct FunctionInfo { pub defined_loc: Loc, pub visibility: Visibility, pub entry: Option, + pub macro_: Option, pub signature: FunctionSignature, } @@ -62,6 +63,7 @@ macro_rules! program_info { defined_loc: fname.loc(), visibility: fdef.visibility.clone(), entry: fdef.entry, + macro_: fdef.macro_, signature: fdef.signature.clone(), }); let constants = mdef.constants.ref_map(|cname, cdef| ConstantInfo { diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/coin_field.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/coin_field.rs index c3ebe46674ed6..3e287aa2f26d3 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/coin_field.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/coin_field.rs @@ -75,6 +75,8 @@ fn is_field_coin_type(sp!(_, t): &N::Type) -> bool { let sp!(_, tname) = tname; tname.is(SUI_PKG_NAME, COIN_MOD_NAME, COIN_STRUCT_NAME) } - T::Unit | T::Param(_) | T::Var(_) | T::Anything | T::UnresolvedError => false, + T::Unit | T::Param(_) | T::Var(_) | T::Anything | T::UnresolvedError | T::Fun(_, _) => { + false + } } } diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/freeze_wrapped.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/freeze_wrapped.rs index 160111879dd6b..8902416a8e299 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/freeze_wrapped.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/freeze_wrapped.rs @@ -188,7 +188,8 @@ impl<'a> Context<'a> { | T::Ref(_, _) | T::Var(_) | T::Anything - | T::UnresolvedError => None, + | T::UnresolvedError + | T::Fun(_, _) => None, } } diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs index 8ab67738b5822..f71e51771ed9b 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs @@ -143,7 +143,7 @@ pub fn base_type(t: &N::Type) -> Option<&N::Type> { match &t.value { T::Ref(_, inner_t) => base_type(inner_t), T::Apply(_, _, _) | T::Param(_) => Some(t), - T::Unit | T::Var(_) | T::Anything | T::UnresolvedError => None, + T::Unit | T::Var(_) | T::Anything | T::UnresolvedError | T::Fun(_, _) => None, } } diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs b/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs index 173c8c357a8ee..7f6b0492ad60b 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs @@ -246,6 +246,7 @@ fn function(context: &mut Context, name: FunctionName, fdef: &mut T::Function) { body, warning_filter: _, index: _, + macro_: _, attributes, entry, } = fdef; @@ -667,7 +668,8 @@ fn is_mut_clock(param_ty: &Type) -> bool { | Type_::Param(_) | Type_::Var(_) | Type_::Anything - | Type_::UnresolvedError => false, + | Type_::UnresolvedError + | Type_::Fun(_, _) => false, } } @@ -730,7 +732,7 @@ fn is_entry_primitive_ty(param_ty: &Type) -> bool { Type_::Unit => false, // Error case nothing to do - Type_::UnresolvedError | Type_::Anything | Type_::Var(_) => true, + Type_::UnresolvedError | Type_::Anything | Type_::Var(_) | Type_::Fun(_, _) => true, } } @@ -760,7 +762,11 @@ fn is_entry_object_ty_inner(param_ty: &Type) -> bool { Type_::Apply(Some(abilities), _, _) => abilities.has_ability_(Ability_::Key), // Error case nothing to do - Type_::UnresolvedError | Type_::Anything | Type_::Var(_) | Type_::Unit => true, + Type_::UnresolvedError + | Type_::Anything + | Type_::Var(_) + | Type_::Unit + | Type_::Fun(_, _) => true, // Unreachable cases Type_::Apply(None, _, _) => unreachable!("ICE abilities should have been expanded"), } @@ -821,7 +827,7 @@ fn entry_return( } } // Error case nothing to do - Type_::UnresolvedError | Type_::Anything | Type_::Var(_) => (), + Type_::UnresolvedError | Type_::Anything | Type_::Var(_) | Type_::Fun(_, _) => (), // Unreachable cases Type_::Apply(None, _, _) => unreachable!("ICE abilities should have been expanded"), } diff --git a/external-crates/move/crates/move-compiler/src/typing/ast.rs b/external-crates/move/crates/move-compiler/src/typing/ast.rs index 15e8301786145..48af17c3ae759 100644 --- a/external-crates/move/crates/move-compiler/src/typing/ast.rs +++ b/external-crates/move/crates/move-compiler/src/typing/ast.rs @@ -9,7 +9,10 @@ use crate::{ BlockLabel, FunctionSignature, Neighbor, StructDefinition, Type, TypeName_, Type_, UseFuns, Var, }, - parser::ast::{BinOp, ConstantName, Field, FunctionName, StructName, UnaryOp, ENTRY_MODIFIER}, + parser::ast::{ + BinOp, ConstantName, Field, FunctionName, StructName, UnaryOp, ENTRY_MODIFIER, + MACRO_MODIFIER, NATIVE_MODIFIER, + }, shared::{ast_debug::*, program_info::TypingProgramInfo, unique_map::UniqueMap, Name}, }; use move_ir_types::location::*; @@ -66,6 +69,7 @@ pub struct ModuleDefinition { pub enum FunctionBody_ { Defined(Sequence), Native, + Macro, } pub type FunctionBody = Spanned; @@ -77,6 +81,7 @@ pub struct Function { pub attributes: Attributes, pub visibility: Visibility, pub entry: Option, + pub macro_: Option, pub signature: FunctionSignature, pub body: FunctionBody, } @@ -136,7 +141,7 @@ pub struct ModuleCall { #[allow(clippy::large_enum_variant)] pub enum BuiltinFunction_ { Freeze(Type), - Assert(/* is_macro */ bool), + Assert(/* is_macro */ Option), } pub type BuiltinFunction = Spanned; @@ -162,7 +167,7 @@ pub enum UnannotatedExp_ { Vector(Loc, usize, Box, Box), IfElse(Box, Box, Box), - While(Box, BlockLabel, Box), + While(BlockLabel, Box, Box), Loop { name: BlockLabel, has_break: bool, @@ -199,9 +204,6 @@ pub struct Exp { pub ty: Type, pub exp: UnannotatedExp, } -pub fn exp(ty: Type, exp: UnannotatedExp) -> Exp { - Exp { ty, exp } -} pub type Sequence = (UseFuns, VecDeque); #[derive(Debug, PartialEq, Clone)] @@ -218,20 +220,6 @@ pub enum ExpListItem { Splat(Loc, Exp, Vec), } -pub fn single_item(e: Exp) -> ExpListItem { - let ty = Box::new(e.ty.clone()); - ExpListItem::Single(e, ty) -} - -pub fn splat_item(splat_loc: Loc, e: Exp) -> ExpListItem { - let ss = match &e.ty { - sp!(_, Type_::Unit) => vec![], - sp!(_, Type_::Apply(_, sp!(_, TypeName_::Multiple(_)), ss)) => ss.clone(), - _ => panic!("ICE splat of non list type"), - }; - ExpListItem::Splat(splat_loc, e, ss) -} - //************************************************************************************************** // impls //************************************************************************************************** @@ -247,6 +235,41 @@ impl BuiltinFunction_ { } } +pub fn explist(loc: Loc, mut es: Vec) -> Exp { + match es.len() { + 0 => { + let e__ = UnannotatedExp_::Unit { trailing: false }; + let ty = sp(loc, Type_::Unit); + exp(ty, sp(loc, e__)) + } + 1 => es.pop().unwrap(), + _ => { + let tys = es.iter().map(|e| e.ty.clone()).collect(); + let items = es.into_iter().map(single_item).collect(); + let ty = Type_::multiple(loc, tys); + exp(ty, sp(loc, UnannotatedExp_::ExpList(items))) + } + } +} + +pub fn exp(ty: Type, exp: UnannotatedExp) -> Exp { + Exp { ty, exp } +} + +pub fn single_item(e: Exp) -> ExpListItem { + let ty = Box::new(e.ty.clone()); + ExpListItem::Single(e, ty) +} + +pub fn splat_item(splat_loc: Loc, e: Exp) -> ExpListItem { + let ss = match &e.ty { + sp!(_, Type_::Unit) => vec![], + sp!(_, Type_::Apply(_, sp!(_, TypeName_::Multiple(_)), ss)) => ss.clone(), + _ => panic!("ICE splat of non list type"), + }; + ExpListItem::Splat(splat_loc, e, ss) +} + //************************************************************************************************** // Display //************************************************************************************************** @@ -346,6 +369,7 @@ impl AstDebug for (FunctionName, &Function) { attributes, visibility, entry, + macro_, signature, body, }, @@ -356,14 +380,24 @@ impl AstDebug for (FunctionName, &Function) { if entry.is_some() { w.write(&format!("{} ", ENTRY_MODIFIER)); } + if macro_.is_some() { + w.write(&format!("{} ", MACRO_MODIFIER)); + } if let FunctionBody_::Native = &body.value { - w.write("native "); + w.write(&format!("{} ", NATIVE_MODIFIER)); } w.write(&format!("fun#{index} {name}")); signature.ast_debug(w); - match &body.value { - FunctionBody_::Defined(body) => body.ast_debug(w), - FunctionBody_::Native => w.writeln(";"), + body.ast_debug(w); + } +} + +impl AstDebug for FunctionBody_ { + fn ast_debug(&self, w: &mut AstWriter) { + use FunctionBody_ as F; + match self { + F::Defined(seq) => seq.ast_debug(w), + F::Native | F::Macro => w.writeln(";"), } } } @@ -507,9 +541,8 @@ impl AstDebug for UnannotatedExp_ { f.ast_debug(w); } E::While(name, b, e) => { - w.write("while@"); name.ast_debug(w); - w.write(" ("); + w.write(": while ("); b.ast_debug(w); w.write(")"); e.ast_debug(w); @@ -519,13 +552,12 @@ impl AstDebug for UnannotatedExp_ { has_break, body, } => { - w.write("loop"); + name.ast_debug(w); + w.write(": loop"); if *has_break { w.write("#with_break"); } w.write(" "); - name.ast_debug(w); - w.write(": "); body.ast_debug(w); } E::NamedBlock(name, seq) => { diff --git a/external-crates/move/crates/move-compiler/src/typing/core.rs b/external-crates/move/crates/move-compiler/src/typing/core.rs index 294ba9c7ea1e3..9e77e17bd8bf9 100644 --- a/external-crates/move/crates/move-compiler/src/typing/core.rs +++ b/external-crates/move/crates/move-compiler/src/typing/core.rs @@ -4,10 +4,14 @@ use crate::{ debug_display, diag, - diagnostics::{codes::NameResolution, Diagnostic}, + diagnostics::{ + codes::{NameResolution, TypeSafety}, + Diagnostic, + }, expansion::ast::{AbilitySet, ModuleIdent, ModuleIdent_, Visibility}, + ice, naming::ast::{ - self as N, BlockLabel, BuiltinTypeName_, ResolvedUseFuns, StructDefinition, + self as N, BlockLabel, BuiltinTypeName_, Color, ResolvedUseFuns, StructDefinition, StructTypeParameter, TParam, TParamID, TVar, Type, TypeName, TypeName_, Type_, UseFunKind, Var, }, @@ -19,13 +23,17 @@ use crate::{ }; use move_ir_types::location::*; use move_symbol_pool::Symbol; -use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::{ + cell::RefCell, + collections::{BTreeMap, BTreeSet, HashMap}, +}; //************************************************************************************************** // Context //************************************************************************************************** -struct UseFunsScope { +pub struct UseFunsScope { + color: Option, count: usize, use_funs: ResolvedUseFuns, } @@ -52,14 +60,32 @@ pub struct Local { pub used_mut: Option, } +#[derive(Debug)] +pub struct MacroCall { + pub module: ModuleIdent, + pub function: FunctionName, + pub invocation: Loc, + pub scope_color: Color, +} + +#[derive(Debug)] +pub enum MacroExpansion { + Call(Box), + // An argument to a macro, where the entire expression was substituted in + Argument { scope_color: Color }, +} + pub struct Context<'env> { pub modules: NamingProgramInfo, + macros: UniqueMap>, pub env: &'env mut CompilationEnv, use_funs: Vec, pub current_package: Option, pub current_module: Option, pub current_function: Option, + pub in_macro_function: bool, + max_variable_color: RefCell, pub return_type: Option, locals: UniqueMap, @@ -76,6 +102,20 @@ pub struct Context<'env> { /// collects all used module members (functions and constants) but it's a superset of these in /// that it may contain other identifiers that do not in fact represent a function or a constant pub used_module_members: BTreeMap>, + /// Current macros being expanded + pub macro_expansion: Vec, + /// Stack of items from `macro_expansion` pushed/popped when entering/leaving a lambda expansion + /// This is to prevent accidentally thinking we are in a recursive call if a macro is used + /// inside a lambda body + pub lambda_expansion: Vec>, +} + +pub struct ResolvedFunctionType { + pub declared: Loc, + pub macro_: Option, + pub ty_args: Vec, + pub params: Vec<(Var, Type)>, + pub return_: Type, } impl UseFunsScope { @@ -107,7 +147,11 @@ impl UseFunsScope { use_funs.insert(tn.clone(), public_methods); } } - UseFunsScope { count, use_funs } + UseFunsScope { + color: None, + count, + use_funs, + } } } @@ -124,19 +168,33 @@ impl<'env> Context<'env> { current_package: None, current_module: None, current_function: None, + in_macro_function: false, + max_variable_color: RefCell::new(0), return_type: None, constraints: vec![], locals: UniqueMap::new(), modules: info, + macros: UniqueMap::new(), named_block_map: BTreeMap::new(), env, new_friends: BTreeSet::new(), used_module_members: BTreeMap::new(), + macro_expansion: vec![], + lambda_expansion: vec![], } } + pub fn set_macros( + &mut self, + macros: UniqueMap>, + ) { + debug_assert!(self.macros.is_empty()); + self.macros = macros; + } + pub fn add_use_funs_scope(&mut self, new_scope: N::UseFuns) { let N::UseFuns { + color, resolved: new_scope, implicit_candidates, } = new_scope; @@ -145,13 +203,14 @@ impl<'env> Context<'env> { "ICE use fun candidates should have been resolved" ); let cur = self.use_funs.last_mut().unwrap(); - if new_scope.is_empty() { + if new_scope.is_empty() && cur.color == Some(color) { cur.count += 1; return; } self.use_funs.push(UseFunsScope { count: 1, use_funs: new_scope, + color: Some(color), }) } @@ -159,12 +218,11 @@ impl<'env> Context<'env> { let cur = self.use_funs.last_mut().unwrap(); if cur.count > 1 { cur.count -= 1; - return N::UseFuns { - resolved: N::ResolvedUseFuns::new(), - implicit_candidates: UniqueMap::new(), - }; + return N::UseFuns::new(cur.color.unwrap_or(0)); } - let UseFunsScope { use_funs, .. } = self.use_funs.pop().unwrap(); + let UseFunsScope { + use_funs, color, .. + } = self.use_funs.pop().unwrap(); for (tn, methods) in use_funs.iter() { let unused = methods.iter().filter(|(_, _, uf)| !uf.used); for (_, method, use_fun) in unused { @@ -193,6 +251,7 @@ impl<'env> Context<'env> { } N::UseFuns { resolved: use_funs, + color: color.unwrap_or(0), implicit_candidates: UniqueMap::new(), } } @@ -202,13 +261,123 @@ impl<'env> Context<'env> { tn: &TypeName, method: Name, ) -> Option<(ModuleIdent, FunctionName)> { + let cur_color = self.use_funs.last().unwrap().color; self.use_funs.iter_mut().rev().find_map(|scope| { + // scope color is None for global scope, which is always in consideration + // otherwise, the color must match the current color. In practice, we are preventing + // macro scopes from interfering with each the scopes in which they are expanded + if scope.color.is_some() && scope.color != cur_color { + return None; + } let use_fun = scope.use_funs.get_mut(tn)?.get_mut(&method)?; use_fun.used = true; Some(use_fun.target_function) }) } + /// true iff it is safe to expand, + /// false with an error otherwise (e.g. a recursive expansion) + pub fn add_macro_expansion(&mut self, m: ModuleIdent, f: FunctionName, loc: Loc) -> bool { + let current_call_color = self.current_call_color(); + + let mut prev_opt = None; + for (idx, mexp) in self.macro_expansion.iter().enumerate().rev() { + match mexp { + MacroExpansion::Argument { scope_color } => { + // the argument has a smaller (or equal) color, meaning this lambda/arg was + // written in an outer scope + if current_call_color > *scope_color { + break; + } + } + MacroExpansion::Call(c) => { + let MacroCall { + module, + function, + scope_color, + .. + } = &**c; + // If we find a call (same module/fn) above us at a shallower expansion depth, + // without an interceding macro arg/lambda, we are in a macro calling itself. + // If it was a deeper depth, that's fine -- it must have come from elsewhere. + if current_call_color > *scope_color && module == &m && function == &f { + prev_opt = Some(idx); + break; + } + } + } + } + + if let Some(idx) = prev_opt { + let msg = format!( + "Recursive macro expansion. '{}::{}' cannot recursively expand itself", + m, f + ); + let mut diag = diag!(TypeSafety::CannotExpandMacro, (loc, msg)); + let cycle = self.macro_expansion[idx..] + .iter() + .filter_map(|case| match case { + MacroExpansion::Call(c) => Some((&c.module, &c.function, &c.invocation)), + MacroExpansion::Argument { .. } => None, + }); + for (prev_m, prev_f, prev_loc) in cycle { + let msg = if prev_m == &m && prev_f == &f { + format!("'{}::{}' previously expanded here", prev_m, prev_f) + } else { + "From this macro expansion".to_owned() + }; + diag.add_secondary_label((*prev_loc, msg)); + } + self.env.add_diag(diag); + false + } else { + self.macro_expansion + .push(MacroExpansion::Call(Box::new(MacroCall { + module: m, + function: f, + invocation: loc, + scope_color: current_call_color, + }))); + true + } + } + + pub fn pop_macro_expansion(&mut self, m: &ModuleIdent, f: &FunctionName) { + let Some(MacroExpansion::Call(c)) = self.macro_expansion.pop() else { + panic!("ICE macro expansion stack should have a call when leaving a macro expansion") + }; + let MacroCall { + module, function, .. + } = *c; + assert!( + m == &module && f == &function, + "ICE macro expansion stack should be popped in reverse order" + ); + } + + pub fn maybe_enter_macro_argument( + &mut self, + from_macro_argument: Option, + color: Color, + ) { + if from_macro_argument.is_some() { + self.macro_expansion + .push(MacroExpansion::Argument { scope_color: color }) + } + } + + pub fn maybe_exit_macro_argument(&mut self, from_macro_argument: Option) { + if from_macro_argument.is_some() { + let Some(MacroExpansion::Argument { .. }) = self.macro_expansion.pop() else { + panic!("ICE macro expansion stack should have a lambda when leaving a lambda") + }; + } + } + + pub fn current_call_color(&self) -> Color { + self.use_funs.last().unwrap().color.unwrap() + } + pub fn reset_for_module_item(&mut self) { self.named_block_map = BTreeMap::new(); self.return_type = None; @@ -216,6 +385,10 @@ impl<'env> Context<'env> { self.subst = Subst::empty(); self.constraints = Constraints::new(); self.current_function = None; + self.in_macro_function = false; + self.max_variable_color = RefCell::new(0); + self.macro_expansion = vec![]; + self.lambda_expansion = vec![]; } pub fn error_type(&mut self, loc: Loc) -> Type { @@ -283,15 +456,30 @@ impl<'env> Context<'env> { ty, used_mut: None, }; - self.locals.add(var, local).unwrap() + if let Err((_, prev_loc)) = self.locals.add(var, local) { + let msg = format!("ICE duplicate {var:?}. Should have been made unique in naming"); + self.env + .add_diag(ice!((var.loc, msg), (prev_loc, "Previously declared here"))); + } } pub fn get_local_type(&mut self, var: &Var) -> Type { - // should not fail, already checked in naming + if !self.locals.contains_key(var) { + let msg = format!("ICE unbound {var:?}. Should have failed in naming"); + self.env.add_diag(ice!((var.loc, msg))); + return self.error_type(var.loc); + } + self.locals.get(var).unwrap().ty.clone() } pub fn mark_mutable_usage(&mut self, loc: Loc, var: &Var) -> (Loc, Mutability) { + if !self.locals.contains_key(var) { + let msg = format!("ICE unbound {var:?}. Should have failed in naming"); + self.env.add_diag(ice!((loc, msg))); + return (loc, Mutability::None); + } + // should not fail, already checked in naming let decl_loc = *self.locals.get_loc(var).unwrap(); let local = self.locals.get_mut(var).unwrap(); @@ -377,10 +565,14 @@ impl<'env> Context<'env> { self.modules.struct_type_parameters(m, n) } - fn function_info(&self, m: &ModuleIdent, n: &FunctionName) -> &FunctionInfo { + pub fn function_info(&self, m: &ModuleIdent, n: &FunctionName) -> &FunctionInfo { self.modules.function_info(m, n) } + pub fn macro_body(&self, m: &ModuleIdent, n: &FunctionName) -> Option<&N::Sequence> { + self.macros.get(m)?.get(n) + } + fn constant_info(&mut self, m: &ModuleIdent, n: &ConstantName) -> &ConstantInfo { let constants = &self.module_info(m).constants; constants.get(n).expect("ICE should have failed in naming") @@ -400,6 +592,24 @@ impl<'env> Context<'env> { pub fn named_block_type_opt(&self, name: BlockLabel) -> Option { self.named_block_map.get(&name).cloned() } + + pub fn next_variable_color(&mut self) -> Color { + let max_variable_color: &mut u16 = &mut self.max_variable_color.borrow_mut(); + *max_variable_color += 1; + *max_variable_color + } + + pub fn set_max_variable_color(&self, color: Color) { + let max_variable_color: &mut u16 = &mut self.max_variable_color.borrow_mut(); + assert!( + *max_variable_color <= color, + "ICE a new, lower color means reusing variables \ + {} <= {}", + *max_variable_color, + color, + ); + *max_variable_color = color; + } } //************************************************************************************************** @@ -522,6 +732,13 @@ fn error_format_impl_(b_: &Type_, subst: &Subst, nested: bool) -> String { }; format!("{}{}", n, tys_str) } + Fun(args, result) => { + format!( + "|{}| -> {}", + format_comma(args.iter().map(|t| error_format_nested(t, subst))), + error_format_nested(result, subst) + ) + } Param(tp) => tp.user_specified_name.value.to_string(), Ref(mut_, ty) => format!( "&{}{}", @@ -580,6 +797,7 @@ pub fn infer_abilities( })) .unwrap() } + T::Fun(_, _) => AbilitySet::functions(loc), } } @@ -610,6 +828,7 @@ fn debug_abilities_info(context: &Context, ty: &Type) -> (Option, AbilitySe context.struct_declared_abilities(m, n).clone(), ty_args.clone(), ), + T::Fun(_, _) => (None, AbilitySet::functions(loc), vec![]), } } @@ -791,18 +1010,11 @@ pub fn make_method_call_type( tn: &TypeName, method: Name, ty_args_opt: Option>, -) -> Option<( - Loc, - ModuleIdent, - FunctionName, - Vec, - Vec<(Var, Type)>, - Type, -)> { +) -> Option<(ModuleIdent, FunctionName, ResolvedFunctionType)> { let target_function_opt = context.find_method_and_mark_used(tn, method); // try to find a function in the defining module for errors let Some((target_m, target_f)) = target_function_opt else { - let lhs_ty_str = error_format_nested(lhs_ty, &Subst::empty()); + let lhs_ty_str = error_format_nested(lhs_ty, &context.subst); let defining_module = match &tn.value { TypeName_::Multiple(_) => panic!("ICE method on tuple"), TypeName_::Builtin(sp!(_, bt_)) => context.env.primitive_definer(*bt_), @@ -829,7 +1041,7 @@ pub fn make_method_call_type( }; let arg_msg = match first_ty { Some(ty) => { - let tys_str = error_format(&ty, &Subst::empty()); + let tys_str = error_format(&ty, &context.subst); format!("but it has a different type for its first argument, {tys_str}") } None => "but it takes no arguments".to_owned(), @@ -866,10 +1078,9 @@ pub fn make_method_call_type( return None; }; - let (defined_loc, ty_args, params, return_ty) = - make_function_type(context, loc, &target_m, &target_f, ty_args_opt); + let function_ty = make_function_type(context, loc, &target_m, &target_f, ty_args_opt); - Some((defined_loc, target_m, target_f, ty_args, params, return_ty)) + Some((target_m, target_f, function_ty)) } pub fn make_function_type( @@ -878,13 +1089,14 @@ pub fn make_function_type( m: &ModuleIdent, f: &FunctionName, ty_args_opt: Option>, -) -> (Loc, Vec, Vec<(Var, Type)>, Type) { +) -> ResolvedFunctionType { let in_current_module = match &context.current_module { Some(current) => m == current, None => false, }; - let constraints: Vec<_> = context - .function_info(m, f) + let finfo = context.function_info(m, f); + let macro_ = finfo.macro_; + let constraints: Vec<_> = finfo .signature .type_parameters .iter() @@ -893,10 +1105,20 @@ pub fn make_function_type( let ty_args = match ty_args_opt { None => { + let case = if macro_.is_some() { + TVarCase::Macro + } else { + TVarCase::Base + }; let locs_constraints = constraints.into_iter().map(|k| (loc, k)).collect(); - make_tparams(context, loc, TVarCase::Base, locs_constraints) + make_tparams(context, loc, case, locs_constraints) } Some(ty_args) => { + let case = if macro_.is_some() { + TArgCase::Macro + } else { + TArgCase::Fun + }; let ty_args = check_type_argument_arity( context, loc, @@ -904,7 +1126,7 @@ pub fn make_function_type( ty_args, &constraints, ); - instantiate_type_args(context, loc, None, ty_args, constraints) + instantiate_type_args(context, loc, case, ty_args, constraints) } }; @@ -936,7 +1158,7 @@ pub fn make_function_type( visibility_error( context, public_for_testing, - (loc, format!("Invalid call to '{}::{}'", m, f)), + (loc, format!("Invalid call to internal function '{m}::{f}'")), (defined_loc, internal_msg), ); } @@ -946,6 +1168,12 @@ pub fn make_function_type( context.record_current_module_as_friend(m, loc); } Visibility::Package(vis_loc) => { + let msg = format!( + "Invalid call to '{}' visible function '{}::{}'", + Visibility::PACKAGE, + m, + f + ); let internal_msg = format!( "A '{}' function can only be called from the same address and package as \ module '{}' in package '{}'. This call is from address '{}' in package '{}'", @@ -969,28 +1197,37 @@ pub fn make_function_type( visibility_error( context, public_for_testing, - (loc, format!("Invalid call to '{}::{}'", m, f)), + (loc, msg), (vis_loc, internal_msg), ); } Visibility::Friend(_) if in_current_module || context.current_module_is_a_friend_of(m) => {} Visibility::Friend(vis_loc) => { - let internal_msg = format!( - "This function can only be called from a 'friend' of module '{}'", - m + let msg = format!( + "Invalid call to '{}' visible function '{m}::{f}'", + Visibility::FRIEND, ); + let internal_msg = + format!("This function can only be called from a 'friend' of module '{m}'",); visibility_error( context, public_for_testing, - (loc, format!("Invalid call to '{}::{}'", m, f)), + (loc, msg), (vis_loc, internal_msg), ); } Visibility::Public(_) => (), }; - (defined_loc, ty_args, params, return_ty) + ResolvedFunctionType { + declared: defined_loc, + macro_, + ty_args, + params, + return_: return_ty, + } } +#[derive(Clone, Copy)] pub enum PublicForTesting { /// The function is entry, so it can be called in unit tests Entry(Loc), @@ -1047,6 +1284,35 @@ fn visibility_error( context.env.add_diag(diag) } +pub fn check_call_arity S>( + context: &mut Context, + loc: Loc, + msg: F, + arity: usize, + argloc: Loc, + given_len: usize, +) { + if given_len == arity { + return; + } + let code = if given_len < arity { + TypeSafety::TooFewArguments + } else { + TypeSafety::TooManyArguments + }; + let cmsg = format!( + "{}. The call expected {} argument(s) but got {}", + msg(), + arity, + given_len + ); + context.env.add_diag(diag!( + code, + (loc, cmsg), + (argloc, format!("Found {} argument(s) here", given_len)), + )); +} + //************************************************************************************************** // Constraints //************************************************************************************************** @@ -1260,7 +1526,7 @@ fn solve_base_type_constraint(context: &mut Context, loc: Loc, msg: String, ty: (tyloc, tmsg) )) } - UnresolvedError | Anything | Param(_) | Apply(_, _, _) => (), + UnresolvedError | Anything | Param(_) | Apply(_, _, _) | Fun(_, _) => (), } } @@ -1281,7 +1547,7 @@ fn solve_single_type_constraint(context: &mut Context, loc: Loc, msg: String, ty (tyloc, tmsg) )) } - UnresolvedError | Anything | Ref(_, _) | Param(_) | Apply(_, _, _) => (), + UnresolvedError | Anything | Ref(_, _) | Param(_) | Apply(_, _, _) | Fun(_, _) => (), } } @@ -1355,6 +1621,11 @@ pub fn subst_tparams(subst: &TParamSubst, sp!(loc, t_): Type) -> Type { .collect(); sp(loc, Apply(k, n, ftys)) } + Fun(args, result) => { + let ftys = args.into_iter().map(|t| subst_tparams(subst, t)).collect(); + let fres = Box::new(subst_tparams(subst, *result)); + sp(loc, Fun(ftys, fres)) + } } } @@ -1375,6 +1646,11 @@ pub fn ready_tvars(subst: &Subst, sp!(loc, t_): Type) -> Type { Some(t) => ready_tvars(subst, t.clone()), } } + Fun(args, result) => { + let args = args.into_iter().map(|t| ready_tvars(subst, t)).collect(); + let result = Box::new(ready_tvars(subst, *result)); + sp(loc, Fun(args, result)) + } } } @@ -1396,8 +1672,17 @@ pub fn instantiate(context: &mut Context, sp!(loc, t_): Type) -> Type { Apply(abilities_opt, n, ty_args) => { instantiate_apply(context, loc, abilities_opt, n, ty_args) } + Fun(args, result) => Fun( + args.into_iter().map(|t| instantiate(context, t)).collect(), + Box::new(instantiate(context, *result)), + ), x @ Param(_) => x, - Var(_) => panic!("ICE instantiate type variable"), + // instantiating a var really shouldn't happen... but it does because of macro expansion + // We expand macros before type checking, but after the arguments to the macro are type + // checked (otherwise we couldn't properly do method syntax macros). As a result, we are + // substituting type variables into the macro body, and might hit one while expanding a + // type in the macro where a type parameter's argument had a type variable. + x @ Var(_) => x, }; sp(loc, it_) } @@ -1423,7 +1708,13 @@ fn instantiate_apply( } }; - let tys = instantiate_type_args(context, loc, Some(&n.value), ty_args, tparam_constraints); + let tys = instantiate_type_args( + context, + loc, + TArgCase::Apply(&n.value), + ty_args, + tparam_constraints, + ); Type_::Apply(abilities_opt, n, tys) } @@ -1435,7 +1726,7 @@ fn instantiate_apply( fn instantiate_type_args( context: &mut Context, loc: Loc, - n: Option<&TypeName_>, + case: TArgCase, mut ty_args: Vec, constraints: Vec, ) -> Vec { @@ -1445,11 +1736,14 @@ fn instantiate_type_args( .zip(&ty_args) .map(|(abilities, t)| (t.loc, abilities)) .collect(); - let tvar_case = match n { - Some(TypeName_::Multiple(_)) => { + let tvar_case = match case { + TArgCase::Apply(TypeName_::Multiple(_)) => { TVarCase::Single("Invalid expression list type argument".to_owned()) } - None | Some(TypeName_::Builtin(_)) | Some(TypeName_::ModuleType(_, _)) => TVarCase::Base, + TArgCase::Fun + | TArgCase::Apply(TypeName_::Builtin(_)) + | TArgCase::Apply(TypeName_::ModuleType(_, _)) => TVarCase::Base, + TArgCase::Macro => TVarCase::Macro, }; let tvars = make_tparams(context, loc, tvar_case, locs_constraints); ty_args = ty_args @@ -1510,6 +1804,13 @@ fn check_type_argument_arity String>( enum TVarCase { Single(String), Base, + Macro, +} + +enum TArgCase<'a> { + Apply(&'a TypeName_), + Fun, + Macro, } fn make_tparams( @@ -1528,12 +1829,34 @@ fn make_tparams( TVarCase::Base => { context.add_base_type_constraint(loc, "Invalid type argument", tvar.clone()) } + TVarCase::Macro => (), }; tvar }) .collect() } +// used in macros to make the signatures consistent with the bodies, in that we don't check +// constraints until application +pub fn give_tparams_all_abilities(sp!(_, ty_): &mut Type) { + match ty_ { + Type_::Unit | Type_::Var(_) | Type_::UnresolvedError | Type_::Anything => (), + Type_::Ref(_, inner) => give_tparams_all_abilities(inner), + Type_::Apply(_, _, ty_args) => { + for ty_arg in ty_args { + give_tparams_all_abilities(ty_arg) + } + } + Type_::Fun(args, ret) => { + for arg in args { + give_tparams_all_abilities(arg) + } + give_tparams_all_abilities(ret) + } + Type_::Param(_) => *ty_ = Type_::Anything, + } +} + //************************************************************************************************** // Subtype and joining //************************************************************************************************** @@ -1543,6 +1866,7 @@ pub enum TypingError { SubtypeError(Box, Box), Incompatible(Box, Box), ArityMismatch(usize, Box, usize, Box), + FunArityMismatch(usize, Box, usize, Box), RecursiveType(Loc), } @@ -1624,6 +1948,24 @@ fn join_impl( let (subst, tys) = join_impl_types(subst, case, tys1, tys2)?; Ok((subst, sp(*loc, Apply(k2.clone(), n2.clone(), tys)))) } + (sp!(_, Fun(a1, _)), sp!(_, Fun(a2, _))) if a1.len() != a2.len() => { + Err(TypingError::FunArityMismatch( + a1.len(), + Box::new(lhs.clone()), + a2.len(), + Box::new(rhs.clone()), + )) + } + (sp!(_, Fun(a1, r1)), sp!(loc, Fun(a2, r2))) => { + // TODO this is going to likely lead to some strange error locations/messages + // since the RHS in subtyping is currently assumed to be an annotation + let (subst, args) = match case { + Join => join_impl_types(subst, case, a1, a2)?, + Subtype => join_impl_types(subst, case, a2, a1)?, + }; + let (subst, result) = join_impl(subst, case, r1, r2)?; + Ok((subst, sp(*loc, Fun(args, Box::new(result))))) + } (sp!(loc1, Var(id1)), sp!(loc2, Var(id2))) => { if *id1 == *id2 { Ok((subst, sp(*loc2, Var(*id2)))) @@ -1770,6 +2112,13 @@ fn join_bind_tvar(subst: &mut Subst, loc: Loc, tvar: TVar, ty: Type) -> Result { + inner_args + .iter() + .rev() + .for_each(|inner| used_tvars(used, inner)); + used_tvars(used, inner_ret) + } T::Unit | T::Param(_) | T::Anything | T::UnresolvedError => (), } } diff --git a/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs b/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs index d47edd61f3812..5419e96349213 100644 --- a/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs +++ b/external-crates/move/crates/move-compiler/src/typing/dependency_ordering.rs @@ -288,6 +288,10 @@ fn type_(context: &mut Context, sp!(_, ty_): &N::Type) { types(context, tys); } T::Ref(_, t) => type_(context, t), + T::Fun(tys, t) => { + types(context, tys); + type_(context, t); + } T::Unit | T::Param(_) | T::Var(_) | T::Anything | T::UnresolvedError => (), } } @@ -373,7 +377,7 @@ fn exp(context: &mut Context, e: &T::Exp) { exp(context, e2); exp(context, e3); } - E::While(e1, _, e2) => { + E::While(_, e1, e2) => { exp(context, e1); exp(context, e2); } diff --git a/external-crates/move/crates/move-compiler/src/typing/expand.rs b/external-crates/move/crates/move-compiler/src/typing/expand.rs index c9ee3b58d570c..a908098df3700 100644 --- a/external-crates/move/crates/move-compiler/src/typing/expand.rs +++ b/external-crates/move/crates/move-compiler/src/typing/expand.rs @@ -5,6 +5,7 @@ use super::core::{self, Context}; use crate::{ diag, + editions::FeatureGate, expansion::ast::Value_, naming::ast::{BuiltinTypeName_, FunctionSignature, Type, TypeName_, Type_}, parser::ast::Ability_, @@ -19,7 +20,7 @@ use move_ir_types::location::*; pub fn function_body_(context: &mut Context, b_: &mut T::FunctionBody_) { match b_ { - T::FunctionBody_::Native => (), + T::FunctionBody_::Native | T::FunctionBody_::Macro => (), T::FunctionBody_::Defined(es) => sequence(context, es), } } @@ -64,6 +65,11 @@ pub fn type_(context: &mut Context, ty: &mut Type) { .add_diag(diag!(TypeSafety::UninferredType, (ty.loc, msg))); sp(loc, UnresolvedError) } + sp!(loc, Fun(_, _)) if !context.in_macro_function => { + // catch this here for better location infomration (the tvar instead of the fun) + unexpected_lambda_type(context, ty.loc); + sp(loc, UnresolvedError) + } t => t, }; *ty = replacement; @@ -81,6 +87,28 @@ pub fn type_(context: &mut Context, ty: &mut Type) { _ => panic!("ICE impossible. tapply switched to nontapply"), } } + Fun(args, result) => { + if context.in_macro_function { + types(context, args); + type_(context, result); + } else { + unexpected_lambda_type(context, ty.loc); + *ty = sp(ty.loc, UnresolvedError) + } + } + } +} + +fn unexpected_lambda_type(context: &mut Context, loc: Loc) { + if context + .env + .check_feature(FeatureGate::MacroFuns, context.current_package, loc) + { + let msg = "Unexpected lambda type. \ + Lambdas can only be used with 'macro' functions, as parameters or direct arguments"; + context + .env + .add_diag(diag!(TypeSafety::UnexpectedFunctionType, (loc, msg))); } } @@ -240,7 +268,7 @@ pub fn exp(context: &mut Context, e: &mut T::Exp) { exp(context, et); exp(context, ef); } - E::While(eb, _, eloop) => { + E::While(_, eb, eloop) => { exp(context, eb); exp(context, eloop); } diff --git a/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs b/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs index 81cda3e7fe44f..eda099ace65c4 100644 --- a/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs +++ b/external-crates/move/crates/move-compiler/src/typing/infinite_instantiations.rs @@ -94,6 +94,15 @@ impl<'a> Context<'a> { tys.iter() .for_each(|t| Self::add_tparam_edges(acc, tparam, info.clone(), t)) } + Fun(tys, t) => { + let info = EdgeInfo { + edge: Edge::Nested, + ..info + }; + tys.iter() + .for_each(|t| Self::add_tparam_edges(acc, tparam, info.clone(), t)); + Self::add_tparam_edges(acc, tparam, info.clone(), t) + } Param(tp) => { let tp_neighbors = acc.entry(tp.clone()).or_default(); match tp_neighbors.get(tparam) { @@ -187,7 +196,7 @@ fn module<'a>( fn function_body(context: &mut Context, sp!(_, b_): &T::FunctionBody) { match b_ { - T::FunctionBody_::Native => (), + T::FunctionBody_::Native | T::FunctionBody_::Macro => (), T::FunctionBody_::Defined(es) => sequence(context, es), } } @@ -232,7 +241,7 @@ fn exp(context: &mut Context, e: &T::Exp) { exp(context, et); exp(context, ef); } - E::While(eb, _, eloop) => { + E::While(_, eb, eloop) => { exp(context, eb); exp(context, eloop); } diff --git a/external-crates/move/crates/move-compiler/src/typing/macro_expand.rs b/external-crates/move/crates/move-compiler/src/typing/macro_expand.rs new file mode 100644 index 0000000000000..664903d93eb17 --- /dev/null +++ b/external-crates/move/crates/move-compiler/src/typing/macro_expand.rs @@ -0,0 +1,955 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + diag, + diagnostics::Diagnostic, + expansion::ast::ModuleIdent, + naming::ast::{self as N, BlockLabel, Color, TParamID, Type, Type_, UseFuns, Var, Var_}, + parser::ast::FunctionName, + shared::program_info::FunctionInfo, + typing::{ + ast as T, + core::{self, TParamSubst}, + }, +}; +use move_ir_types::location::*; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; + +type LambdaMap = BTreeMap, Type)>; +type ArgMap = BTreeMap; +struct ParamInfo { + argument: Option>, + used: bool, +} + +struct Context<'a, 'b> { + core: &'a mut core::Context<'b>, + // used for removing unbound params + all_params: BTreeMap, + // used for expanding lambda calls in VarCall + lambdas: LambdaMap, + // used for expanding by-name arguments in Var usage + by_name_args: ArgMap, + tparam_subst: TParamSubst, + macro_color: Color, +} + +pub struct ExpandedMacro { + pub by_value_args: Vec<(Spanned>, T::Exp)>, + pub body: Box, +} + +pub enum EvalStrategy { + ByValue(ByValue), + ByName(ByName), +} + +pub type Arg = EvalStrategy; + +pub(crate) fn call( + context: &mut core::Context, + call_loc: Loc, + m: ModuleIdent, + f: FunctionName, + type_args: Vec, + args: Vec, + return_type: Type, +) -> Option { + let next_color = context.next_variable_color(); + // If none, there is no body to expand, likely because of an error in the macro definition + let macro_body = context.macro_body(&m, &f)?; + let macro_info = context.function_info(&m, &f); + let (macro_type_params, macro_params, mut macro_body, return_label, max_color) = + match recolor_macro(call_loc, &m, &f, macro_info, macro_body, next_color) { + Ok(res) => res, + Err(None) => { + assert!(context.env.has_errors()); + return None; + } + Err(Some(diag)) => { + context.env.add_diag(*diag); + return None; + } + }; + context.set_max_variable_color(max_color); + + if macro_type_params.len() != type_args.len() || macro_params.len() != args.len() { + assert!(context.env.has_errors()); + return None; + } + // tparam subst + assert_eq!( + macro_type_params.len(), + type_args.len(), + "ICE should be fixed/caught by the module/method call" + ); + let tparam_subst = macro_type_params.into_iter().zip(type_args).collect(); + // make separate out by-value and by-name arguments + let mut all_params: BTreeMap<_, _> = macro_params + .iter() + .map(|(_, sp!(_, v_), _)| { + let info = ParamInfo { + argument: None, + used: false, + }; + (*v_, info) + }) + .collect(); + let mut lambdas = BTreeMap::new(); + let mut by_name_args = BTreeMap::new(); + let mut by_value_args = vec![]; + for ((_, param, _param_ty), arg) in macro_params.into_iter().zip(args) { + let param_loc = param.loc; + let param = if param.value.name == symbol!("_") { + None + } else { + Some(param.value) + }; + let (arg_loc, arg_ty) = match &arg { + Arg::ByValue(e) => (EvalStrategy::ByValue(e.exp.loc), e.ty.clone()), + Arg::ByName((e, ty)) => (EvalStrategy::ByName(e.loc), ty.clone()), + }; + let unfolded = core::unfold_type(&context.subst, arg_ty); + if let sp!(_, Type_::Fun(param_tys, result_ty)) = unfolded { + let arg_exp = match arg { + Arg::ByValue(_) => { + assert!( + context.env.has_errors(), + "ICE lambda args should never be by value" + ); + continue; + } + Arg::ByName((e, _)) => e, + }; + if let Some(v) = param { + bind_lambda(context, &mut lambdas, v, arg_exp, param_tys, *result_ty)? + } + } else { + match arg { + Arg::ByValue(e) => by_value_args.push((sp(param_loc, param), e)), + Arg::ByName((e, expected_ty)) => { + if let Some(v) = param { + by_name_args.insert(v, (e, expected_ty)); + } + } + } + } + if let Some(v) = param { + let info = ParamInfo { + argument: Some(arg_loc), + used: false, + }; + all_params.insert(v, info); + } else { + report_unused_argument(context, arg_loc); + } + } + let break_labels: BTreeSet<_> = BTreeSet::from([return_label]); + let mut context = Context { + core: context, + lambdas, + all_params, + by_name_args, + tparam_subst, + macro_color: next_color, + }; + block(&mut context, &mut macro_body); + context.report_unused_arguments(); + let mut wrapped_body = Box::new(sp(call_loc, N::Exp_::Block(macro_body))); + for label in break_labels { + let seq = ( + N::UseFuns::new(next_color), + VecDeque::from([sp(call_loc, N::SequenceItem_::Seq(wrapped_body))]), + ); + let block = N::Block { + name: Some(label), + from_macro_argument: None, + seq, + }; + wrapped_body = Box::new(sp(call_loc, N::Exp_::Block(block))); + } + let body = Box::new(sp(call_loc, N::Exp_::Annotate(wrapped_body, return_type))); + Some(ExpandedMacro { + by_value_args, + body, + }) +} + +fn recolor_macro( + call_loc: Loc, + _m: &ModuleIdent, + _f: &FunctionName, + macro_info: &FunctionInfo, + macro_body: &N::Sequence, + color: u16, +) -> Result< + ( + Vec, + Vec<(Option, Var, N::Type)>, + N::Block, + BlockLabel, + Color, + ), + Option>, +> { + let FunctionInfo { + macro_, signature, .. + } = macro_info; + if macro_.is_none() { + // error handled in call type checking + return Err(None); + } + let N::FunctionSignature { + type_parameters, + parameters, + .. + } = signature; + let tparam_ids = type_parameters.iter().map(|t| t.id).collect(); + let label = sp( + call_loc, + N::Var_ { + name: N::BlockLabel::MACRO_RETURN_NAME_SYMBOL, + id: 0, + color, + }, + ); + let return_label = BlockLabel { + label, + is_implicit: true, + }; + let recolor_use_funs = true; + let recolor = &mut Recolor::new(color, Some(return_label), recolor_use_funs); + recolor.add_params(parameters); + let parameters = parameters + .iter() + .map(|(mut_, v, t)| (*mut_, recolor_var_owned(recolor, *v), t.clone())) + .collect(); + let body = { + let mut body = macro_body.clone(); + recolor_seq(recolor, &mut body); + N::Block { + name: None, + from_macro_argument: None, + seq: body, + } + }; + let max_color = recolor.max_color(); + debug_assert_eq!(color, max_color, "ICE should only have one color in macros"); + Ok((tparam_ids, parameters, body, return_label, max_color)) +} + +fn bind_lambda( + context: &mut core::Context, + lambdas: &mut LambdaMap, + param: Var_, + arg: N::Exp, + param_ty: Vec, + result_ty: Type, +) -> Option<()> { + match arg.value { + N::Exp_::Lambda(lambda) => { + lambdas.insert(param, (lambda, param_ty, result_ty)); + Some(()) + } + _ => { + let msg = format!( + "Unable to bind lambda to parameter '{}'. The lambda must be passed directly", + param.name + ); + context + .env + .add_diag(diag!(TypeSafety::CannotExpandMacro, (arg.loc, msg))); + None + } + } +} + +//************************************************************************************************** +// recolor +//************************************************************************************************** + +use recolor_struct::*; + +mod recolor_struct { + use crate::naming::ast::{self as N, BlockLabel, Color, Var}; + use move_ir_types::location::Loc; + use std::collections::{BTreeMap, BTreeSet}; + // handles all of the recoloring of variables, labels, and use funs. + // The mask of known vars and labels is here to handle the case where a variable was captured + // by a lambda + pub(super) struct Recolor { + next_color: Color, + remapping: BTreeMap, + recolor_use_funs: bool, + return_label: Option, + vars: BTreeSet, + block_labels: BTreeSet, + } + + impl Recolor { + pub fn new(color: u16, return_label: Option, recolor_use_funs: bool) -> Self { + Self { + next_color: color, + remapping: BTreeMap::new(), + recolor_use_funs, + return_label, + vars: BTreeSet::new(), + block_labels: BTreeSet::new(), + } + } + + pub fn add_params(&mut self, params: &[(Option, Var, N::Type)]) { + for (_, v, _) in params { + self.vars.insert(*v); + } + } + + pub fn add_lvalues(&mut self, lvalues: &N::LValueList) { + for lvalue in &lvalues.value { + self.add_lvalue(lvalue) + } + } + + pub fn add_lvalue(&mut self, sp!(_, lvalue_): &N::LValue) { + match lvalue_ { + N::LValue_::Ignore => (), + N::LValue_::Var { var, .. } => { + self.vars.insert(*var); + } + N::LValue_::Unpack(_, _, _, lvalues) => { + for (_, _, (_, lvalue)) in lvalues { + self.add_lvalue(lvalue) + } + } + } + } + + pub fn add_block_label(&mut self, label: BlockLabel) { + self.block_labels.insert(label); + } + + // We need to fully remap colors, and not simply set everything to the specified color, + // to handle the case where a lambda captures another expanded lambda, for example + // `|i| v.push_back(f(i))` + // where f is + // `|i| i`` + // In this case we have + // two different colored `i`s when applying the outer lambda, e.g. + // `let i#_#c = arg; v.push_back({ let i#_#d = i#_#c; i#_#d })` + // we need to make sure `i#_#c` and `i#_#d` remain separated + // + // This has similar feeling to lifting De Bruijn indices, though it is not exactly the same + // (... I think) + pub fn remap_color(&mut self, color: Color) -> Color { + *self.remapping.entry(color).or_insert_with(|| { + let cur = self.next_color; + self.next_color += 1; + cur + }) + } + + pub fn recolor_use_funs(&self) -> bool { + self.recolor_use_funs + } + + pub fn max_color(&self) -> Color { + if self.remapping.is_empty() { + // next color never used + self.next_color + } else { + // subtract one to skip the "next" color + let max = self.next_color - 1; + debug_assert!(self.remapping.values().all(|&c| c <= max)); + max + } + } + + pub fn return_label(&self) -> Option { + self.return_label + } + + pub fn contains_var(&self, v: &Var) -> bool { + self.vars.contains(v) + } + + pub fn contains_block_label(&self, label: &BlockLabel) -> bool { + self.block_labels.contains(label) + } + } +} + +fn recolor_var_owned(ctx: &mut Recolor, mut v: Var) -> Var { + assert!(ctx.contains_var(&v)); + recolor_var(ctx, &mut v); + v +} + +fn recolor_var(ctx: &mut Recolor, v: &mut Var) { + // do not recolor if not in the ctx + // this is to handle captured variables in lambda bodies + if !ctx.contains_var(v) { + return; + } + v.value.color = ctx.remap_color(v.value.color); +} + +fn recolor_block_label_owned(ctx: &mut Recolor, mut label: BlockLabel) -> BlockLabel { + assert!(ctx.contains_block_label(&label)); + recolor_block_label(ctx, &mut label); + label +} + +fn recolor_block_label(ctx: &mut Recolor, label: &mut BlockLabel) { + // do not recolor if not in the ctx + // this is to handle captured labels in lambda bodies + if !ctx.contains_block_label(label) { + return; + } + label.label.value.color = ctx.remap_color(label.label.value.color); +} + +fn recolor_use_funs(ctx: &mut Recolor, use_funs: &mut UseFuns) { + recolor_use_funs_(ctx, &mut use_funs.color); +} + +fn recolor_use_funs_(ctx: &mut Recolor, use_fun_color: &mut Color) { + if ctx.recolor_use_funs() { + assert_eq!( + *use_fun_color, 0, + "ICE only expected to recolor use funs in fresh macro bodies" + ); + *use_fun_color = ctx.remap_color(*use_fun_color); + } +} + +fn recolor_seq(ctx: &mut Recolor, (use_funs, seq): &mut N::Sequence) { + recolor_use_funs(ctx, use_funs); + for sp!(_, item_) in seq { + match item_ { + N::SequenceItem_::Seq(e) => recolor_exp(ctx, e), + N::SequenceItem_::Declare(lvalues, _) => { + ctx.add_lvalues(lvalues); + recolor_lvalues(ctx, lvalues) + } + N::SequenceItem_::Bind(lvalues, e) => { + ctx.add_lvalues(lvalues); + recolor_lvalues(ctx, lvalues); + recolor_exp(ctx, e) + } + } + } +} + +fn recolor_lvalues(ctx: &mut Recolor, lvalues: &mut N::LValueList) { + for lvalue in &mut lvalues.value { + recolor_lvalue(ctx, lvalue) + } +} + +fn recolor_lvalue(ctx: &mut Recolor, sp!(_, lvalue_): &mut N::LValue) { + match lvalue_ { + N::LValue_::Ignore => (), + N::LValue_::Var { var, .. } => recolor_var(ctx, var), + N::LValue_::Unpack(_, _, _, lvalues) => { + for (_, _, (_, lvalue)) in lvalues { + recolor_lvalue(ctx, lvalue) + } + } + } +} + +fn recolor_exp(ctx: &mut Recolor, sp!(_, e_): &mut N::Exp) { + match e_ { + N::Exp_::Value(_) | N::Exp_::Constant(_, _) => (), + N::Exp_::Give(_usage, label, e) => { + recolor_block_label(ctx, label); + recolor_exp(ctx, e) + } + N::Exp_::Continue(label) => recolor_block_label(ctx, label), + N::Exp_::Unit { .. } | N::Exp_::UnresolvedError => (), + N::Exp_::Var(var) => recolor_var(ctx, var), + N::Exp_::Return(e) => { + recolor_exp(ctx, e); + if let Some(label) = ctx.return_label() { + let N::Exp_::Return(e) = + std::mem::replace(e_, /* dummy */ N::Exp_::UnresolvedError) + else { + unreachable!() + }; + *e_ = N::Exp_::Give(N::NominalBlockUsage::Return, label, e) + } + } + + N::Exp_::Abort(e) + | N::Exp_::Dereference(e) + | N::Exp_::UnaryExp(_, e) + | N::Exp_::Cast(e, _) + | N::Exp_::Annotate(e, _) => recolor_exp(ctx, e), + N::Exp_::Assign(lvalues, e) => { + recolor_lvalues(ctx, lvalues); + recolor_exp(ctx, e) + } + N::Exp_::IfElse(econd, et, ef) => { + recolor_exp(ctx, econd); + recolor_exp(ctx, et); + recolor_exp(ctx, ef); + } + N::Exp_::Loop(name, e) => { + ctx.add_block_label(*name); + recolor_block_label(ctx, name); + recolor_exp(ctx, e) + } + N::Exp_::While(name, econd, ebody) => { + ctx.add_block_label(*name); + recolor_block_label(ctx, name); + recolor_exp(ctx, econd); + recolor_exp(ctx, ebody) + } + N::Exp_::Block(N::Block { + name, + from_macro_argument: _, + seq: s, + }) => { + if let Some(name) = name { + ctx.add_block_label(*name); + recolor_block_label(ctx, name); + } + recolor_seq(ctx, s); + } + N::Exp_::FieldMutate(ed, e) => { + recolor_exp_dotted(ctx, ed); + recolor_exp(ctx, e) + } + N::Exp_::Mutate(el, er) | N::Exp_::BinopExp(el, _, er) => { + recolor_exp(ctx, el); + recolor_exp(ctx, er) + } + N::Exp_::Pack(_, _, _, fields) => { + for (_, _, (_, e)) in fields { + recolor_exp(ctx, e) + } + } + N::Exp_::Builtin(_, sp!(_, es)) + | N::Exp_::Vector(_, _, sp!(_, es)) + | N::Exp_::ModuleCall(_, _, _, _, sp!(_, es)) + | N::Exp_::ExpList(es) => { + for e in es { + recolor_exp(ctx, e) + } + } + N::Exp_::MethodCall(ed, _, _, _, sp!(_, es)) => { + recolor_exp_dotted(ctx, ed); + for e in es { + recolor_exp(ctx, e) + } + } + N::Exp_::VarCall(v, sp!(_, es)) => { + recolor_var(ctx, v); + for e in es { + recolor_exp(ctx, e) + } + } + + N::Exp_::Lambda(N::Lambda { + parameters: sp!(_, parameters), + return_type: _, + return_label, + use_fun_color, + body, + }) => { + ctx.add_block_label(*return_label); + for (lvs, _) in &*parameters { + ctx.add_lvalues(lvs); + } + recolor_use_funs_(ctx, use_fun_color); + for (lvs, _) in parameters { + recolor_lvalues(ctx, lvs); + } + recolor_block_label(ctx, return_label); + recolor_exp(ctx, body) + } + N::Exp_::ExpDotted(_dotted_usage, ed) => recolor_exp_dotted(ctx, ed), + } +} + +fn recolor_exp_dotted(ctx: &mut Recolor, sp!(_, ed_): &mut N::ExpDotted) { + match ed_ { + N::ExpDotted_::Exp(e) => recolor_exp(ctx, e), + N::ExpDotted_::Dot(ed, _) => recolor_exp_dotted(ctx, ed), + } +} + +//************************************************************************************************** +// subst args +//************************************************************************************************** + +impl Context<'_, '_> { + fn mark_used(&mut self, v: &Var_) { + self.all_params.get_mut(v).unwrap().used = true; + } + + fn report_unused_arguments(self) { + let unused = self + .all_params + .into_values() + .filter(|info| !info.used) + .filter_map(|info| info.argument); + for loc in unused { + report_unused_argument(self.core, loc) + } + } +} + +fn report_unused_argument(context: &mut core::Context, loc: EvalStrategy) { + let loc = match loc { + EvalStrategy::ByValue(_) => return, // will be evaluated + EvalStrategy::ByName(loc) => loc, + }; + let msg = "Unused macro argument. \ + Its expression will not be type checked and it will not evaluated"; + context + .env + .add_diag(diag!(UnusedItem::DeadCode, (loc, msg))); +} + +fn types(context: &mut Context, tys: &mut [Type]) { + for ty in tys { + type_(context, ty) + } +} + +fn type_(context: &mut Context, ty: &mut N::Type) { + *ty = core::subst_tparams(&context.tparam_subst, ty.clone()) +} + +fn block(context: &mut Context, b: &mut N::Block) { + seq(context, &mut b.seq) +} + +fn seq(context: &mut Context, (_use_funs, seq): &mut N::Sequence) { + for sp!(_, item_) in seq { + match item_ { + N::SequenceItem_::Seq(e) => exp(context, e), + N::SequenceItem_::Declare(lvs, _) => lvalues(context, lvs), + N::SequenceItem_::Bind(lvs, e) => { + lvalues(context, lvs); + exp(context, e) + } + } + } +} + +fn lvalues(context: &mut Context, sp!(_, lvs_): &mut N::LValueList) { + for lv in lvs_ { + lvalue(context, lv) + } +} + +fn lvalue(context: &mut Context, sp!(_, lv_): &mut N::LValue) { + match lv_ { + N::LValue_::Ignore => (), + N::LValue_::Var { + var: sp!(_, v_), .. + } => { + if context.all_params.contains_key(v_) { + assert!( + context.core.env.has_errors(), + "ICE cannot assign to macro parameter" + ); + *lv_ = N::LValue_::Ignore + } + } + N::LValue_::Unpack(_, _, tys_opt, lvalues) => { + if let Some(tys) = tys_opt { + types(context, tys) + } + for (_, _, (_, lv)) in lvalues { + lvalue(context, lv) + } + } + } +} + +fn exp(context: &mut Context, sp!(eloc, e_): &mut N::Exp) { + match e_ { + N::Exp_::Value(_) + | N::Exp_::Constant(_, _) + | N::Exp_::Continue(_) + | N::Exp_::Unit { .. } + | N::Exp_::UnresolvedError => (), + N::Exp_::Give(_, _, e) + | N::Exp_::Return(e) + | N::Exp_::Abort(e) + | N::Exp_::Dereference(e) + | N::Exp_::UnaryExp(_, e) + | N::Exp_::Loop(_, e) => exp(context, e), + N::Exp_::Cast(e, ty) | N::Exp_::Annotate(e, ty) => { + exp(context, e); + type_(context, ty) + } + N::Exp_::Assign(lvs, e) => { + lvalues(context, lvs); + exp(context, e) + } + N::Exp_::IfElse(econd, et, ef) => { + exp(context, econd); + exp(context, et); + exp(context, ef); + } + N::Exp_::While(_name, econd, ebody) => { + exp(context, econd); + exp(context, ebody) + } + N::Exp_::Block(N::Block { + name: _, + from_macro_argument: _, + seq: s, + }) => seq(context, s), + N::Exp_::FieldMutate(ed, e) => { + exp_dotted(context, ed); + exp(context, e) + } + N::Exp_::Mutate(el, er) | N::Exp_::BinopExp(el, _, er) => { + exp(context, el); + exp(context, er) + } + N::Exp_::Pack(_, _, tys_opt, fields) => { + if let Some(tys) = tys_opt { + types(context, tys) + } + for (_, _, (_, e)) in fields { + exp(context, e) + } + } + N::Exp_::Builtin(bf, sp!(_, es)) => { + builtin_function(context, bf); + exps(context, es) + } + N::Exp_::Vector(_, ty_opt, sp!(_, es)) => { + if let Some(ty) = ty_opt { + type_(context, ty) + } + exps(context, es) + } + N::Exp_::ModuleCall(_, _, _, tys_opt, sp!(_, es)) => { + if let Some(tys) = tys_opt { + types(context, tys) + } + exps(context, es) + } + N::Exp_::MethodCall(ed, _, _, tys_opt, sp!(_, es)) => { + if let Some(tys) = tys_opt { + types(context, tys) + } + exp_dotted(context, ed); + exps(context, es) + } + N::Exp_::ExpList(es) => exps(context, es), + N::Exp_::Lambda(N::Lambda { + parameters: sp!(_, parameters), + body: e, + .. + }) => { + for (lvs, ty_opt) in parameters { + lvalues(context, lvs); + if let Some(ty) = ty_opt { + type_(context, ty) + } + } + exp(context, e) + } + N::Exp_::ExpDotted(_usage, ed) => exp_dotted(context, ed), + + /////// + // Lambda cases + /////// + N::Exp_::Var(sp!(_, v_)) if context.lambdas.contains_key(v_) => { + context.mark_used(v_); + let (lambda, _, _) = context.lambdas.get(v_).unwrap(); + *e_ = N::Exp_::Lambda(lambda.clone()); + } + N::Exp_::VarCall(sp!(_, v_), sp!(argloc, es)) if context.lambdas.contains_key(v_) => { + context.mark_used(v_); + exps(context, es); + // param_ty and result_ty have already been substituted + let ( + N::Lambda { + parameters: sp!(_, mut lambda_params), + return_type: _, + return_label, + use_fun_color, + body: mut lambda_body, + }, + param_tys, + result_ty, + ) = context.lambdas.get(v_).unwrap().clone(); + // recolor in case the lambda is used more than once + let next_color = context.core.next_variable_color(); + let recolor_use_funs = false; + let recolor = &mut Recolor::new( + next_color, + /* return already labeled */ None, + recolor_use_funs, + ); + recolor.add_block_label(return_label); + for (lvs, _) in &lambda_params { + recolor.add_lvalues(lvs); + } + let return_label = recolor_block_label_owned(recolor, return_label); + for (lvs, _) in &mut lambda_params { + recolor_lvalues(recolor, lvs); + } + recolor_exp(recolor, &mut lambda_body); + // set max color when coloring is finished + context.core.set_max_variable_color(recolor.max_color()); + // check arity before expanding + let argloc = *argloc; + core::check_call_arity( + context.core, + *eloc, + || format!("Invalid lambda call of '{}'", v_.name), + param_tys.len(), + argloc, + es.len(), + ); + // expand the call, replacing with a dummy value to take the args by value + let N::Exp_::VarCall(_, sp!(_, args)) = + std::mem::replace(e_, /* dummy */ N::Exp_::UnresolvedError) + else { + unreachable!() + }; + let body_loc = lambda_body.loc; + let annot_body = Box::new(sp(body_loc, N::Exp_::Annotate(lambda_body, result_ty))); + let labeled_seq = VecDeque::from([sp(body_loc, N::SequenceItem_::Seq(annot_body))]); + let labeled_body_ = N::Exp_::Block(N::Block { + name: Some(return_label), + // mark lambda expansion for recursive macro check + from_macro_argument: Some(N::MacroArgument::Lambda(*eloc)), + seq: (N::UseFuns::new(use_fun_color), labeled_seq), + }); + let labeled_body = Box::new(sp(body_loc, labeled_body_)); + // pad args with errors + let args = args.into_iter().chain(std::iter::repeat_with(|| { + sp(argloc, N::Exp_::UnresolvedError) + })); + // Unlike other by-name arguments, we try to check the type of the lambda before + // expanding them macro. That, plus the arity check above, ensures these zips are safe + let mut result: VecDeque<_> = lambda_params + .into_iter() + .zip(args) + .zip(param_tys) + .map(|(((lvs, _lv_ty_opt), arg), param_ty)| { + let param_loc = param_ty.loc; + let arg = Box::new(arg); + let annot_arg = Box::new(sp(param_loc, N::Exp_::Annotate(arg, param_ty))); + sp(param_loc, N::SequenceItem_::Bind(lvs, annot_arg)) + }) + .collect(); + result.push_back(sp(body_loc, N::SequenceItem_::Seq(labeled_body))); + *e_ = N::Exp_::Block(N::Block { + name: None, + from_macro_argument: None, + seq: (N::UseFuns::new(context.macro_color), result), + }); + } + + /////// + // Argument cases + /////// + N::Exp_::Var(sp!(_, v_)) if context.by_name_args.contains_key(v_) => { + context.mark_used(v_); + let (mut arg, expected_ty) = context.by_name_args.get(v_).cloned().unwrap(); + // recolor the arg in case it is used more than once + let next_color = context.core.next_variable_color(); + let recolor_use_funs = false; + let recolor = &mut Recolor::new( + next_color, + /* return already labeled */ None, + recolor_use_funs, + ); + recolor_exp(recolor, &mut arg); + context.core.set_max_variable_color(recolor.max_color()); + + // mark the arg as coming from an argument substitution for recursive checks + match &mut arg.value { + N::Exp_::Block(block) => { + block.from_macro_argument = Some(N::MacroArgument::Substituted(*eloc)) + } + N::Exp_::UnresolvedError => (), + _ => unreachable!("ICE all macro args should have been made blocks in naming"), + }; + + *e_ = N::Exp_::Annotate(Box::new(arg), expected_ty); + } + N::Exp_::VarCall(sp!(_, v_), _) if context.by_name_args.contains_key(v_) => { + context.mark_used(v_); + let (arg, _expected_ty) = context.by_name_args.get(v_).unwrap(); + context.core.env.add_diag(diag!( + TypeSafety::CannotExpandMacro, + (*eloc, "Cannot call non-lambda argument"), + (arg.loc, "Expected a lambda argument") + )); + *e_ = N::Exp_::UnresolvedError; + } + + /////// + // Other var cases + /////// + N::Exp_::Var(sp!(_, v_)) => { + let is_unbound_param = context + .all_params + .get(v_) + .is_some_and(|info| info.argument.is_none()); + if is_unbound_param { + assert!(!context.lambdas.contains_key(v_)); + assert!(!context.by_name_args.contains_key(v_)); + assert!( + context.core.env.has_errors(), + "ICE unbound param should have already resulted in an error" + ); + *e_ = N::Exp_::UnresolvedError; + } + } + N::Exp_::VarCall(sp!(_, v_), sp!(_, es)) => { + exps(context, es); + let is_unbound_param = context + .all_params + .get(v_) + .is_some_and(|info| info.argument.is_none()); + if is_unbound_param { + assert!(!context.lambdas.contains_key(v_)); + assert!(!context.by_name_args.contains_key(v_)); + assert!( + context.core.env.has_errors(), + "ICE unbound param should have already resulted in an error" + ); + *e_ = N::Exp_::UnresolvedError; + } + } + } +} + +fn builtin_function(context: &mut Context, sp!(_, bf_): &mut N::BuiltinFunction) { + match bf_ { + N::BuiltinFunction_::Freeze(ty_opt) => { + if let Some(ty) = ty_opt { + type_(context, ty) + } + } + N::BuiltinFunction_::Assert(_) => (), + } +} + +fn exp_dotted(context: &mut Context, sp!(_, ed_): &mut N::ExpDotted) { + match ed_ { + N::ExpDotted_::Exp(e) => exp(context, e), + N::ExpDotted_::Dot(ed, _) => exp_dotted(context, ed), + } +} + +fn exps(context: &mut Context, es: &mut [N::Exp]) { + for e in es { + exp(context, e) + } +} diff --git a/external-crates/move/crates/move-compiler/src/typing/mod.rs b/external-crates/move/crates/move-compiler/src/typing/mod.rs index 189255a4693e9..45e9a45415846 100644 --- a/external-crates/move/crates/move-compiler/src/typing/mod.rs +++ b/external-crates/move/crates/move-compiler/src/typing/mod.rs @@ -7,6 +7,7 @@ pub mod core; mod dependency_ordering; mod expand; mod infinite_instantiations; +mod macro_expand; mod recursive_structs; pub(crate) mod translate; pub mod visitor; diff --git a/external-crates/move/crates/move-compiler/src/typing/recursive_structs.rs b/external-crates/move/crates/move-compiler/src/typing/recursive_structs.rs index dec930d635425..eb522307a25b3 100644 --- a/external-crates/move/crates/move-compiler/src/typing/recursive_structs.rs +++ b/external-crates/move/crates/move-compiler/src/typing/recursive_structs.rs @@ -105,6 +105,10 @@ fn type_(context: &mut Context, sp!(loc, ty_): &N::Type) { } tys.iter().for_each(|t| type_(context, t)) } + Fun(ts, t) => { + ts.iter().for_each(|t| type_(context, t)); + type_(context, t) + } } } diff --git a/external-crates/move/crates/move-compiler/src/typing/translate.rs b/external-crates/move/crates/move-compiler/src/typing/translate.rs index ff5e0f8a52442..036cfedec633d 100644 --- a/external-crates/move/crates/move-compiler/src/typing/translate.rs +++ b/external-crates/move/crates/move-compiler/src/typing/translate.rs @@ -25,8 +25,8 @@ use crate::{ sui_mode, typing::{ ast as T, - core::{public_testing_visibility, PublicForTesting}, - dependency_ordering, + core::{make_tvar, public_testing_visibility, PublicForTesting, ResolvedFunctionType}, + dependency_ordering, macro_expand, }, FullyCompiledProgram, }; @@ -46,9 +46,9 @@ pub fn program( info, inner: N::Program_ { modules: nmodules }, } = prog; - let mut context = Context::new(compilation_env, pre_compiled_lib, info); + let mut context = Box::new(Context::new(compilation_env, pre_compiled_lib, info)); - // we extract module use funs into the module info context + extract_macros(&mut context, &nmodules); let mut modules = modules(&mut context, nmodules); assert!(context.constraints.is_empty()); @@ -56,6 +56,7 @@ pub fn program( recursive_structs::modules(context.env, &modules); infinite_instantiations::modules(context.env, &modules); let mut prog = T::Program_ { modules }; + // we extract module use funs into the module info context let module_use_funs = context .modules .modules @@ -73,6 +74,47 @@ pub fn program( } } +fn extract_macros(context: &mut Context, modules: &UniqueMap) { + // Merges the methods of the module into the local methods for each macro. + fn merge_use_funs(module_use_funs: &N::UseFuns, mut macro_use_funs: N::UseFuns) -> N::UseFuns { + let N::UseFuns { + color: _, + resolved, + implicit_candidates, + } = module_use_funs; + for (tn, module_methods) in resolved { + let macro_methods = macro_use_funs.resolved.entry(tn.clone()).or_default(); + for (name, method) in module_methods.key_cloned_iter() { + if !macro_methods.contains_key(&name) { + macro_methods.add(name, method.clone()).unwrap(); + } + } + } + for (name, module_candidate) in implicit_candidates.key_cloned_iter() { + if !macro_use_funs.implicit_candidates.contains_key(&name) { + macro_use_funs + .implicit_candidates + .add(name, module_candidate.clone()) + .unwrap(); + } + } + macro_use_funs + } + let all_macro_definitions = modules.ref_map(|_mident, mdef| { + mdef.functions.ref_filter_map(|_name, f| { + let _macro_loc = f.macro_?; + if let N::FunctionBody_::Defined((use_funs, body)) = &f.body.value { + let use_funs = merge_use_funs(&mdef.use_funs, use_funs.clone()); + Some((use_funs, body.clone())) + } else { + None + } + }) + }); + + context.set_macros(all_macro_definitions); +} + fn modules( context: &mut Context, modules: UniqueMap, @@ -115,6 +157,7 @@ fn module( mdef: N::ModuleDefinition, ) -> (T::ModuleDefinition, BTreeSet<(ModuleIdent, Loc)>) { assert!(context.current_package.is_none()); + assert!(context.new_friends.is_empty()); let N::ModuleDefinition { loc, @@ -173,6 +216,7 @@ fn function(context: &mut Context, name: FunctionName, f: N::Function) -> T::Fun attributes, visibility, entry, + macro_, mut signature, body: n_body, } = f; @@ -180,18 +224,25 @@ fn function(context: &mut Context, name: FunctionName, f: N::Function) -> T::Fun assert!(context.constraints.is_empty()); context.reset_for_module_item(); context.current_function = Some(name); + context.in_macro_function = macro_.is_some(); process_attributes(context, &attributes); let visibility = match public_testing_visibility(context.env, context.current_package, &name, entry) { Some(PublicForTesting::Entry(loc)) => Visibility::Public(loc), None => visibility, }; - function_signature(context, &signature); + function_signature(context, macro_, &signature); expand::function_signature(context, &mut signature); - let body = function_body(context, n_body); - unused_let_muts(context); + let body = if macro_.is_some() { + sp(n_body.loc, T::FunctionBody_::Macro) + } else { + let body = function_body(context, n_body); + unused_let_muts(context); + body + }; context.current_function = None; + context.in_macro_function = false; context.env.pop_warning_filter_scope(); T::Function { warning_filter, @@ -199,16 +250,22 @@ fn function(context: &mut Context, name: FunctionName, f: N::Function) -> T::Fun attributes, visibility, entry, + macro_, signature, body, } } -fn function_signature(context: &mut Context, sig: &N::FunctionSignature) { +fn function_signature(context: &mut Context, macro_: Option, sig: &N::FunctionSignature) { assert!(context.constraints.is_empty()); for (mut_, param, param_ty) in &sig.parameters { - let param_ty = core::instantiate(context, param_ty.clone()); + let mut param_ty = param_ty.clone(); + if macro_.is_some() { + core::give_tparams_all_abilities(&mut param_ty) + }; + let param_ty = core::instantiate(context, param_ty); + // TODO we can relax this for macros once we can bind tuples to variables context.add_single_type_constraint( param_ty.loc, "Invalid parameter type", @@ -216,7 +273,11 @@ fn function_signature(context: &mut Context, sig: &N::FunctionSignature) { ); context.declare_local(*mut_, *param, param_ty); } - context.return_type = Some(core::instantiate(context, sig.return_type.clone())); + let mut return_type = sig.return_type.clone(); + if macro_.is_some() { + core::give_tparams_all_abilities(&mut return_type) + }; + context.return_type = Some(core::instantiate(context, return_type)); core::solve_constraints(context); } @@ -277,7 +338,7 @@ fn constant(context: &mut Context, _name: ConstantName, nconstant: N::Constant) ); context.return_type = Some(signature.clone()); - let mut value = exp_(context, nvalue); + let mut value = exp(context, Box::new(nvalue)); unused_let_muts(context); subtype( context, @@ -300,7 +361,7 @@ fn constant(context: &mut Context, _name: ConstantName, nconstant: N::Constant) attributes, loc, signature, - value, + value: *value, } } @@ -441,7 +502,7 @@ mod check_valid_constant { exp(context, ef); "'if' expressions are" } - E::While(eb, _, eloop) => { + E::While(_, eb, eloop) => { exp(context, eb); exp(context, eloop); "'while' expressions are" @@ -709,6 +770,17 @@ fn visit_type_params( } } }, + Type_::Fun(args, result) => { + for ty in args { + visit_type_params(context, ty, ParamPos::NonPhantom(NonPhantomPos::TypeArg), f) + } + visit_type_params( + context, + result, + ParamPos::NonPhantom(NonPhantomPos::TypeArg), + f, + ) + } Type_::Var(_) | Type_::Anything | Type_::UnresolvedError => {} Type_::Unit => {} } @@ -769,6 +841,9 @@ fn has_unresolved_error_type(ty: &Type) -> bool { Type_::UnresolvedError => true, Type_::Ref(_, ty) => has_unresolved_error_type(ty), Type_::Apply(_, _, ty_args) => ty_args.iter().any(has_unresolved_error_type), + Type_::Fun(args, result) => { + args.iter().any(has_unresolved_error_type) || has_unresolved_error_type(result) + } Type_::Param(_) | Type_::Var(_) | Type_::Anything | Type_::Unit => false, } } @@ -829,6 +904,37 @@ fn typing_error T>( (loc2, msg2) ) } + FunArityMismatch(a1, t1, a2, t2) => { + let loc1 = core::best_loc(subst, &t1); + let loc2 = core::best_loc(subst, &t2); + let t1_str = core::error_format(&t1, subst); + let t2_str = core::error_format(&t2, subst); + let msg1 = if from_subtype { + format!("Given lambda with {} arguments: {}", a1, t1_str) + } else { + format!( + "Found a lambda type with {} arguments: {}. It is not compatible with the \ + other type with {} arguments.", + a1, t1_str, a2 + ) + }; + let msg2 = if from_subtype { + format!("Expected a lambda with {} arguments: {}", a2, t2_str) + } else { + format!( + "Found a lambda type with {} arguments: {}. It is not compatible with the \ + other type with {} arguments.", + a2, t2_str, a1 + ) + }; + + diag!( + TypeSafety::JoinError, + (loc, msg), + (loc1, msg1), + (loc2, msg2) + ) + } Incompatible(t1, t2) => { let loc1 = core::best_loc(subst, &t1); let loc2 = core::best_loc(subst, &t2); @@ -989,7 +1095,7 @@ fn sequence(context: &mut Context, (use_funs, seq): N::Sequence) -> T::Sequence for (idx, sp!(loc, ns_)) in seq.into_iter().enumerate() { match ns_ { NS::Seq(ne) => { - let e = exp_(context, ne); + let e = exp(context, ne); // If it is not the last element if idx < len - 1 { context.add_ability_constraint( @@ -1002,7 +1108,7 @@ fn sequence(context: &mut Context, (use_funs, seq): N::Sequence) -> T::Sequence Ability_::Drop, ) } - work_queue.push_front(SeqCase::Seq(loc, Box::new(e))); + work_queue.push_front(SeqCase::Seq(loc, e)); } NS::Declare(nbind, ty_opt) => { let instantiated_ty_op = ty_opt.map(|t| core::instantiate(context, t)); @@ -1010,13 +1116,9 @@ fn sequence(context: &mut Context, (use_funs, seq): N::Sequence) -> T::Sequence work_queue.push_front(SeqCase::Declare { loc, b }); } NS::Bind(nbind, nr) => { - let e = exp_(context, nr); + let e = exp(context, nr); let b = bind_list(context, nbind, Some(e.ty.clone())); - work_queue.push_front(SeqCase::Bind { - loc, - b, - e: Box::new(e), - }); + work_queue.push_front(SeqCase::Bind { loc, b, e }); } } } @@ -1047,26 +1149,21 @@ fn sequence_type((_, seq): &T::Sequence) -> &Type { } fn exp_vec(context: &mut Context, es: Vec) -> Vec { - es.into_iter().map(|e| exp_(context, e)).collect() + es.into_iter().map(|e| *exp(context, Box::new(e))).collect() } fn exp(context: &mut Context, ne: Box) -> Box { - Box::new(exp_(context, *ne)) -} - -fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { use N::Exp_ as NE; use T::UnannotatedExp_ as TE; - - if matches!(ne_, NE::BinopExp(..)) { + if matches!(ne.value, NE::BinopExp(..)) { return process_binops!( (BinOp, Loc), - T::Exp, - sp(eloc, ne_), + Box, + *ne, sp!(loc, cur_), cur_, NE::BinopExp(lhs, op, rhs) => { (*lhs, (op, loc), *rhs) }, - { exp_(context, sp(loc, cur_)) }, + { exp(context, Box::new(sp(loc, cur_))) }, value_stack, (bop, loc) => { let el = value_stack.pop().expect("ICE binop typing issue"); @@ -1076,6 +1173,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { ); } + let sp!(eloc, ne_) = *ne; let (ty, e_) = match ne_ { NE::Unit { trailing } => (sp(eloc, Type_::Unit), TE::Unit { trailing }), NE::Value(sp!(vloc, Value_::InferredNum(v))) => ( @@ -1098,7 +1196,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { let ty = context.get_local_type(&var); (ty, TE::Use(var)) } - NE::MethodCall(ndotted, f, ty_args_opt, sp!(argloc, nargs_)) => { + NE::MethodCall(ndotted, f, /* is_macro */ None, ty_args_opt, sp!(argloc, nargs_)) => { let (edotted, last_ty) = exp_dotted(context, None, ndotted); let args = exp_vec(context, nargs_); let ty_call_opt = method_call( @@ -1119,10 +1217,51 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { Some(ty_call) => ty_call, } } - NE::ModuleCall(m, f, ty_args_opt, sp!(argloc, nargs_)) => { + NE::ModuleCall(m, f, /* is_macro */ None, ty_args_opt, sp!(argloc, nargs_)) => { let args = exp_vec(context, nargs_); module_call(context, eloc, m, f, ty_args_opt, argloc, args) } + NE::MethodCall(ndotted, f, Some(macro_call_loc), ty_args_opt, sp!(argloc, nargs_)) => { + let (edotted, last_ty) = exp_dotted(context, None, ndotted); + let ty_call_opt = macro_method_call( + context, + eloc, + edotted, + last_ty, + f, + macro_call_loc, + ty_args_opt, + argloc, + nargs_, + ); + match ty_call_opt { + None => { + assert!(context.env.has_errors()); + (context.error_type(eloc), TE::UnresolvedError) + } + Some(ty_call) => ty_call, + } + } + NE::ModuleCall(m, f, Some(macro_call_loc), ty_args_opt, sp!(argloc, nargs_)) => { + macro_module_call( + context, + eloc, + m, + f, + macro_call_loc, + ty_args_opt, + argloc, + nargs_, + ) + } + NE::VarCall(_, sp!(_, nargs_)) => { + exp_vec(context, nargs_); + assert!( + context.env.has_errors(), + "ICE unbound var call. Should be expanded" + ); + (context.error_type(eloc), TE::UnresolvedError) + } NE::Builtin(b, sp!(argloc, nargs_)) => { let args = exp_vec(context, nargs_); builtin_call(context, eloc, b, argloc, args) @@ -1153,7 +1292,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { ); (ty, TE::IfElse(eb, et, ef)) } - NE::While(nb, name, nloop) => { + NE::While(name, nb, nloop) => { let eb = exp(context, nb); let bloc = eb.exp.loc; subtype( @@ -1164,7 +1303,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { Type_::bool(bloc), ); let (_has_break, ty, body) = loop_body(context, eloc, name, false, nloop); - (sp(eloc, ty.value), TE::While(eb, name, body)) + (sp(eloc, ty.value), TE::While(name, eb, body)) } NE::Loop(name, nloop) => { let (has_break, ty, body) = loop_body(context, eloc, name, true, nloop); @@ -1175,25 +1314,45 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { }; (sp(eloc, ty.value), eloop) } - NE::NamedBlock(name, body) => { - let seq = sequence(context, body); + NE::Block(N::Block { + name, + from_macro_argument, + seq: nseq, + }) => { + context.maybe_enter_macro_argument(from_macro_argument, nseq.0.color); + let seq = sequence(context, nseq); let seq_ty = sequence_type(&seq).clone(); - let final_type = if let Some(local_return_type) = context.named_block_type_opt(name) { - join( - context, - eloc, - || "Invalid named block", - seq_ty, - local_return_type, - ) + let res = if let Some(name) = name { + let final_type = if let Some(local_return_type) = context.named_block_type_opt(name) + { + let msg = if let Some(N::MacroArgument::Lambda(_)) = from_macro_argument { + || "Invalid lambda return" + } else { + || "Invalid named block" + }; + join(context, eloc, msg, seq_ty, local_return_type) + } else { + seq_ty + }; + (sp(eloc, final_type.value), TE::NamedBlock(name, seq)) } else { - seq_ty + (seq_ty, TE::Block(seq)) }; - (sp(eloc, final_type.value), TE::NamedBlock(name, seq)) + context.maybe_exit_macro_argument(from_macro_argument); + res } - NE::Block(nseq) => { - let seq = sequence(context, nseq); - (sequence_type(&seq).clone(), TE::Block(seq)) + + NE::Lambda(_) => { + if context + .env + .check_feature(FeatureGate::MacroFuns, context.current_package, eloc) + { + let msg = "Lambdas can only be used directly as arguments to 'macro' functions"; + context + .env + .add_diag(diag!(TypeSafety::UnexpectedLambda, (eloc, msg))) + } + (context.error_type(eloc), TE::UnresolvedError) } NE::Assign(na, nr) => { @@ -1231,13 +1390,13 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { subtype(context, eloc, || "Invalid abort", ecode.ty.clone(), code_ty); (sp(eloc, Type_::Anything), TE::Abort(ecode)) } - NE::Give(name, rhs) => { + NE::Give(usage, name, rhs) => { let break_rhs = exp(context, rhs); let loop_ty = context.named_block_type(name, eloc); subtype( context, eloc, - || "Invalid break", + || format!("Invalid {usage}"), break_rhs.ty.clone(), loop_ty, ); @@ -1310,7 +1469,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { add_field_types(context, eloc, "argument", &m, &n, targs.clone(), nfields); let tfields = typed_nfields.map(|f, (idx, (fty, narg))| { - let arg = exp_(context, narg); + let arg = exp(context, Box::new(narg)); subtype( context, arg.exp.loc, @@ -1318,7 +1477,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { arg.ty.clone(), fty.clone(), ); - (idx, (fty, arg)) + (idx, (fty, *arg)) }); if !context.is_current_module(&m) { let msg = format!( @@ -1334,11 +1493,11 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { } NE::ExpDotted(DottedUsage::Use, sp!(_, N::ExpDotted_::Exp(ner))) => { - let er = exp_(context, *ner); + let er = exp(context, ner); (er.ty, er.exp.value) } NE::ExpDotted(DottedUsage::Borrow(mut_), sp!(_, N::ExpDotted_::Exp(ner))) => { - let er = exp_(context, *ner); + let er = exp(context, ner); warn_on_constant_borrow(context, eloc, &er); context.add_base_type_constraint(eloc, "Invalid borrow", er.ty.clone()); let ty = sp(eloc, Type_::Ref(mut_, Box::new(er.ty.clone()))); @@ -1354,7 +1513,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { (ty, eborrow) } NE::ExpDotted(DottedUsage::Move(loc), sp!(_, N::ExpDotted_::Exp(ner))) => { - let er = exp_(context, *ner); + let er = exp(context, ner); match er.exp.value { TE::Use(var) => ( @@ -1379,7 +1538,7 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { } } NE::ExpDotted(DottedUsage::Copy(loc), sp!(_, N::ExpDotted_::Exp(ner))) => { - let er = exp_(context, *ner); + let er = exp(context, ner); let (ty, ecopy) = match er.exp.value { TE::Use(var) => ( er.ty, @@ -1433,25 +1592,20 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { NE::Cast(nl, ty) => { let el = exp(context, nl); - let tyloc = ty.loc; let rhs = core::instantiate(context, ty); context.add_numeric_constraint(el.exp.loc, "as", el.ty.clone()); - context.add_numeric_constraint(tyloc, "as", rhs.clone()); + context.add_numeric_constraint(el.exp.loc, "as", rhs.clone()); (rhs.clone(), TE::Cast(el, Box::new(rhs))) } NE::Annotate(nl, ty_annot) => { let el = exp(context, nl); let annot_loc = ty_annot.loc; + let msg = || "Invalid type annotation"; let rhs = core::instantiate(context, ty_annot); - subtype( - context, - annot_loc, - || "Invalid type annotation", - el.ty.clone(), - rhs.clone(), - ); - (rhs.clone(), TE::Annotate(el, Box::new(rhs))) + subtype(context, annot_loc, msg, el.ty.clone(), rhs.clone()); + let e_ = TE::Annotate(el, Box::new(rhs.clone())); + (rhs, e_) } NE::UnresolvedError => { assert!(context.env.has_errors()); @@ -1460,10 +1614,16 @@ fn exp_(context: &mut Context, sp!(eloc, ne_): N::Exp) -> T::Exp { NE::BinopExp(..) => unreachable!(), }; - T::exp(ty, sp(eloc, e_)) + Box::new(T::exp(ty, sp(eloc, e_))) } -fn binop(context: &mut Context, el: T::Exp, bop: BinOp, loc: Loc, er: T::Exp) -> T::Exp { +fn binop( + context: &mut Context, + el: Box, + bop: BinOp, + loc: Loc, + er: Box, +) -> Box { use BinOp_::*; use T::UnannotatedExp_ as TE; let msg = || format!("Incompatible arguments to '{}'", &bop); @@ -1527,13 +1687,10 @@ fn binop(context: &mut Context, el: T::Exp, bop: BinOp, loc: Loc, er: T::Exp) -> Range | Implies | Iff => panic!("specification operator unexpected"), }; - T::exp( + Box::new(T::exp( ty, - sp( - loc, - TE::BinopExp(Box::new(el), bop, Box::new(operand_ty), Box::new(er)), - ), - ) + sp(loc, TE::BinopExp(el, bop, Box::new(operand_ty), er)), + )) } fn loop_body( @@ -2158,16 +2315,32 @@ impl crate::shared::ast_debug::AstDebug for ExpDotted_ { fn method_call( context: &mut Context, loc: Loc, - mut edotted: ExpDotted, + edotted: ExpDotted, edotted_ty: Type, method: Name, ty_args_opt: Option>, argloc: Loc, mut args: Vec, ) -> Option<(Type, T::UnannotatedExp_)> { + use T::UnannotatedExp_ as TE; + let (m, f, fty, first_arg) = + method_call_resolve(context, loc, edotted, edotted_ty, method, ty_args_opt)?; + args.insert(0, first_arg); + let (mut call, ret_ty) = module_call_impl(context, loc, m, f, fty, argloc, args); + call.method_name = Some(method); + Some((ret_ty, TE::ModuleCall(Box::new(call)))) +} + +fn method_call_resolve( + context: &mut Context, + loc: Loc, + mut edotted: ExpDotted, + edotted_ty: Type, + method: Name, + ty_args_opt: Option>, +) -> Option<(ModuleIdent, FunctionName, ResolvedFunctionType, T::Exp)> { use TypeName_ as TN; use Type_ as Ty; - use T::UnannotatedExp_ as TE; let edotted_ty_unfolded = core::unfold_type(&context.subst, edotted_ty.clone()); let edotted_bty = edotted_ty_base(&edotted_ty_unfolded); let tn = match &edotted_bty.value { @@ -2177,7 +2350,7 @@ fn method_call( Ty::Anything => { "Unable to infer type for method call. Try annotating this type".to_owned() } - Ty::Unit | Ty::Apply(_, sp!(_, TN::Multiple(_)), _) => { + Ty::Unit | Ty::Apply(_, sp!(_, TN::Multiple(_)), _) | Ty::Fun(_, _) => { let tsubst = core::error_format_(t, &context.subst); format!( "Method calls are only supported on single types. \ @@ -2206,10 +2379,10 @@ fn method_call( return None; } }; - let (_defined_loc, m, f, targs, parameters, ret_ty) = + let (m, f, fty) = core::make_method_call_type(context, loc, &edotted_ty, tn, method, ty_args_opt)?; - let first_arg = match ¶meters[0].1.value { + let first_arg = match &fty.params[0].1.value { Ty::Ref(mut_, _) => { // add a borrow if needed let mut cur = &mut edotted; @@ -2231,10 +2404,7 @@ fn method_call( } _ => exp_dotted_to_owned_value(context, DottedUsage::Use, loc, edotted, edotted_ty), }; - args.insert(0, first_arg); - let mut call = module_call_impl(context, loc, m, f, targs, parameters, argloc, args); - call.method_name = Some(method); - Some((ret_ty, TE::ModuleCall(Box::new(call)))) + Some((m, f, fty, first_arg)) } fn edotted_ty_base(ty: &Type) -> &Type { @@ -2243,7 +2413,8 @@ fn edotted_ty_base(ty: &Type) -> &Type { | Type_::Param(_) | Type_::Anything | Type_::UnresolvedError - | Type_::Apply(_, _, _) => ty, + | Type_::Apply(_, _, _) + | Type_::Fun(_, _) => ty, Type_::Ref(_, inner) => inner, Type_::Var(_) => panic!("ICE unfolding failed"), } @@ -2258,9 +2429,8 @@ fn module_call( argloc: Loc, args: Vec, ) -> (Type, T::UnannotatedExp_) { - let (_, ty_args, parameters, ret_ty) = - core::make_function_type(context, loc, &m, &f, ty_args_opt); - let call = module_call_impl(context, loc, m, f, ty_args, parameters, argloc, args); + let fty = core::make_function_type(context, loc, &m, &f, ty_args_opt); + let (call, ret_ty) = module_call_impl(context, loc, m, f, fty, argloc, args); (ret_ty, T::UnannotatedExp_::ModuleCall(Box::new(call))) } @@ -2269,11 +2439,20 @@ fn module_call_impl( loc: Loc, m: ModuleIdent, f: FunctionName, - ty_args: Vec, - parameters: Vec<(N::Var, Type)>, + fty: ResolvedFunctionType, argloc: Loc, args: Vec, -) -> T::ModuleCall { +) -> (T::ModuleCall, Type) { + let ResolvedFunctionType { + declared, + macro_, + ty_args, + params: parameters, + return_, + } = fty; + check_call_target( + context, loc, /* is_macro_call */ None, macro_, declared, f, + ); let (arguments, arg_tys) = call_args( context, loc, @@ -2306,7 +2485,7 @@ fn module_call_impl( .entry(m.value) .or_default() .insert(f.value()); - call + (call, return_) } fn builtin_call( @@ -2443,24 +2622,7 @@ fn make_arg_types S>( mut given: Vec, ) -> Vec { let given_len = given.len(); - if given_len != arity { - let code = if given_len < arity { - TypeSafety::TooFewArguments - } else { - TypeSafety::TooManyArguments - }; - let cmsg = format!( - "{}. The call expected {} argument(s) but got {}", - msg(), - arity, - given_len - ); - context.env.add_diag(diag!( - code, - (loc, cmsg), - (argloc, format!("Found {} argument(s) here", given_len)), - )); - } + core::check_call_arity(context, loc, msg, arity, argloc, given_len); while given.len() < arity { given.push(context.error_type(argloc)) } @@ -2470,6 +2632,302 @@ fn make_arg_types S>( given } +fn check_call_target( + context: &mut Context, + call_loc: Loc, + is_macro_call: Option, + declared_macro_modifier: Option, + declared: Loc, + f: FunctionName, +) { + let decl_is_macro = declared_macro_modifier.is_some(); + if is_macro_call.is_some() == decl_is_macro { + return; + } + + let macro_call_loc = is_macro_call.unwrap_or(call_loc); + let decl_loc = declared_macro_modifier.unwrap_or(declared); + let call_msg = if decl_is_macro { + format!( + "'{f}' is a macro function and must be called with a `!`. \ + Try replacing with '{f}!'" + ) + } else { + format!( + "'{f}' is not a macro function and cannot be called with a `!`. \ + Try replacing with '{f}'" + ) + }; + let decl_msg = if decl_is_macro { + "'macro' function is declared here" + } else { + "Normal (non-'macro') function is declared here" + }; + context.env.add_diag(diag!( + TypeSafety::InvalidCallTarget, + (macro_call_loc, call_msg), + (decl_loc, decl_msg), + )); +} + +//************************************************************************************************** +// Macro +//************************************************************************************************** + +fn macro_method_call( + context: &mut Context, + loc: Loc, + edotted: ExpDotted, + edotted_ty: Type, + method: Name, + macro_call_loc: Loc, + ty_args_opt: Option>, + argloc: Loc, + nargs: Vec, +) -> Option<(Type, T::UnannotatedExp_)> { + let (m, f, fty, first_arg) = + method_call_resolve(context, loc, edotted, edotted_ty, method, ty_args_opt)?; + let mut args = vec![macro_expand::EvalStrategy::ByValue(first_arg)]; + args.extend( + nargs + .into_iter() + .map(|e| macro_expand::EvalStrategy::ByName(convert_macro_arg_to_block(context, e))), + ); + let (type_arguments, args, return_ty) = + macro_call_impl(context, loc, m, f, macro_call_loc, fty, argloc, args); + Some(expand_macro( + context, + loc, + m, + f, + type_arguments, + args, + return_ty, + )) +} + +fn macro_module_call( + context: &mut Context, + loc: Loc, + m: ModuleIdent, + f: FunctionName, + macro_call_loc: Loc, + ty_args_opt: Option>, + argloc: Loc, + nargs: Vec, +) -> (Type, T::UnannotatedExp_) { + let fty = core::make_function_type(context, loc, &m, &f, ty_args_opt); + let args = nargs + .into_iter() + .map(|e| macro_expand::EvalStrategy::ByName(convert_macro_arg_to_block(context, e))) + .collect(); + let (type_arguments, args, return_ty) = + macro_call_impl(context, loc, m, f, macro_call_loc, fty, argloc, args); + expand_macro(context, loc, m, f, type_arguments, args, return_ty) +} + +fn macro_call_impl( + context: &mut Context, + loc: Loc, + m: ModuleIdent, + f: FunctionName, + macro_call_loc: Loc, + fty: ResolvedFunctionType, + argloc: Loc, + mut args: Vec>, +) -> (Vec, Vec, Type) { + use macro_expand::EvalStrategy; + let ResolvedFunctionType { + declared, + macro_, + ty_args, + params: parameters, + return_, + } = fty; + check_call_target( + context, + loc, + /* is_macro_call */ Some(macro_call_loc), + macro_, + declared, + f, + ); + core::check_call_arity( + context, + loc, + || format!("Invalid call of '{}::{}'", &m, &f), + parameters.len(), + argloc, + args.len(), + ); + // instantiate the param types to check for constraints, even if the argument isn't used + for (_, param_ty) in ¶meters { + core::instantiate(context, param_ty.clone()); + } + while args.len() < parameters.len() { + args.push(EvalStrategy::ByName(sp(loc, N::Exp_::UnresolvedError))); + } + while args.len() > parameters.len() { + args.pop(); + } + assert!(args.len() == parameters.len()); + let args_with_ty = args + .into_iter() + .zip(parameters) + .map(|(arg, (param, param_ty))| match arg { + EvalStrategy::ByValue(e) => { + let msg = || { + format!( + "Invalid call of '{}::{}'. Invalid argument for parameter '{}'", + &m, &f, ¶m.value.name + ) + }; + subtype(context, loc, msg, e.ty.clone(), param_ty.clone()); + EvalStrategy::ByValue(e) + } + EvalStrategy::ByName(ne) => { + let expected_ty = + expected_by_name_arg_type(context, loc, &m, &f, ¶m, &ne, param_ty.clone()); + EvalStrategy::ByName((ne, expected_ty)) + } + }) + .collect(); + context + .used_module_members + .entry(m.value) + .or_default() + .insert(f.value()); + (ty_args, args_with_ty, return_) +} + +// If the argument is a lambda, we need to check that the lambda's type matches the expected type +// so that any calls to the lambda can be properly expanded +// Otherwise, we just return the parameters type +fn expected_by_name_arg_type( + context: &mut Context, + call_loc: Loc, + m: &ModuleIdent, + f: &FunctionName, + param: &N::Var, + ne: &N::Exp, + param_ty: Type, +) -> Type { + let (eloc, lambda) = match ne { + sp!(eloc, N::Exp_::Lambda(l)) => (*eloc, l), + _ => return param_ty, + }; + let param_tys = lambda + .parameters + .value + .iter() + .map(|(p, ty_opt)| { + if let Some(ty) = ty_opt { + core::instantiate(context, ty.clone()) + } else { + core::make_tvar(context, p.loc) + } + }) + .collect(); + let ret_ty = if let Some(ty) = lambda.return_type.clone() { + core::instantiate(context, ty) + } else { + make_tvar(context, lambda.body.loc) + }; + let tfun = sp(eloc, Type_::Fun(param_tys, Box::new(ret_ty))); + let msg = || { + format!( + "Invalid call of '{}::{}'. Invalid argument for parameter '{}'", + m, &f, ¶m.value.name + ) + }; + subtype(context, call_loc, msg, tfun.clone(), param_ty); + // prefer the lambda type over the parameters to preserve annotations on the lambda + tfun +} + +fn expand_macro( + context: &mut core::Context, + call_loc: Loc, + m: ModuleIdent, + f: FunctionName, + type_args: Vec, + args: Vec, + return_ty: Type, +) -> (Type, T::UnannotatedExp_) { + use T::SequenceItem_ as TS; + use T::UnannotatedExp_ as TE; + + let valid = context.add_macro_expansion(m, f, call_loc); + if !valid { + assert!(context.env.has_errors()); + return (context.error_type(call_loc), TE::UnresolvedError); + } + let res = match macro_expand::call(context, call_loc, m, f, type_args, args, return_ty) { + None => { + assert!(context.env.has_errors()); + (context.error_type(call_loc), TE::UnresolvedError) + } + Some(macro_expand::ExpandedMacro { + by_value_args, + body, + }) => { + // bind the locals + let mut seq: VecDeque<_> = by_value_args + .into_iter() + .map(|(sp!(vloc, v_), e)| { + let lvalue_ = match v_ { + Some(var_) => N::LValue_::Var { + mut_: None, + var: sp(vloc, var_), + unused_binding: false, + }, + None => N::LValue_::Ignore, + }; + let lvalue = sp(vloc, lvalue_); + let lvalues = sp(vloc, vec![lvalue]); + let b = bind_list(context, lvalues, Some(e.ty.clone())); + let lvalue_ty = lvalues_expected_types(context, &b); + sp(b.loc, TS::Bind(b, lvalue_ty, Box::new(e))) + }) + .collect(); + // add the body + let body = exp(context, body); + let ty = body.ty.clone(); + seq.push_back(sp(body.exp.loc, TS::Seq(body))); + let use_funs = N::UseFuns::new(context.current_call_color()); + let e_ = TE::Block((use_funs, seq)); + (ty, e_) + } + }; + context.pop_macro_expansion(&m, &f); + res +} + +/// We need to make sure that arguments to macro calls are either lambdas or a Block +/// These arguments are call-by-name so the whole expression is substituted in. So we need to track +/// metadata about the scope where these expressions were originally written. +/// The Block lets us track two pieces of metadata +/// 1) We can track the use_fun_scope, which is used for resolving method calls correctly +/// 2) After substitution, we can mark the Block as coming from a macro expansion which is used +/// for tracking recursive macro calls +fn convert_macro_arg_to_block(context: &Context, sp!(loc, ne_): N::Exp) -> N::Exp { + let ne_ = match ne_ { + N::Exp_::Block(_) | N::Exp_::Lambda(_) | N::Exp_::UnresolvedError => ne_, + ne_ => { + let color = context.current_call_color(); + let seq_ = VecDeque::from([sp(loc, N::SequenceItem_::Seq(Box::new(sp(loc, ne_))))]); + let seq = (N::UseFuns::new(color), seq_); + let block = N::Block { + name: None, + from_macro_argument: None, + seq, + }; + N::Exp_::Block(block) + } + }; + sp(loc, ne_) +} + //************************************************************************************************** // Utils //************************************************************************************************** @@ -2513,7 +2971,7 @@ fn unused_let_muts(context: &mut Context) { for (v, local) in locals { let Local { mut_, used_mut, .. } = local; let Some(mut_loc) = mut_ else { continue }; - if used_mut.is_none() { + if used_mut.is_none() && !v.value.starts_with_underscore() { let decl_msg = format!("The variable '{}' is never used mutably", v.value.name); let mut_msg = "Consider removing the 'mut' declaration here"; context.env.add_diag(diag!( diff --git a/external-crates/move/crates/move-compiler/src/typing/visitor.rs b/external-crates/move/crates/move-compiler/src/typing/visitor.rs index 451af272b5d2e..104790f1ea848 100644 --- a/external-crates/move/crates/move-compiler/src/typing/visitor.rs +++ b/external-crates/move/crates/move-compiler/src/typing/visitor.rs @@ -165,7 +165,7 @@ pub trait TypingVisitorContext { self.visit_exp(e2); self.visit_exp(e3); } - E::While(e1, _, e2) => { + E::While(_, e1, e2) => { self.visit_exp(e1); self.visit_exp(e2); } diff --git a/external-crates/move/crates/move-compiler/src/unit_test/filter_test_members.rs b/external-crates/move/crates/move-compiler/src/unit_test/filter_test_members.rs index d567cb6a334c0..3bef869e80ae8 100644 --- a/external-crates/move/crates/move-compiler/src/unit_test/filter_test_members.rs +++ b/external-crates/move/crates/move-compiler/src/unit_test/filter_test_members.rs @@ -148,7 +148,7 @@ fn create_test_poison(mloc: Loc) -> P::ModuleMember { )]; let nop_call = P::Exp_::Call( sp(mloc, P::NameAccessChain_::Three(mod_addr_name, fn_name)), - false, + None, None, sp(mloc, args_), ); @@ -159,6 +159,7 @@ fn create_test_poison(mloc: Loc) -> P::ModuleMember { loc: mloc, visibility: P::Visibility::Internal, entry: Some(mloc), // it's a bit of a hack to avoid treating this function as unused + macro_: None, signature, name: P::FunctionName(sp(mloc, "unit_test_poison".into())), body: sp( diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.exp b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.exp new file mode 100644 index 0000000000000..f63ac58a6fa9f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.exp @@ -0,0 +1,8 @@ +error[E02007]: invalid 'fun' declaration + ┌─ tests/move_2024/expansion/entry_macro.move:3:5 + │ +3 │ entry macro fun foo() {} + │ ^^^^^ ----- Function declared as 'macro' here + │ │ + │ Invalid function declaration. It is meaningless for 'macro' functions to be 'entry' since they are fully-expanded inline during compilation + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.move new file mode 100644 index 0000000000000..3b993d04b6929 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/entry_macro.move @@ -0,0 +1,4 @@ +module a::m { + // cannot have entry macro + entry macro fun foo() {} +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.exp b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.exp new file mode 100644 index 0000000000000..ad31d783c4dd5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.exp @@ -0,0 +1,30 @@ +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_for_non_macro.move:2:29 + │ +2 │ public struct S() + │ ^^ Invalid type parameter name. Only 'macro fun' type parameter names cat start with '$' + │ + = Type parameter names starting with '$' indicate that their arguments do not have to satisfy certain constraints before the macro is expanded, meaning types like '&mut u64' or '(bool, u8)' may be used as arguments. + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_for_non_macro.move:3:16 + │ +3 │ fun foo($x: u64, $f: |u64|) { + │ ^^ Invalid parameter name '$x'. Non-'macro' parameter names cannot start with '$' + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_for_non_macro.move:3:25 + │ +3 │ fun foo($x: u64, $f: |u64|) { + │ ^^ Invalid parameter name '$f'. Non-'macro' parameter names cannot start with '$' + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + +error[E04030]: invalid usage of lambda type + ┌─ tests/move_2024/expansion/macro_identifier_for_non_macro.move:3:29 + │ +3 │ fun foo($x: u64, $f: |u64|) { + │ ^^^^^ Unexpected lambda type. Lambdas can only be used with 'macro' functions, as parameters or direct arguments + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.move new file mode 100644 index 0000000000000..d53ead302e112 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_for_non_macro.move @@ -0,0 +1,6 @@ +module a::m { + public struct S() + fun foo($x: u64, $f: |u64|) { + $f($x) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.exp b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.exp new file mode 100644 index 0000000000000..e539f0cc03d3d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.exp @@ -0,0 +1,24 @@ +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_invalid_position.move:1:9 + │ +1 │ address $a { + │ ^^ Invalid address name '$a'. Identifiers starting with '$' can be used only for parameters and type paramters + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_invalid_position.move:2:8 + │ +2 │ module $m {} + │ ^^ Invalid module name '$m'. Identifiers starting with '$' can be used only for parameters and type paramters + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_invalid_position.move:5:8 + │ +5 │ module $a::m {} + │ ^^ Invalid address name '$a'. Identifiers starting with '$' can be used only for parameters and type paramters + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_invalid_position.move:6:8 + │ +6 │ module $b::m {} + │ ^^ Invalid address name '$b'. Identifiers starting with '$' can be used only for parameters and type paramters + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.move new file mode 100644 index 0000000000000..e7ac8a8b28fd1 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_position.move @@ -0,0 +1,6 @@ +address $a { +module $m {} +} + +module $a::m {} +module $b::m {} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.exp b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.exp new file mode 100644 index 0000000000000..f19763d95e51e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.exp @@ -0,0 +1,30 @@ +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_missing.move:2:19 + │ +2 │ macro fun foo(x: u64, f: |u64|) { + │ ----- ^ Invalid type parameter name. 'macro fun' type parameter names must start with '$' + │ │ + │ Declared 'macro' here + │ + = Type parameter names starting with '$' indicate that their arguments do not have to satisfy certain constraints before the macro is expanded, meaning types like '&mut u64' or '(bool, u8)' may be used as arguments. + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_missing.move:2:22 + │ +2 │ macro fun foo(x: u64, f: |u64|) { + │ ----- ^ Invalid parameter name 'x'. 'macro' parameter names must start with '$' (or must be '_') + │ │ + │ Declared 'macro' here + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_missing.move:2:30 + │ +2 │ macro fun foo(x: u64, f: |u64|) { + │ ----- ^ Invalid parameter name 'f'. 'macro' parameter names must start with '$' (or must be '_') + │ │ + │ Declared 'macro' here + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.move new file mode 100644 index 0000000000000..f8deac7306a3c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_missing.move @@ -0,0 +1,5 @@ +module a::m { + macro fun foo(x: u64, f: |u64|) { + f(x) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move new file mode 100644 index 0000000000000..2eff747f7dd4a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move @@ -0,0 +1,16 @@ +module a::m { + // TODO should we allow all of these? + macro fun foo<$T, $x, $_>( + _: u64, + $_: u64, + $f: |u64| -> u64, + $X: bool, + $u64: u8, + $vector: address, + ) { + $f($_); + $X; + $u64; + $vector; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.exp b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.exp new file mode 100644 index 0000000000000..0072ff4543de5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.exp @@ -0,0 +1,8 @@ +error[E02007]: invalid 'fun' declaration + ┌─ tests/move_2024/expansion/native_macro.move:3:5 + │ +3 │ native macro fun foo(); + │ ^^^^^^ ----- Function declared as 'macro' here + │ │ + │ Invalid function declaration. 'native' functions cannot be 'macro' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.move new file mode 100644 index 0000000000000..d4a245b94bb16 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/native_macro.move @@ -0,0 +1,4 @@ +module a::m { + // cannot have native macro + native macro fun foo(); +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.exp new file mode 100644 index 0000000000000..b8b877c112a42 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.exp @@ -0,0 +1,134 @@ +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:7:24 + │ +6 │ do!(|| { + │ ╭─────────────' +7 │ │ if (false) break; + │ │ ^^^^^ Invalid 'break'. This usage is not yet supported for lambdas or macros +8 │ │ if (false) continue; +9 │ │ }); + │ ╰─────────' Inside this lambda + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:8:24 + │ +6 │ do!(|| { + │ ╭─────────────' +7 │ │ if (false) break; +8 │ │ if (false) continue; + │ │ ^^^^^^^^ Invalid 'continue'. This usage is not yet supported for lambdas or macros +9 │ │ }); + │ ╰─────────' Inside this lambda + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:15:28 + │ +13 │ ╭ loop { +14 │ │ do!(|| { + │ ╭─│─────────────────' +15 │ │ │ if (false) break; + │ │ │ ^^^^^ Invalid 'break'. This usage is not yet supported for lambdas or macros +16 │ │ │ if (false) continue; +17 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +18 │ │ }; + │ ╰─────────' To 'break' to this loop, add a label, e.g. `'label: loop` and `break 'label` + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:16:28 + │ +13 │ ╭ loop { +14 │ │ do!(|| { + │ ╭─│─────────────────' +15 │ │ │ if (false) break; +16 │ │ │ if (false) continue; + │ │ │ ^^^^^^^^ Invalid 'continue'. This usage is not yet supported for lambdas or macros +17 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +18 │ │ }; + │ ╰─────────' To 'continue' to this loop, add a label, e.g. `'label: loop` and `continue 'label` + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:22:28 + │ +20 │ ╭ while (true) { +21 │ │ do!(|| { + │ ╭─│─────────────────' +22 │ │ │ if (false) break; + │ │ │ ^^^^^ Invalid 'break'. This usage is not yet supported for lambdas or macros +23 │ │ │ if (false) continue; +24 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +25 │ │ } + │ ╰─────────' To 'break' to this loop, add a label, e.g. `'label: while` and `break 'label` + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:23:28 + │ +20 │ ╭ while (true) { +21 │ │ do!(|| { + │ ╭─│─────────────────' +22 │ │ │ if (false) break; +23 │ │ │ if (false) continue; + │ │ │ ^^^^^^^^ Invalid 'continue'. This usage is not yet supported for lambdas or macros +24 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +25 │ │ } + │ ╰─────────' To 'continue' to this loop, add a label, e.g. `'label: while` and `continue 'label` + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:31:28 + │ +29 │ ╭ 'a: loop { +30 │ │ do!(|| { + │ ╭─│─────────────────' +31 │ │ │ if (false) break; + │ │ │ ^^^^^ Invalid 'break'. This usage is not yet supported for lambdas or macros +32 │ │ │ if (false) continue; +33 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +34 │ │ }; + │ ╰─────────' To 'break' to this loop, specify the label, e.g. `break 'a` + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:32:28 + │ +29 │ ╭ 'a: loop { +30 │ │ do!(|| { + │ ╭─│─────────────────' +31 │ │ │ if (false) break; +32 │ │ │ if (false) continue; + │ │ │ ^^^^^^^^ Invalid 'continue'. This usage is not yet supported for lambdas or macros +33 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +34 │ │ }; + │ ╰─────────' To 'continue' to this loop, specify the label, e.g. `continue 'a` + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:38:28 + │ +36 │ ╭ 'b: while (true) { +37 │ │ do!(|| { + │ ╭─│─────────────────' +38 │ │ │ if (false) break; + │ │ │ ^^^^^ Invalid 'break'. This usage is not yet supported for lambdas or macros +39 │ │ │ if (false) continue; +40 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +41 │ │ } + │ ╰─────────' To 'break' to this loop, specify the label, e.g. `break 'b` + +error[E04014]: invalid loop control + ┌─ tests/move_2024/naming/lambda_captures_break_and_continue.move:39:28 + │ +36 │ ╭ 'b: while (true) { +37 │ │ do!(|| { + │ ╭─│─────────────────' +38 │ │ │ if (false) break; +39 │ │ │ if (false) continue; + │ │ │ ^^^^^^^^ Invalid 'continue'. This usage is not yet supported for lambdas or macros +40 │ │ │ }); + │ ╰─│─────────────' Inside this lambda +41 │ │ } + │ ╰─────────' To 'continue' to this loop, specify the label, e.g. `continue 'b` + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.move new file mode 100644 index 0000000000000..14f2c35414072 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_captures_break_and_continue.move @@ -0,0 +1,44 @@ +module a::m { + macro fun do<$T>($f: || -> $T): $T { $f() } + + // lambdas capture break/continue + fun t() { + do!(|| { + if (false) break; + if (false) continue; + }); + } + + fun tloop() { + loop { + do!(|| { + if (false) break; + if (false) continue; + }); + }; + + while (true) { + do!(|| { + if (false) break; + if (false) continue; + }); + } + } + + fun tnamedloop() { + 'a: loop { + do!(|| { + if (false) break; + if (false) continue; + }); + }; + + 'b: while (true) { + do!(|| { + if (false) break; + if (false) continue; + }); + } + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.exp new file mode 100644 index 0000000000000..cb8dbd6f526a2 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.exp @@ -0,0 +1,18 @@ +error[E02010]: invalid name + ┌─ tests/move_2024/naming/lambda_shadows_function.move:4:22 + │ +4 │ macro fun do<$T>(f: || -> $T): $T { + │ ----- ^ Invalid parameter name 'f'. 'macro' parameter names must start with '$' (or must be '_') + │ │ + │ Declared 'macro' here + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + +warning[W09002]: unused variable + ┌─ tests/move_2024/naming/lambda_shadows_function.move:4:22 + │ +4 │ macro fun do<$T>(f: || -> $T): $T { + │ ^ Unused parameter 'f'. Consider removing or prefixing with an underscore: '_f' + │ + = This warning can be suppressed with '#[allow(unused_variable)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.move new file mode 100644 index 0000000000000..502d1d75ed2e4 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_shadows_function.move @@ -0,0 +1,7 @@ +module a::m { + fun f() {} + // if we ever add non-$ var calls, we will need to fix this + macro fun do<$T>(f: || -> $T): $T { + f() + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_to_outer_loop.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_to_outer_loop.move new file mode 100644 index 0000000000000..7149495cc0809 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_to_outer_loop.move @@ -0,0 +1,22 @@ +module a::m { + macro fun do($f: || -> ()): () { + $f() + } + + // TODO Fix deadcode bug + // fun t(cond: bool) { + // let _: u64 = 'a: loop { + // do!(|| { + // if (cond) continue 'a; + // break 'a 0 + // }) + // }; + // 'b: while (true) { + // do!(|| { + // if (cond) continue 'b; + // break 'b + // }) + // }; + // } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow.move new file mode 100644 index 0000000000000..16d0232ccb55a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow.move @@ -0,0 +1,11 @@ +module a::m { + macro fun do<$T>($f: || -> $T): $T { $f() } + + // simple test of break/return in a lambda + fun t() { + do!(|| { + if (false) return 0; + 0 + }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow_named.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow_named.move new file mode 100644 index 0000000000000..a73c3829e43bf --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_control_flow_named.move @@ -0,0 +1,39 @@ +module a::m { + macro fun do<$T>($f: || -> $T): $T { $f() } + macro fun do2<$T1, $T2>($f: || -> $T1, $g: || -> $T2): ($T1, $T2) { ($f(), $g()) } + + + // simple test of break/return in a lambda with a named block + fun t() { + do!(|| 'a: { + if (false) return'a 0; + 0 + }); + do!(|| ('a: { + if (false) return'a 0; + 0 + })); + do2!(|| 'a: { + if (false) return'a 0; + 0 + }, + || 'b: { + if (false) return'b 0; + 0 + }); + } + fun nested() { + do!(|| 'outer: { + do2!(|| 'a: { + if (false) return'outer (0, 1); + if (false) return'a 0; + 0 + }, + || 'b: { + if (false) return'outer (0, 1); + if (false) return'b 0; + 0 + }) + }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.exp new file mode 100644 index 0000000000000..6501f0d0b5b7d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.exp @@ -0,0 +1,6 @@ +error[E03007]: too many type arguments + ┌─ tests/move_2024/naming/lambda_with_type_args.move:3:9 + │ +3 │ $f(0) + │ ^^ Invalid lambda call. Expected zero type arguments + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.move new file mode 100644 index 0000000000000..7494bb7f576e4 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/lambda_with_type_args.move @@ -0,0 +1,5 @@ +module a::m { + macro fun do<$T>($f: |$T| -> $T): $T { + $f(0) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp new file mode 100644 index 0000000000000..d436323f04ce6 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp @@ -0,0 +1,28 @@ +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/naming/macro_identifier_assignment.move:3:9 + │ +3 │ $f = 0; + │ ^^ Cannot assign to argument for parameter '$f'. Arguments must be used in value positions + │ + = 'macro' parameters are substituted without being evaluated. There is no local variable to assign to + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/naming/macro_identifier_assignment.move:4:9 + │ +4 │ $x = 0; + │ ^^ Cannot assign to argument for parameter '$x'. Arguments must be used in value positions + │ + = 'macro' parameters are substituted without being evaluated. There is no local variable to assign to + +error[E04007]: incompatible types + ┌─ tests/move_2024/naming/macro_identifier_assignment.move:10:19 + │ + 2 │ macro fun call($f: |u64| -> u64, $x: u64): u64 { + │ --- Expected: 'u64' + · +10 │ call!(|_| false, 0) + 1; + │ ^^^^^ + │ │ + │ Invalid type annotation + │ Given: 'bool' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.move new file mode 100644 index 0000000000000..020aa06ea68d2 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.move @@ -0,0 +1,12 @@ +module a::m { + macro fun call($f: |u64| -> u64, $x: u64): u64 { + $f = 0; + $x = 0; + $f($x) + } + + fun t() { + // ensure the macro is expanded + call!(|_| false, 0) + 1; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.exp new file mode 100644 index 0000000000000..d021c94c93432 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.exp @@ -0,0 +1,20 @@ +error[E02010]: invalid name + ┌─ tests/move_2024/naming/macro_parameter_assignment.move:2:20 + │ +2 │ macro fun call(f: |u64| -> u64, x: u64): u64 { + │ ----- ^ Invalid parameter name 'f'. 'macro' parameter names must start with '$' (or must be '_') + │ │ + │ Declared 'macro' here + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + +error[E02010]: invalid name + ┌─ tests/move_2024/naming/macro_parameter_assignment.move:2:37 + │ +2 │ macro fun call(f: |u64| -> u64, x: u64): u64 { + │ ----- ^ Invalid parameter name 'x'. 'macro' parameter names must start with '$' (or must be '_') + │ │ + │ Declared 'macro' here + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.move new file mode 100644 index 0000000000000..eb06f385b41e7 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/macro_parameter_assignment.move @@ -0,0 +1,12 @@ +module a::m { + macro fun call(f: |u64| -> u64, x: u64): u64 { + f = abort 0; + x = 0; + f(x) + } + + fun t() { + // ensure the macro is expanded + call!(|_| 1, 0) + 1; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.exp new file mode 100644 index 0000000000000..566e6ea8fec25 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.exp @@ -0,0 +1,18 @@ +error[E03016]: invalid 'mut' declaration + ┌─ tests/move_2024/naming/mut_underscore.move:3:13 + │ +3 │ fun foo(mut _: u64) { + │ ^^^ Invalid 'mut' declaration. 'mut' is applied to variables and cannot be applied to the '_' pattern + +error[E03016]: invalid 'mut' declaration + ┌─ tests/move_2024/naming/mut_underscore.move:4:13 + │ +4 │ let mut _ = 0; + │ ^^^ Invalid 'mut' declaration. 'mut' is applied to variables and cannot be applied to the '_' pattern + +error[E03016]: invalid 'mut' declaration + ┌─ tests/move_2024/naming/mut_underscore.move:5:17 + │ +5 │ callf!(|mut _: u64| ()); + │ ^^^ Invalid 'mut' declaration. 'mut' is applied to variables and cannot be applied to the '_' pattern + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.move new file mode 100644 index 0000000000000..fe4cf2f70dcb8 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/mut_underscore.move @@ -0,0 +1,13 @@ +module a::m { + // meaningless to have mut _ + fun foo(mut _: u64) { + let mut _ = 0; + callf!(|mut _: u64| ()); + } + + macro fun callf($f: |u64|) { + $f(0) + } + + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.exp index ce3fcbe6d2300..987491aee3729 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.exp @@ -4,7 +4,7 @@ error[E03014]: invalid use of label 4 │ if (cond) { break 'name 10 }; │ ^^^^^ Invalid usage of 'break' with a named block label │ - = Loop labels may only be used with 'break' and 'continue', not 'return' + = Named block labels may only be used with 'return', not 'break' or 'continue'. error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:5:34 @@ -12,7 +12,7 @@ error[E03014]: invalid use of label 5 │ if (cond) { continue 'name }; │ ^^^^^ Invalid usage of 'continue' with a named block label │ - = Loop labels may only be used with 'break' and 'continue', not 'return' + = Named block labels may only be used with 'return', not 'break' or 'continue'. error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:12:32 @@ -20,7 +20,7 @@ error[E03014]: invalid use of label 12 │ if (cond) { return 'name 10 }; │ ^^^^^ Invalid usage of 'return' with a loop block label │ - = Named block labels may only be used with 'return', not 'break' or 'continue'. + = Loop labels may only be used with 'break' and 'continue', not 'return' error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:19:36 @@ -28,7 +28,7 @@ error[E03014]: invalid use of label 19 │ if (cond) { return 'outer 10 }; │ ^^^^^^ Invalid usage of 'return' with a loop block label │ - = Named block labels may only be used with 'return', not 'break' or 'continue'. + = Loop labels may only be used with 'break' and 'continue', not 'return' error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:20:36 @@ -36,7 +36,7 @@ error[E03014]: invalid use of label 20 │ if (cond) { return 'inner 20 }; │ ^^^^^^ Invalid usage of 'return' with a loop block label │ - = Named block labels may only be used with 'return', not 'break' or 'continue'. + = Loop labels may only be used with 'break' and 'continue', not 'return' error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:28:36 @@ -44,7 +44,7 @@ error[E03014]: invalid use of label 28 │ if (cond) { return 'outer }; │ ^^^^^^ Invalid usage of 'return' with a loop block label │ - = Named block labels may only be used with 'return', not 'break' or 'continue'. + = Loop labels may only be used with 'break' and 'continue', not 'return' error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:29:36 @@ -52,7 +52,7 @@ error[E03014]: invalid use of label 29 │ if (cond) { return 'inner }; │ ^^^^^^ Invalid usage of 'return' with a loop block label │ - = Named block labels may only be used with 'return', not 'break' or 'continue'. + = Loop labels may only be used with 'break' and 'continue', not 'return' error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:37:36 @@ -60,7 +60,7 @@ error[E03014]: invalid use of label 37 │ if (cond) { return 'outer }; │ ^^^^^^ Invalid usage of 'return' with a loop block label │ - = Named block labels may only be used with 'return', not 'break' or 'continue'. + = Loop labels may only be used with 'break' and 'continue', not 'return' error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:38:35 @@ -68,7 +68,7 @@ error[E03014]: invalid use of label 38 │ if (cond) { break 'inner 10 }; │ ^^^^^^ Invalid usage of 'break' with a named block label │ - = Loop labels may only be used with 'break' and 'continue', not 'return' + = Named block labels may only be used with 'return', not 'break' or 'continue'. error[E03014]: invalid use of label ┌─ tests/move_2024/naming/named_blocks_invalid.move:47:24 @@ -76,51 +76,51 @@ error[E03014]: invalid use of label 47 │ return 'l │ ^^ Invalid usage of 'return' with a loop block label │ - = Named block labels may only be used with 'return', not 'break' or 'continue'. + = Loop labels may only be used with 'break' and 'continue', not 'return' -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:54:32 │ 54 │ if (cond) { return 'name2 10 }; │ ^^^^^^ Invalid return. Unbound label 'name2 -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:61:34 │ 61 │ if (cond) { continue 'name2 }; │ ^^^^^^ Invalid continue. Unbound label 'name2 -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:62:31 │ 62 │ if (cond) { break 'name2 10 }; │ ^^^^^^ Invalid break. Unbound label 'name2 -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:69:35 │ 69 │ if (cond) { break 'outer 10 }; │ ^^^^^^ Invalid break. Unbound label 'outer -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:70:35 │ 70 │ if (cond) { break 'inner 20 }; │ ^^^^^^ Invalid break. Unbound label 'inner -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:78:38 │ 78 │ if (cond) { continue 'outer2 }; │ ^^^^^^^ Invalid continue. Unbound label 'outer2 -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:79:35 │ 79 │ if (cond) { break 'inner2 }; │ ^^^^^^^ Invalid break. Unbound label 'inner2 -error[E03009]: unbound variable +error[E03015]: unbound label ┌─ tests/move_2024/naming/named_blocks_invalid.move:87:23 │ 87 │ break 'l2 diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.move index 12c758b07dc13..b4405222c9635 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.move +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/named_blocks_invalid.move @@ -8,14 +8,14 @@ module 0x42::m { } fun t1(cond: bool): u64 { - loop 'name: { + 'name: loop { if (cond) { return 'name 10 }; } } fun t2(cond: bool): u64 { - loop 'outer: { - loop 'inner: { + 'outer: loop { + 'inner: loop { if (cond) { return 'outer 10 }; if (cond) { return 'inner 20 }; }; @@ -23,8 +23,8 @@ module 0x42::m { } fun t3(cond: bool) { - while (cond) 'outer: { - while (cond) 'inner: { + 'outer: while (cond) { + 'inner: while (cond) { if (cond) { return 'outer }; if (cond) { return 'inner }; } @@ -32,7 +32,7 @@ module 0x42::m { } fun t4(cond: bool) { - while (cond) 'outer: { + 'outer: while (cond) { let _x = 'inner: { if (cond) { return 'outer }; if (cond) { break 'inner 10 }; @@ -42,8 +42,8 @@ module 0x42::m { } fun t5() { - loop 'l: { - loop 'l: { + 'l: loop { + 'l: loop { return 'l } } @@ -57,15 +57,15 @@ module 0x42::m { } fun t7(cond: bool): u64 { - loop 'name: { + 'name: loop { if (cond) { continue 'name2 }; if (cond) { break 'name2 10 }; } } fun t8(cond: bool): u64 { - loop 'outer2: { - loop 'inner2: { + 'outer2: loop { + 'inner2: loop { if (cond) { break 'outer 10 }; if (cond) { break 'inner 20 }; }; @@ -73,8 +73,8 @@ module 0x42::m { } fun t9(cond: bool) { - while (cond) 'outer: { - while (cond) 'inner: { + 'outer: while (cond) { + 'inner: while (cond) { if (cond) { continue 'outer2 }; if (cond) { break 'inner2 }; } @@ -82,8 +82,8 @@ module 0x42::m { } fun t10() { - loop 'l: { - loop 'l: { + 'l: loop { + 'l: loop { break 'l2 } } diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.exp new file mode 100644 index 0000000000000..2d728b0368ef7 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.exp @@ -0,0 +1,6 @@ +error[E03005]: unbound unscoped name + ┌─ tests/move_2024/naming/unbound_var_call.move:6:9 + │ +6 │ fo(); + │ ^^ Unbound function 'fo' in current scope + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.move new file mode 100644 index 0000000000000..74cb670bf4088 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unbound_var_call.move @@ -0,0 +1,9 @@ +module a::m { + fun foo() {} + + fun unbound(fooo: u64) { + // unbound function/variable fo + fo(); + fooo; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.exp new file mode 100644 index 0000000000000..8938db380bc0d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.exp @@ -0,0 +1,16 @@ +warning[W09002]: unused variable + ┌─ tests/move_2024/naming/unused_by_value_arg.move:4:19 + │ +4 │ macro fun foo($x: X) {} + │ ^^ Unused parameter '$x'. Consider removing or prefixing with an underscore: '_$x' + │ + = This warning can be suppressed with '#[allow(unused_variable)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09003]: unused assignment + ┌─ tests/move_2024/naming/unused_by_value_arg.move:4:19 + │ +4 │ macro fun foo($x: X) {} + │ ^^ Unused assignment for variable '$x'. Consider removing, replacing with '_', or prefixing with '_' (e.g., '_$x') + │ + = This warning can be suppressed with '#[allow(unused_assignment)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.move new file mode 100644 index 0000000000000..1b6eb629e3512 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_by_value_arg.move @@ -0,0 +1,10 @@ +module a::m { + public struct X() has copy, drop; + + macro fun foo($x: X) {} + + fun t() { + X().foo!() + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.exp new file mode 100644 index 0000000000000..38c3dd4efc03d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.exp @@ -0,0 +1,32 @@ +warning[W09003]: unused assignment + ┌─ tests/move_2024/naming/unused_lambda_arg.move:12:13 + │ +12 │ p!(|p| 0); + │ ^ Unused assignment for variable 'p'. Consider removing, replacing with '_', or prefixing with '_' (e.g., '_p') + │ + = This warning can be suppressed with '#[allow(unused_assignment)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09003]: unused assignment + ┌─ tests/move_2024/naming/unused_lambda_arg.move:13:24 + │ +13 │ p!(|Point { x, y }| x); + │ ^ Unused assignment for variable 'y'. Consider removing, replacing with '_', or prefixing with '_' (e.g., '_y') + │ + = This warning can be suppressed with '#[allow(unused_assignment)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09003]: unused assignment + ┌─ tests/move_2024/naming/unused_lambda_arg.move:14:13 + │ +14 │ r!(|p| 0); + │ ^ Unused assignment for variable 'p'. Consider removing, replacing with '_', or prefixing with '_' (e.g., '_p') + │ + = This warning can be suppressed with '#[allow(unused_assignment)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09003]: unused assignment + ┌─ tests/move_2024/naming/unused_lambda_arg.move:15:24 + │ +15 │ r!(|Point { x, y }| *x); + │ ^ Unused assignment for variable 'y'. Consider removing, replacing with '_', or prefixing with '_' (e.g., '_y') + │ + = This warning can be suppressed with '#[allow(unused_assignment)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.move new file mode 100644 index 0000000000000..e9c023c901ba5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/unused_lambda_arg.move @@ -0,0 +1,17 @@ +module a::m { + public struct Point { x: u64, y: u64 } has copy, drop; + macro fun p($f: |Point| -> u64): u64 { + $f(Point { x: 1, y: 2 }) + } + + macro fun r($f: |&Point| -> u64): u64 { + $f(&Point { x: 1, y: 2 }) + } + + fun t() { + p!(|p| 0); + p!(|Point { x, y }| x); + r!(|p| 0); + r!(|Point { x, y }| *x); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.exp new file mode 100644 index 0000000000000..baeecb403df77 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.exp @@ -0,0 +1,28 @@ +error[E03005]: unbound unscoped name + ┌─ tests/move_2024/naming/use_fun_local.move:3:17 + │ +3 │ use fun f as u64.f; + │ ^ Unbound function 'f' in current scope + +error[E02010]: invalid name + ┌─ tests/move_2024/naming/use_fun_local.move:7:19 + │ +7 │ macro fun bar(f: u64) { + │ ----- ^ Invalid parameter name 'f'. 'macro' parameter names must start with '$' (or must be '_') + │ │ + │ Declared 'macro' here + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + +error[E03005]: unbound unscoped name + ┌─ tests/move_2024/naming/use_fun_local.move:8:17 + │ +8 │ use fun f as u64.f; + │ ^ Unbound function 'f' in current scope + +error[E03005]: unbound unscoped name + ┌─ tests/move_2024/naming/use_fun_local.move:13:17 + │ +13 │ use fun $f as u64.f; + │ ^^ Unbound function '$f' in current scope + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.move new file mode 100644 index 0000000000000..38e81a04ca52f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local.move @@ -0,0 +1,16 @@ +module a::m { + fun foo(f: u64) { + use fun f as u64.f; + f; + } + + macro fun bar(f: u64) { + use fun f as u64.f; + f; + } + + macro fun baz($f: u64) { + use fun $f as u64.f; + $f; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local_doesnt_shadow.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local_doesnt_shadow.move new file mode 100644 index 0000000000000..74ff55427a893 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_local_doesnt_shadow.move @@ -0,0 +1,12 @@ +module a::m { + fun id(x: u64): u64 { x } + + macro fun apply($id: |u8| -> u8) { + use fun id as u64.id; // not shadowed by the local + $id((0u64.id() as u8)); + } + + fun t() { + apply!(|x| x); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.exp b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.exp new file mode 100644 index 0000000000000..13d97b12e47bb --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.exp @@ -0,0 +1,18 @@ +error[E03005]: unbound unscoped name + ┌─ tests/move_2024/naming/use_fun_unbound.move:2:13 + │ +2 │ use fun id as u64.id; + │ ^^ Unbound function 'id' in current scope + +error[E03005]: unbound unscoped name + ┌─ tests/move_2024/naming/use_fun_unbound.move:5:17 + │ +5 │ use fun x as u64.x; + │ ^ Unbound function 'x' in current scope + +error[E03005]: unbound unscoped name + ┌─ tests/move_2024/naming/use_fun_unbound.move:10:17 + │ +10 │ use fun $f as u64.f; + │ ^^ Unbound function '$f' in current scope + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.move b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.move new file mode 100644 index 0000000000000..04babb0d38e0b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/naming/use_fun_unbound.move @@ -0,0 +1,13 @@ +module a::m { + use fun id as u64.id; + + fun t(x: u64): u64 { + use fun x as u64.x; + x + } + + macro fun apply($f: |u64| -> ()) { + use fun $f as u64.f; + $f(0); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.exp new file mode 100644 index 0000000000000..da324d1cca748 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.exp @@ -0,0 +1,8 @@ +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_2024/parser/duplicate_macro_modifier.move:2:11 + │ +2 │ macro macro fun foo() {} + │ ----- ^^^^^ Duplicate 'macro' modifier + │ │ + │ 'macro' modifier previously given here + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.move new file mode 100644 index 0000000000000..b205d41af59e9 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/duplicate_macro_modifier.move @@ -0,0 +1,3 @@ +module a::m { + macro macro fun foo() {} +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.exp new file mode 100644 index 0000000000000..f9651c0efd561 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.exp @@ -0,0 +1,9 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/global_access_value_invalid_macro_ident.move:3:9 + │ +3 │ ::$a; + │ ^^ + │ │ + │ Unexpected '::' + │ Expected An identifier after '::' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.move new file mode 100644 index 0000000000000..dfe377dae7946 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/global_access_value_invalid_macro_ident.move @@ -0,0 +1,5 @@ +module a::m { + fun foo() { + ::$a; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.exp new file mode 100644 index 0000000000000..e9207afbe4596 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.exp @@ -0,0 +1,9 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/invalid_macro_bang.move:3:30 + │ +3 │ fun bar(): u64 { foo!(42) } + │ ^ + │ │ + │ Unexpected '!' + │ Expected ';' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.move new file mode 100644 index 0000000000000..8ba9dc9d65140 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang.move @@ -0,0 +1,4 @@ +module a::m { + macro fun foo<$T>($x: $T): $T { $x } + fun bar(): u64 { foo!(42) } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.exp new file mode 100644 index 0000000000000..12c98786c3b81 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.exp @@ -0,0 +1,9 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/invalid_macro_bang_no_args.move:3:21 + │ +3 │ fun bar() { foo!; } + │ ^ + │ │ + │ Unexpected ';' + │ Expected '(' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.move new file mode 100644 index 0000000000000..5655c8117111d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_bang_no_args.move @@ -0,0 +1,4 @@ +module a::m { + macro fun foo<$T>($x: $T): $T { $x } + fun bar() { foo!; } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.exp new file mode 100644 index 0000000000000..5d2e3c90284ed --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.exp @@ -0,0 +1,27 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/invalid_macro_identifier_usage.move:2:9 + │ +2 │ fun $foo() { let $x = 0; } + │ ^^^^ + │ │ + │ Unexpected '$foo' + │ Expected an identifier + +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/invalid_macro_identifier_usage.move:3:19 + │ +3 │ public struct $X() + │ ^^ + │ │ + │ Unexpected '$X' + │ Expected an identifier + +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/invalid_macro_identifier_usage.move:4:11 + │ +4 │ const $C: u64 = 0; + │ ^^ + │ │ + │ Unexpected '$C' + │ Expected an identifier + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.move new file mode 100644 index 0000000000000..d7217b65d6054 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_identifier_usage.move @@ -0,0 +1,5 @@ +module a::m { + fun $foo() { let $x = 0; } + public struct $X() + const $C: u64 = 0; +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.exp new file mode 100644 index 0000000000000..8fe31a27633ce --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.exp @@ -0,0 +1,40 @@ +error[E01003]: invalid modifier + ┌─ tests/move_2024/parser/invalid_macro_modifier.move:2:5 + │ +2 │ macro const X: u64 = 0; + │ ^^^^^ Invalid constant declaration. 'macro' is used only on functions + +error[E01003]: invalid modifier + ┌─ tests/move_2024/parser/invalid_macro_modifier.move:3:5 + │ +3 │ macro public struct S() + │ ^^^^^ Invalid struct declaration. 'macro' is used only on functions + +error[E01003]: invalid modifier + ┌─ tests/move_2024/parser/invalid_macro_modifier.move:4:5 + │ +4 │ macro use a::m as n; + │ ^^^^^ Invalid use declaration. 'macro' is used only on functions + +warning[W09001]: unused alias + ┌─ tests/move_2024/parser/invalid_macro_modifier.move:4:23 + │ +4 │ macro use a::m as n; + │ ^ Unused 'use' of alias 'n'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +error[E01003]: invalid modifier + ┌─ tests/move_2024/parser/invalid_macro_modifier.move:5:5 + │ +5 │ macro use fun foo as S.bar; + │ ^^^^^ Invalid use declaration. 'macro' is used only on functions + +warning[W09001]: unused alias + ┌─ tests/move_2024/parser/invalid_macro_modifier.move:5:5 + │ +5 │ macro use fun foo as S.bar; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused 'use fun' of 'a::m::S.bar'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.move new file mode 100644 index 0000000000000..e4b854c551c0d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/invalid_macro_modifier.move @@ -0,0 +1,7 @@ +module a::m { + macro const X: u64 = 0; + macro public struct S() + macro use a::m as n; + macro use fun foo as S.bar; + fun foo(s: S): S { s } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type.move new file mode 100644 index 0000000000000..713812c4fde3f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type.move @@ -0,0 +1,12 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun t() { + call!(|| -> () { }); + call!(|| -> () { () }); + call!(|| -> u64 { 0 }); + call!(|| -> (u64, u8) { (0, 0) }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.exp new file mode 100644 index 0000000000000..7877bf6f927c6 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.exp @@ -0,0 +1,9 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/lambda_expression_return_type_invalid.move:7:25 + │ +7 │ call!(|| -> u64 0); + │ ^ + │ │ + │ Unexpected '0' + │ Expected '{' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.move new file mode 100644 index 0000000000000..9f8635f56d45b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_invalid.move @@ -0,0 +1,9 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun t() { + call!(|| -> u64 0); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.exp new file mode 100644 index 0000000000000..14436ffc9d3e5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.exp @@ -0,0 +1,9 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/lambda_expression_return_type_named_block.move:7:25 + │ +7 │ call!(|| -> u64 'a: { 0 }); + │ ^^ + │ │ + │ Unexpected ''a' + │ Expected '{' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.move new file mode 100644 index 0000000000000..1b344531a3f1b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_expression_return_type_named_block.move @@ -0,0 +1,9 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun t() { + call!(|| -> u64 'a: { 0 }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue.move new file mode 100644 index 0000000000000..4fda1b6a83a25 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue.move @@ -0,0 +1,30 @@ +module a::m { + public struct Point(u64, u64) has copy, drop, store; + public struct FPoint { x: u64, y: u64 } has copy, drop, store; + + macro fun foo( + $x: &u64, + $p: Point, + $s: &mut FPoint, + $f: |u64, (&u64, Point, &mut FPoint)| + ) { + $f(0, ($x, $p, $s)) + } + + fun t() { + let p = Point(1, 2); + foo!( + &0, + p, + &mut FPoint { x: 3, y: 4 }, + |_, (_x, Point(mut xp, mut yp), FPoint { x, y })| { + assert!(xp == 1, 0); + assert!(yp == 2, 0); + xp = *x; + yp = *y; + *x = yp; + *y = xp; + } + ) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue_with_types.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue_with_types.move new file mode 100644 index 0000000000000..710b852f26c37 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/lambda_lvalue_with_types.move @@ -0,0 +1,34 @@ +module a::m { + public struct X() has copy, drop, store; + + macro fun foo( + $x: &u64, + $p: X, + $s: &mut X, + $f: |u64, (&u64, X, &mut X)| + ) { + $f(0, ($x, $p, $s)) + } + + fun t() { + let x1 = X(); + foo!( + &0, + x1, + &mut X(), + |_: u64, (_, X(), _x): (&u64, X, &mut X)| () + ); + foo!( + &0, + x1, + &mut X(), + |_, (_, X(), _x): (&u64, X, &mut X)| () + ); + foo!( + &0, + x1, + &mut X(), + |_: u64, (_, X(), _x)| () + ) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.exp new file mode 100644 index 0000000000000..c58ac0acb608d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.exp @@ -0,0 +1,6 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/macro_identifier_invalid.move:2:19 + │ +2 │ macro fun foo($0: u64) { + │ ^ Expected an identifier following '$', e.g. '$x' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.move new file mode 100644 index 0000000000000..5e84f4e8e46da --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid.move @@ -0,0 +1,4 @@ +module a::m { + macro fun foo($0: u64) { + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.exp b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.exp new file mode 100644 index 0000000000000..719db31dfc1f3 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.exp @@ -0,0 +1,6 @@ +error[E01002]: unexpected token + ┌─ tests/move_2024/parser/macro_identifier_invalid_no_following_characters.move:2:19 + │ +2 │ macro fun foo($ + │ ^ Expected an identifier following '$', e.g. '$x' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.move new file mode 100644 index 0000000000000..9b4900b3a03bc --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/macro_identifier_invalid_no_following_characters.move @@ -0,0 +1,2 @@ +module a::m { + macro fun foo($ diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/named_blocks.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/named_blocks.move index f1d37bdeb9424..3c04ccddd94f7 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/parser/named_blocks.move +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/named_blocks.move @@ -8,14 +8,17 @@ module 0x42::m { } fun t1(cond: bool): u64 { - loop 'name: { + 'name: loop { if (cond) { break 'name 10 }; } } + fun t1_inline(cond: bool): u64 { + 'name: loop if (cond) { break 'name 10 } + } fun t2(cond: bool): u64 { - loop 'outer: { - loop 'inner: { + 'outer: loop { + 'inner: loop { if (cond) { break 'outer 10 }; if (cond) { break 'inner 20 }; }; @@ -23,16 +26,23 @@ module 0x42::m { } fun t3(cond: bool) { - while (cond) 'outer: { - while (cond) 'inner: { + 'outer: while (cond) { + 'inner: while (cond) { if (cond) { break 'outer }; if (cond) { break 'inner }; } } } + fun t3_inline(cond: bool) { + 'outer: while (cond) + 'inner: while (cond) { + if (cond) { break 'outer }; + if (cond) { break 'inner }; + } + } fun t4(cond: bool) { - while (cond) 'outer: { + 'outer: while (cond) { let _x = 'inner: { if (cond) { break 'outer }; if (cond) { return 'inner 10 }; @@ -42,8 +52,8 @@ module 0x42::m { } fun t5() { - loop 'l: { - loop 'l: { + 'l: loop { + 'l: loop { break 'l } } diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/parser/valid_fun_types.move b/external-crates/move/crates/move-compiler/tests/move_2024/parser/valid_fun_types.move new file mode 100644 index 0000000000000..c4621594bfaea --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/parser/valid_fun_types.move @@ -0,0 +1,27 @@ +module a::m { + public struct Cup has drop {} + public macro fun foo( + _: ||, + _: || -> (), + _: || -> u64, + _: || -> (u64), + _: || -> (u64, bool), + _: |&u64|, + _: |&u64| -> (), + _: |&u64| -> u64, + _: |&u64| -> (u64), + _: |&u64| -> (u64, bool), + _: |bool, address|, + _: |bool, address| -> (), + _: |bool, address| -> u64, + _: |bool, address| -> (u64), + _: |bool, address| -> (u64, bool), + _: |bool, address| -> (u64, bool, &u64), + _: || -> || -> ||, + _: || -> || -> || -> || -> (), + _: || -> | | -> || -> | | -> u64, + _: | | -> || -> | | -> || -> (u64), + _: Cup<||>, + _: Cup<|| -> u64>, + ) {} +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.exp new file mode 100644 index 0000000000000..7f1005dd84391 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.exp @@ -0,0 +1,6 @@ +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/annotated_lambda.move:8:15 + │ +8 │ call!((|| 1 : || -> u64)) + │ ^^^^^^^^^^^^^^^^^^ Unable to bind lambda to parameter '$f'. The lambda must be passed directly + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.move new file mode 100644 index 0000000000000..4d44c03991216 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/annotated_lambda.move @@ -0,0 +1,10 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun t() { + // cannot annotate a lambda this way + call!((|| 1 : || -> u64)) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.exp new file mode 100644 index 0000000000000..bd0ecf0881f46 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.exp @@ -0,0 +1,36 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/break_in_macro_arg_invalid.move:8:14 + │ + 8 │ foo!('a: { + │ ╭──────────────^ + 9 │ │ if (cond) return'a vector[]; + │ │ -------- Found: 'vector<_>'. It is not compatible with the other type. +10 │ │ 0 + │ │ - Found: integer. It is not compatible with the other type. +11 │ │ }); + │ ╰─────────^ Invalid named block + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/break_in_macro_arg_invalid.move:9:32 + │ +9 │ if (cond) return'a vector[]; + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/break_in_macro_arg_invalid.move:12:14 + │ +12 │ foo!('a: { + │ ╭──────────────^ +13 │ │ if (cond) return'a 0; + │ │ ---------- Found: integer. It is not compatible with the other type. +14 │ │ vector[] + │ │ -------- Found: 'vector<_>'. It is not compatible with the other type. +15 │ │ }); + │ ╰─────────^ Invalid named block + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/break_in_macro_arg_invalid.move:14:13 + │ +14 │ vector[] + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.move new file mode 100644 index 0000000000000..d6ce462ac8329 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/break_in_macro_arg_invalid.move @@ -0,0 +1,17 @@ +module a::m { + macro fun foo($x: u64): u64 { + $x + $x + } + + fun t(cond: bool) { + // mostly making sure the error doesn't say this is a lambda + foo!('a: { + if (cond) return'a vector[]; + 0 + }); + foo!('a: { + if (cond) return'a 0; + vector[] + }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.exp new file mode 100644 index 0000000000000..3c504686e8a41 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.exp @@ -0,0 +1,27 @@ +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/call_on_non_lambda_arg.move:4:9 + │ + 4 │ $x(0); + │ ^^^^^ Cannot call non-lambda argument + · +11 │ foo!(|x| x, 0); + │ - Expected a lambda argument + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/call_on_non_lambda_arg.move:9:9 + │ +2 │ macro fun foo($f: |u64| -> u64, $x: u64) { + │ --- Expected: 'u64' + · +9 │ foo!(0, |x| x); + │ ^^^^^^^^^^^^^^ + │ │ │ + │ │ Given: '|_| -> _' + │ Invalid call of 'a::m::foo'. Invalid argument for parameter '$x' + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/call_on_non_lambda_arg.move:9:14 + │ +9 │ foo!(0, |x| x); + │ ^ Unable to bind lambda to parameter '$f'. The lambda must be passed directly + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.move new file mode 100644 index 0000000000000..a9b9618723f0b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_on_non_lambda_arg.move @@ -0,0 +1,13 @@ +module a::m { + macro fun foo($f: |u64| -> u64, $x: u64) { + $f(0); + $x(0); + } + + fun t() { + // mismatch on parameters that are actually called + foo!(0, |x| x); + // call a non lambda + foo!(|x| x, 0); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.exp new file mode 100644 index 0000000000000..8074b473f430c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.exp @@ -0,0 +1,36 @@ +error[E04029]: invalid function call + ┌─ tests/move_2024/typing/call_target_mismatch.move:9:20 + │ +4 │ fun not_a_macro(_: X) { + │ ----------- Normal (non-'macro') function is declared here + · +9 │ not_a_macro!(X()); + │ ^ 'not_a_macro' is not a macro function and cannot be called with a `!`. Try replacing with 'not_a_macro' + +error[E04029]: invalid function call + ┌─ tests/move_2024/typing/call_target_mismatch.move:10:9 + │ + 6 │ macro fun a_macro(_: X) {} + │ ----- 'macro' function is declared here + · +10 │ a_macro(X()); + │ ^^^^^^^^^^^^ 'a_macro' is a macro function and must be called with a `!`. Try replacing with 'a_macro!' + +error[E04029]: invalid function call + ┌─ tests/move_2024/typing/call_target_mismatch.move:11:24 + │ + 4 │ fun not_a_macro(_: X) { + │ ----------- Normal (non-'macro') function is declared here + · +11 │ X().not_a_macro!(); + │ ^ 'not_a_macro' is not a macro function and cannot be called with a `!`. Try replacing with 'not_a_macro' + +error[E04029]: invalid function call + ┌─ tests/move_2024/typing/call_target_mismatch.move:12:9 + │ + 6 │ macro fun a_macro(_: X) {} + │ ----- 'macro' function is declared here + · +12 │ X().a_macro(); + │ ^^^^^^^^^^^^^ 'a_macro' is a macro function and must be called with a `!`. Try replacing with 'a_macro!' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.move new file mode 100644 index 0000000000000..21e564f63f47f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/call_target_mismatch.move @@ -0,0 +1,14 @@ +// testing macros called without ! and vice versa +module a::m { + public struct X() + fun not_a_macro(_: X) { + } + macro fun a_macro(_: X) {} + + fun t() { + not_a_macro!(X()); + a_macro(X()); + X().not_a_macro!(); + X().a_macro(); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_private.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_private.exp index 7026e89dad3a9..f3f11bdabfe65 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_private.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_private.exp @@ -5,7 +5,7 @@ error[E04001]: restricted visibility │ - This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module · 18 │ x.f(); - │ ^^^^^ Invalid call to '0x42::t::f' + │ ^^^^^ Invalid call to internal function '0x42::t::f' error[E04001]: restricted visibility ┌─ tests/move_2024/typing/dot_call_private.move:19:5 @@ -14,5 +14,5 @@ error[E04001]: restricted visibility │ - This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module · 19 │ y.x.f(); - │ ^^^^^^^ Invalid call to '0x42::t::f' + │ ^^^^^^^ Invalid call to internal function '0x42::t::f' diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type.move new file mode 100644 index 0000000000000..27f4d00042ee7 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type.move @@ -0,0 +1,17 @@ +module a::m { + public struct X() has copy, drop; + + fun foo(_: X) {} + + fun any(): T { abort 0} + + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun t() { + // we must persist the return type, otherwise we will not know which foo to call + call!(|| -> X { any() }).foo(); + call!(|| -> &u64 { &mut 0 }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp new file mode 100644 index 0000000000000..465219567c1c3 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp @@ -0,0 +1,18 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_annotated_return_type_invalid.move:13:21 + │ +13 │ call!(|| -> X { 0 }).foo(); + │ ^ - Given: integer + │ │ + │ Invalid type annotation + │ Expected: 'a::m::X' + +error[E04006]: invalid subtype + ┌─ tests/move_2024/typing/lambda_annotated_return_type_invalid.move:14:21 + │ +14 │ call!(|| -> &mut u64 { &0 }); + │ ^^^^^^^^ -- Given: '&{integer}' + │ │ + │ Invalid type annotation + │ Expected: '&mut u64' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.move new file mode 100644 index 0000000000000..8cd14fd05abd9 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.move @@ -0,0 +1,16 @@ +module a::m { + public struct X() has copy, drop; + + fun foo(_: X) {} + + fun any(): T { abort 0} + + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun t() { + call!(|| -> X { 0 }).foo(); + call!(|| -> &mut u64 { &0 }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return.move new file mode 100644 index 0000000000000..c1330cb4604e1 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return.move @@ -0,0 +1,40 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + #[allow(dead_code)] + fun simple() { + call!(|| return 0); + call!<&u64>(|| return &0); + call!<(&u64, u8)>(|| return (&0, 1)); + } + + fun conditional(cond: bool) { + call!(|| { if (cond) return 0; 1 }); + call!(|| { if (cond) 1 else return 0 }); + call!<&u64>(|| { if (cond) return &0; &1 }); + call!<&u64>(|| { if (cond) &1 else return &0 }); + call!<(vector, bool)>(|| { if (cond) return (vector[], false); (vector[0], true) }); + call!<(vector, bool)>(|| { if (cond) (vector[], false) else return (vector[0], true) }); + } + + #[allow(dead_code)] + fun commands(cond: bool) { + call!(|| { + if (cond) return false; + if (cond) return false; + return true + }); + call!(|| { + if (cond) return &0; + if (cond) return &mut 0; + return &0 + }); + call!(|| { + if (cond) return (&0, vector[0]); + if (cond) return (&mut 0, vector[0, 1]); + return (&0, vector[]) + }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp new file mode 100644 index 0000000000000..671a0e72d396d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp @@ -0,0 +1,72 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:7:18 + │ +7 │ call!(|| { if (cond) return 0; &1 }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Found: '&{integer}'. It is not compatible with the other type. + │ │ Found: integer. It is not compatible with the other type. + │ Invalid lambda return + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:8:18 + │ +8 │ call!(|| { if (cond) 1 else return &0 }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Found: '&{integer}'. It is not compatible with the other type. + │ Invalid lambda return + │ Found: integer. It is not compatible with the other type. + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:9:18 + │ +9 │ call!(|| { if (cond) return 0; &1 }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Found: '&{integer}'. It is not compatible with the other type. + │ │ Found: integer. It is not compatible with the other type. + │ Invalid lambda return + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:10:18 + │ +10 │ call!(|| { if (cond) 1 else return &0 }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Found: '&{integer}'. It is not compatible with the other type. + │ Invalid lambda return + │ Found: integer. It is not compatible with the other type. + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:11:18 + │ +11 │ call!(|| { if (cond) return (vector[], 0, false); (vector[0], true) }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Found expression list of length 2: '(vector<{integer}>, bool)'. It is not compatible with the other type of length 3. + │ │ Found expression list of length 3: '(vector<_>, {integer}, bool)'. It is not compatible with the other type of length 2. + │ Invalid lambda return + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:11:38 + │ +11 │ call!(|| { if (cond) return (vector[], 0, false); (vector[0], true) }); + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:12:18 + │ +12 │ call!(|| { if (cond) (vector[], 0, false) else return (vector[0], true) }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Found expression list of length 2: '(vector<{integer}>, bool)'. It is not compatible with the other type of length 3. + │ │ Found expression list of length 3: '(vector<_>, {integer}, bool)'. It is not compatible with the other type of length 2. + │ Invalid lambda return + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:12:31 + │ +12 │ call!(|| { if (cond) (vector[], 0, false) else return (vector[0], true) }); + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.move new file mode 100644 index 0000000000000..3f9d14327c2b1 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.move @@ -0,0 +1,14 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun conditional(cond: bool) { + call!(|| { if (cond) return 0; &1 }); + call!(|| { if (cond) 1 else return &0 }); + call!(|| { if (cond) return 0; &1 }); + call!(|| { if (cond) 1 else return &0 }); + call!(|| { if (cond) return (vector[], 0, false); (vector[0], true) }); + call!(|| { if (cond) (vector[], 0, false) else return (vector[0], true) }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.exp new file mode 100644 index 0000000000000..44f24d5990c45 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.exp @@ -0,0 +1,30 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_simple.move:8:23 + │ +8 │ call!(|| return &0); + │ --- ^^^^^^^^^ + │ │ │ │ + │ │ │ Found: '&{integer}'. It is not compatible with the other type. + │ │ Invalid lambda return + │ Found: 'u64'. It is not compatible with the other type. + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_simple.move:9:24 + │ +9 │ call!<&u64>(|| return 0); + │ ---- ^^^^^^^^ + │ │ │ + │ │ Invalid lambda return + │ │ Found: integer. It is not compatible with the other type. + │ Found: '&u64'. It is not compatible with the other type. + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_invalid_simple.move:10:30 + │ +10 │ call!<(&u64, u8)>(|| return (&0, 1, 3)); + │ ---------- ^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Found expression list of length 3: '(&{integer}, {integer}, {integer})'. It is not compatible with the other type of length 2. + │ │ Invalid lambda return + │ Found expression list of length 2: '(&u64, u8)'. It is not compatible with the other type of length 3. + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.move new file mode 100644 index 0000000000000..95817679e668b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_simple.move @@ -0,0 +1,13 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + #[allow(dead_code)] + fun simple() { + call!(|| return &0); + call!<&u64>(|| return 0); + call!<(&u64, u8)>(|| return (&0, 1, 3)); + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.exp new file mode 100644 index 0000000000000..054a3bb0a2d76 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.exp @@ -0,0 +1,57 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_mismatched.move:9:23 + │ +8 │ if (cond) return 0; + │ -------- Expected: integer +9 │ if (cond) return false; + │ ^^^^^^^^^^^^ + │ │ │ + │ │ Given: 'bool' + │ Invalid return + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_mismatched.move:10:13 + │ + 8 │ if (cond) return 0; + │ -------- Expected: integer + 9 │ if (cond) return false; +10 │ return @0 + │ ^^^^^^^^^ + │ │ │ + │ │ Given: 'address' + │ Invalid return + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_mismatched.move:15:13 + │ +13 │ if (cond) return &0; + │ -- Expected: '&{integer}' +14 │ if (cond) return &mut 0; +15 │ return 0 + │ ^^^^^^^^ + │ │ │ + │ │ Given: integer + │ Invalid return + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_mismatched.move:19:23 + │ +18 │ if (cond) return (&0, vector[0]); + │ - Expected: integer +19 │ if (cond) return (&mut 0, vector[false]); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Given: 'bool' + │ Invalid return + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/lambda_return_mismatched.move:24:23 + │ +23 │ if (cond) return (&0, vector[0]); + │ --------------- Expected expression list of length 2: '(&{integer}, vector<{integer}>)' +24 │ if (cond) return (&0, vector[0], 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Given expression list of length 3: '(&{integer}, vector<{integer}>, {integer})' + │ Invalid return + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.move new file mode 100644 index 0000000000000..ef31105b7cbcf --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_return_mismatched.move @@ -0,0 +1,29 @@ +module a::m { + macro fun call<$T>($f: || -> $T): $T { + $f() + } + + fun commands(cond: bool) { + call!(|| { + if (cond) return 0; + if (cond) return false; + return @0 + }); + call!(|| { + if (cond) return &0; + if (cond) return &mut 0; + return 0 + }); + call!(|| { + if (cond) return (&0, vector[0]); + if (cond) return (&mut 0, vector[false]); + return (&0, vector[]) + }); + call!(|| { + if (cond) return (&0, vector[0]); + if (cond) return (&0, vector[0], 1); + return (&0, vector[0]) + }); + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.exp new file mode 100644 index 0000000000000..f347b0a49b224 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.exp @@ -0,0 +1,24 @@ +error[E04006]: invalid subtype + ┌─ tests/move_2024/typing/lambda_subtyping_invalid.move:12:9 + │ + 2 │ macro fun imm_arg($f: |&u64| -> u64) { + │ ---- Given: '&u64' + · +12 │ imm_arg!(|x: &mut u64| *x = 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Expected: '&mut u64' + │ Invalid call of 'a::m::imm_arg'. Invalid argument for parameter '$f' + +error[E04006]: invalid subtype + ┌─ tests/move_2024/typing/lambda_subtyping_invalid.move:13:9 + │ + 7 │ macro fun mut_ret($f: || -> &mut u64) { + │ -------- Expected: '&mut u64' + · +13 │ mut_ret!(|| -> &u64 { &0 }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Given: '&u64' + │ Invalid call of 'a::m::mut_ret'. Invalid argument for parameter '$f' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.move new file mode 100644 index 0000000000000..3085aeb58ff4e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_invalid.move @@ -0,0 +1,15 @@ +module a::m { + macro fun imm_arg($f: |&u64| -> u64) { + let mut x = 0; + $f(&mut x); + } + + macro fun mut_ret($f: || -> &mut u64) { + $f(); + } + + fun t() { + imm_arg!(|x: &mut u64| *x = 1); + mut_ret!(|| -> &u64 { &0 }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp new file mode 100644 index 0000000000000..d97f1fbf2350f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp @@ -0,0 +1,33 @@ +error[E04006]: invalid subtype + ┌─ tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move:15:10 + │ +14 │ macro fun return_imm($f: || -> &u64) { + │ ---- Given: '&u64' +15 │ *$f() = 0; + │ ^^^^ + │ │ + │ Invalid mutation. Expected a mutable reference + │ Expected: '&mut _' + +error[E04006]: invalid subtype + ┌─ tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move:20:24 + │ + 3 │ macro fun pass_imm($f: |&u64|) { + │ ---- Given: '&u64' + · +20 │ pass_imm!(|x| *x = 0); + │ ^ + │ │ + │ Invalid mutation. Expected a mutable reference + │ Expected: '&mut _' + +error[E04006]: invalid subtype + ┌─ tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move:21:30 + │ +21 │ pass_mut!(|x: &u64| *x = 0); + │ ---- ^ + │ │ │ + │ │ Invalid mutation. Expected a mutable reference + │ │ Expected: '&mut _' + │ Given: '&u64' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move new file mode 100644 index 0000000000000..a360ab816e06e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move @@ -0,0 +1,26 @@ +module a::m { + // test that despite "valid" usage, we respect the annotations + macro fun pass_imm($f: |&u64|) { + let mut x = 0; + $f(&mut x) + } + + macro fun pass_mut($f: |&mut u64|) { + let mut x = 0; + $f(&mut x) + } + + + macro fun return_imm($f: || -> &u64) { + *$f() = 0; + } + + fun t() { + // this should error since it was annotated as taking a &u64 + pass_imm!(|x| *x = 0); + pass_mut!(|x: &u64| *x = 0); + // this should error since it was annotated as returning a &u64 + let mut x = 0; + return_imm!(|| &mut x); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_valid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_valid.move new file mode 100644 index 0000000000000..03c2f339e7c3c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_valid.move @@ -0,0 +1,15 @@ +module a::m { + macro fun mut_arg($f: |&mut u64| -> u64) { + let mut x = 0; + $f(&mut x); + } + + macro fun imm_ret($f: || -> &u64) { + $f(); + } + + fun t() { + mut_arg!(|x: &u64| *x); + imm_ret!(|| -> &mut u64 { &mut 0 }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.exp new file mode 100644 index 0000000000000..26692795fe1f2 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.exp @@ -0,0 +1,16 @@ +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.move:4:9 + │ +4 │ $_x = 0; + │ ^^^ Cannot assign to argument for parameter '$_x'. Arguments must be used in value positions + │ + = 'macro' parameters are substituted without being evaluated. There is no local variable to assign to + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.move:9:14 + │ +9 │ foo!(*x); + │ ^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.move new file mode 100644 index 0000000000000..1ddde05f8df05 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_deref.move @@ -0,0 +1,11 @@ +module a::m { + // macro args are call-by-name, not all usages are valid + macro fun foo<$T>($_x: $T) { + $_x = 0; + } + + fun t() { + let x = 0; + foo!(*x); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.exp new file mode 100644 index 0000000000000..5a650af742f0a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.exp @@ -0,0 +1,12 @@ +error[E04026]: invalid 'copy' usage + ┌─ tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.move:4:9 + │ +4 │ copy $x; + │ ^^^^ Invalid 'copy'. Expected a variable or path. + +error[E04027]: invalid 'move' usage + ┌─ tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.move:5:9 + │ +5 │ move $x; + │ ^^^^ Invalid 'move'. Expected a variable or path. + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.move new file mode 100644 index 0000000000000..418bcc4f1f1ac --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_value.move @@ -0,0 +1,11 @@ +module a::m { + // macro args are call-by-name, not all usages are valid + macro fun foo<$T>($x: $T) { + copy $x; + move $x; + } + + fun t() { + foo!(0); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.exp new file mode 100644 index 0000000000000..e7e9c624a8d1b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.exp @@ -0,0 +1,12 @@ +error[E04026]: invalid 'copy' usage + ┌─ tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.move:4:9 + │ +4 │ copy $x; + │ ^^^^ Invalid 'copy'. Expected a variable or path. + +error[E04027]: invalid 'move' usage + ┌─ tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.move:5:9 + │ +5 │ move $x; + │ ^^^^ Invalid 'move'. Expected a variable or path. + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.move new file mode 100644 index 0000000000000..bc5debcbdfc65 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_invalid_usage_var.move @@ -0,0 +1,13 @@ +module a::m { + // macro args are call-by-name, not all usages are valid + macro fun foo<$T>($x: $T) { + copy $x; + move $x; + } + + fun t() { + let x = 0; + foo!(x); + x; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.exp new file mode 100644 index 0000000000000..d9805687f8ad5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.exp @@ -0,0 +1,21 @@ +error[E06002]: use of unassigned variable + ┌─ tests/move_2024/typing/macro_arg_by_name_strange_usage.move:14:14 + │ +14 │ foo!(x); // TODO improve this error message + │ ^ + │ │ + │ Invalid usage of previously moved variable 'x'. + │ Suggestion: use 'copy x' to avoid the move. + │ In a loop, this typically means it was moved in the first iteration, and is not available by the second iteration. + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_arg_by_name_strange_usage.move:17:14 + │ + 2 │ public struct X() has drop; + │ - To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct S { f: X } has drop; + │ - The type 'a::m::X' does not have the ability 'copy' + · +17 │ foo!(s.f); // TODO improve this error message + │ ^^^ Invalid implicit copy of field 'f' without the 'copy' ability + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.move new file mode 100644 index 0000000000000..4f42ff02e5a02 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage.move @@ -0,0 +1,19 @@ +module a::m { + public struct X() has drop; + public struct S { f: X } has drop; + + // some usages of the expression won't do what they would do if they werent used as a value + // In other words, we don't re-interpret the expression as a pth + macro fun foo<$T>($x: $T) { + &$x; + &mut $x; + } + + fun t() { + let x = X(); + foo!(x); // TODO improve this error message + + let s = S { f: X() }; + foo!(s.f); // TODO improve this error message + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.exp new file mode 100644 index 0000000000000..790b99ec9cd61 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.exp @@ -0,0 +1,10 @@ +error[E06002]: use of unassigned variable + ┌─ tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.move:14:14 + │ +14 │ foo!(s); // TODO improve this error message + │ ^ + │ │ + │ Invalid usage of previously moved variable 's'. + │ Suggestion: use 'copy s' to avoid the move. + │ In a loop, this typically means it was moved in the first iteration, and is not available by the second iteration. + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.move new file mode 100644 index 0000000000000..3b68cc0997541 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_arg_by_name_strange_usage_fields.move @@ -0,0 +1,16 @@ +module a::m { + public struct X() has drop; + public struct S { f: X } has drop; + + // some usages of the expression won't do what they would do if they werent used as a value + // In other words, we don't re-interpret the expression as a pth + macro fun foo<$T>($s: $T) { + &$s.f; + &mut $s.f; + } + + fun t() { + let s = S { f: X() }; + foo!(s); // TODO improve this error message + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.exp new file mode 100644 index 0000000000000..746082591dd58 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.exp @@ -0,0 +1,9 @@ +error[E07005]: invalid transfer of references + ┌─ tests/move_2024/typing/macro_by_name_gives_unique_locals.move:13:9 + │ +13 │ check_unique(s1, s2) + │ ^^^^^^^^^^^^^^^^^^^^ Invalid usage of reference as function argument. Cannot transfer a mutable reference that is being borrowed + · +25 │ foo!(&mut s); + │ ------ It is still being mutably borrowed by this reference + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.move new file mode 100644 index 0000000000000..53db6622c640a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_by_name_gives_unique_locals.move @@ -0,0 +1,29 @@ +module a::m { + public struct S { f: u64 } has copy, drop; + + fun check_unique(s1: &mut S, s2: &mut S) { + s1.f = 1; + s2.f = 2; + } + + macro fun foo($s: &mut S) { + let s1 = $s; + let s2 = $s; + // if we do not give unique locals, this will fail since we would not have unique ownership + check_unique(s1, s2) + } + + fun valid() { + // no errors! + // a new s is made for each usage of the arg + foo!({ let mut s = S { f: 0 }; &mut s }); + } + + fun invalid() { + // to double check, we will make an invalid call + let mut s = S { f: 0 }; + foo!(&mut s); + } + + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.exp new file mode 100644 index 0000000000000..b3f3ac60152aa --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.exp @@ -0,0 +1,48 @@ +error[E04031]: invalid usage of lambda + ┌─ tests/move_2024/typing/macro_call_indirect_lambda_invalid.move:3:12 + │ +3 │ $f($x) + │ ^^ Lambdas can only be used directly as arguments to 'macro' functions + +error[E04031]: invalid usage of lambda + ┌─ tests/move_2024/typing/macro_call_indirect_lambda_invalid.move:7:17 + │ +7 │ let f = |x| x + 1; + │ ^^^^^^^^^ Lambdas can only be used directly as arguments to 'macro' functions + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/macro_call_indirect_lambda_invalid.move:8:24 + │ +8 │ let x = apply!(f, 1); + │ ^ Unable to bind lambda to parameter '$f'. The lambda must be passed directly + +error[E04030]: invalid usage of lambda type + ┌─ tests/move_2024/typing/macro_call_indirect_lambda_invalid.move:12:13 + │ +12 │ let f: |u64| -> u64; + │ ^ Unexpected lambda type. Lambdas can only be used with 'macro' functions, as parameters or direct arguments + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/macro_call_indirect_lambda_invalid.move:13:24 + │ +13 │ let x = apply!(f, 1); + │ ^ Unable to bind lambda to parameter '$f'. The lambda must be passed directly + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/macro_call_indirect_lambda_invalid.move:17:24 + │ +17 │ let x = apply!((0: |u64| -> u64), 1); + │ ^^^^^^^^^^^^^^^^^ Unable to bind lambda to parameter '$f'. The lambda must be passed directly + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macro_call_indirect_lambda_invalid.move:21:17 + │ + 2 │ macro fun apply($f: |u64| -> u64, $x: u64): u64 { + │ --- Expected: 'u64' + · +21 │ let x = apply!(|x| x, |x| x); + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Given: '|_| -> _' + │ Invalid call of 'a::m::apply'. Invalid argument for parameter '$x' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.move new file mode 100644 index 0000000000000..a9b8531230370 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_call_indirect_lambda_invalid.move @@ -0,0 +1,23 @@ +module a::m { + macro fun apply($f: |u64| -> u64, $x: u64): u64 { + $f($x) + } + + fun t() { + let f = |x| x + 1; + let x = apply!(f, 1); + } + + fun t2() { + let f: |u64| -> u64; + let x = apply!(f, 1); + } + + fun t3() { + let x = apply!((0: |u64| -> u64), 1); + } + + fun t4() { + let x = apply!(|x| x, |x| x); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_dot_call_auto_borrow.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_dot_call_auto_borrow.move new file mode 100644 index 0000000000000..b74ae803165ee --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_dot_call_auto_borrow.move @@ -0,0 +1,30 @@ +module 0x42::t { + +public struct X has drop {} +public struct Y has drop { x: X } + +macro fun val($_self: X) {} +macro fun imm($_self: &X) {} +macro fun mut_($_self: &mut X) {} + +public fun foo(mut x1: X, x2: &X, x3: &mut X) { + x1.mut_!(); + x3.mut_!(); + + x1.imm!(); + x2.imm!(); + x3.imm!(); + + x1.val!(); +} + +public fun bar(mut y1: Y, y2: &Y, y3: &mut Y) { + y1.x.mut_!(); + y3.x.mut_!(); + + y1.x.imm!(); + y2.x.imm!(); + y3.x.imm!(); +} + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint.move new file mode 100644 index 0000000000000..be8fd7f6f7289 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint.move @@ -0,0 +1,18 @@ +module a::m { + public struct X() has copy, drop; + + fun mycopy(t: &T): T { + *t + } + + macro fun needs_copy<$T, $U, $V>($x: X<$T>, $u: $U, $v: $V): X<$U> { + $x; + $u; + mycopy(&$v); + X() + } + + fun t() { + needs_copy!(X(), 0u64, @0); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.exp new file mode 100644 index 0000000000000..5359c6038c0bd --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.exp @@ -0,0 +1,57 @@ +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_duck_typing_constraint_invalid.move:9:41 + │ + 2 │ public struct X() has copy, drop; + │ ---- 'copy' constraint declared here + 3 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + · + 9 │ macro fun needs_copy<$T, $U, $V>(_: X<$T>, _: $U, $v: $V): X<$U> { + │ ^^^^^ 'copy' constraint not satisifed + · +16 │ needs_copy!(X(), None(), None()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_duck_typing_constraint_invalid.move:9:64 + │ + 2 │ public struct X() has copy, drop; + │ ---- 'copy' constraint declared here + 3 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + · + 9 │ macro fun needs_copy<$T, $U, $V>(_: X<$T>, _: $U, $v: $V): X<$U> { + │ ^^^^^ 'copy' constraint not satisifed + · +16 │ needs_copy!(X(), None(), None()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_duck_typing_constraint_invalid.move:10:9 + │ + 3 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 4 │ + 5 │ fun mycopy(t: &T): T { + │ ---- 'copy' constraint declared here + · +10 │ mycopy(&$v); + │ ^^^^^^^^^^^ 'copy' constraint not satisifed + · +16 │ needs_copy!(X(), None(), None()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_duck_typing_constraint_invalid.move:11:9 + │ + 2 │ public struct X() has copy, drop; + │ ---- 'copy' constraint declared here + 3 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + · +11 │ X() + │ ^^^ 'copy' constraint not satisifed + · +16 │ needs_copy!(X(), None(), None()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.move new file mode 100644 index 0000000000000..2b3e1bcb05a1b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_constraint_invalid.move @@ -0,0 +1,18 @@ +module a::m { + public struct X() has copy, drop; + public struct None() has drop; + + fun mycopy(t: &T): T { + *t + } + + macro fun needs_copy<$T, $U, $V>(_: X<$T>, _: $U, $v: $V): X<$U> { + mycopy(&$v); + X() + } + + #[allow(dead_code)] + fun t() { + needs_copy!(X(), None(), None()); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method.move new file mode 100644 index 0000000000000..a2b91f739230b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method.move @@ -0,0 +1,26 @@ +module a::m { + public struct ByValue() has copy, drop; + public struct ByImm() has copy, drop; + public struct ByMut() has copy, drop; + + use fun bv_foo as ByValue.foo; + fun bv_foo(_: ByValue) {} + + use fun bi_foo as ByImm.foo; + fun bi_foo(_: &ByImm) {} + + use fun bm_foo as ByMut.foo; + fun bm_foo(_: &mut ByMut) {} + + // this is "duck typing" in the sense that this macro can be called only by those + // types that "walk and talk like a duck" + macro fun call_foo<$T>($x: $T) { + $x.foo() + } + + fun t() { + call_foo!(ByValue()); + call_foo!(&ByImm()); + call_foo!(&mut ByMut()); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.exp new file mode 100644 index 0000000000000..52cdc8e8d74fa --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.exp @@ -0,0 +1,9 @@ +error[E04023]: invalid method call + ┌─ tests/move_2024/typing/macro_duck_typing_method_invalid.move:4:9 + │ +4 │ $x.foo() + │ ^^^^^^^^ + │ │ │ + │ │ No local 'use fun' alias was found for 'a::m::X.foo', and no function 'foo' was found in the defining module 'a::m' + │ Invalid method call. No known method 'foo' on type 'a::m::X' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.move new file mode 100644 index 0000000000000..0b2600b34e912 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_method_invalid.move @@ -0,0 +1,10 @@ +module a::m { + public struct X() has copy, drop; + macro fun call_foo<$T>($x: $T) { + $x.foo() + } + + fun t() { + call_foo!(X()); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type.move new file mode 100644 index 0000000000000..ffb36d274b9b0 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type.move @@ -0,0 +1,26 @@ +module a::m { + public struct X() has copy, drop; + + macro fun is_x<$T>($x: $T) { + ($x: X); + } + + macro fun is_x_ret<$T>($x: $T): X { + $x + } + + macro fun is_num<$T>($x: $T) { + ($x as $T); + } + + fun t() { + is_x!(X()); + is_x_ret!(X()); + is_num!(0u8); + is_num!(0u16); + is_num!(0u32); + is_num!(0u64); + is_num!(0u128); + is_num!(0u256); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.exp new file mode 100644 index 0000000000000..7970de55fcbd9 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.exp @@ -0,0 +1,51 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move:5:14 + │ + 5 │ ($x: X); + │ ^ + │ │ + │ Invalid type annotation + │ Expected: 'a::m::X' + · +17 │ is_x!(0); + │ -------- Given: integer + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move:8:37 + │ + 8 │ macro fun is_x_ret<$T>($x: $T): X { + │ ^ + │ │ + │ Invalid type annotation + │ Expected: 'a::m::X' + · +18 │ is_x_ret!(0); + │ ------------ Given: integer + +error[E04003]: built-in operation not supported + ┌─ tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move:13:10 + │ +13 │ ($x as $T); + │ ^^ Invalid argument to 'as' + · +19 │ is_num!(X()); + │ --- Found: 'a::m::X'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + +error[E04003]: built-in operation not supported + ┌─ tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move:13:10 + │ +13 │ ($x as $T); + │ ^^ Invalid argument to 'as' + · +20 │ is_num!(@0); + │ -- Found: 'address'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + +error[E04003]: built-in operation not supported + ┌─ tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move:13:10 + │ +13 │ ($x as $T); + │ ^^ Invalid argument to 'as' + · +21 │ is_num!(vector[0]); + │ --------- Found: 'vector'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move new file mode 100644 index 0000000000000..2262b2f340833 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_duck_typing_specific_type_invalid.move @@ -0,0 +1,23 @@ +module a::m { + public struct X() has copy, drop; + + macro fun is_x<$T>($x: $T) { + ($x: X); + } + + macro fun is_x_ret<$T>($x: $T): X { + $x + } + + macro fun is_num<$T>($x: $T) { + ($x as $T); + } + + fun t() { + is_x!(0); + is_x_ret!(0); + is_num!(X()); + is_num!(@0); + is_num!(vector[0]); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams.move new file mode 100644 index 0000000000000..50e54c17942bd --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams.move @@ -0,0 +1,29 @@ +module a::m { + public struct X() has copy, drop, store; + public fun f(_: X) {} + + macro fun apply<$T>($x: $T, $l: |$T|) { + $l($x); + } + + macro fun useless<$U>($x: X<$U>): X<$U> { + let x = $x; + freeze>(&mut X()); + f<$U>(X()); + X<$U>(); + x.f<$U>(); + apply!(x, |_: X<$U>| ()); + X<$U>() = x; + let _: X<$U> = x; + let X<$U>() = x; + (0 as $U); + (x: X<$U>); + x + } + + fun t() { + useless!(X()); + } + + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.exp new file mode 100644 index 0000000000000..b4ce70e90390e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.exp @@ -0,0 +1,206 @@ +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:10:31 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +10 │ macro fun useless<$U>($x: X<$U>): X<$U> { + │ ^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:10:39 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +10 │ macro fun useless<$U>($x: X<$U>): X<$U> { + │ ^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:12:16 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +12 │ freeze>(&mut X()); + │ ^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:12:28 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +12 │ freeze>(&mut X()); + │ ^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:13:9 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + 4 │ public fun f(_: X) {} + │ ---- 'copy' constraint declared here + · +13 │ f<$U>(X()); + │ ^^^^^^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:13:15 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +13 │ f<$U>(X()); + │ ^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:14:9 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +14 │ X<$U>(); + │ ^^^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:15:9 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + 4 │ public fun f(_: X) {} + │ ---- 'copy' constraint declared here + · +15 │ x.f<$U>(); + │ ^^^^^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:16:23 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +16 │ apply!(x, |_: X<$U>| ()); + │ ^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:17:9 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +17 │ X<$U>() = x; + │ ^^^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:18:16 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +18 │ let _: X<$U> = x; + │ ^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:19:13 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +19 │ let X<$U>() = x; + │ ^^^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E04003]: built-in operation not supported + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:20:10 + │ +20 │ (0 as $U); + │ ^ Invalid argument to 'as' + · +27 │ useless!(X()); + │ ---- Found: 'a::m::None'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:21:13 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +21 │ (x: X<$U>); + │ ^^^^^ 'copy' constraint not satisifed + · +27 │ useless!(X()); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macro_subst_tparams_invalid.move:27:24 + │ + 2 │ public struct None() has drop; + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct X() has copy, drop, store; + │ ---- 'copy' constraint declared here + · +27 │ useless!(X()); + │ ---- ^^^ 'copy' constraint not satisifed + │ │ + │ The type 'a::m::None' does not have the ability 'copy' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.move new file mode 100644 index 0000000000000..401e2cdd06af3 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_subst_tparams_invalid.move @@ -0,0 +1,31 @@ +module a::m { + public struct None() has drop; + public struct X() has copy, drop, store; + public fun f(_: X) {} + + macro fun apply<$T>($x: $T, $l: |$T|) { + $l($x); + } + + macro fun useless<$U>($x: X<$U>): X<$U> { + let x = $x; + freeze>(&mut X()); + f<$U>(X()); + X<$U>(); + x.f<$U>(); + apply!(x, |_: X<$U>| ()); + X<$U>() = x; + let _: X<$U> = x; + let X<$U>() = x; + (0 as $U); + (x: X<$U>); + x + } + + fun t() { + // the susbstitutions are all correct, but should all hit errors + useless!(X()); + } + + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_type_args_ref_or_tuple.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_type_args_ref_or_tuple.move new file mode 100644 index 0000000000000..8963482ab0160 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_type_args_ref_or_tuple.move @@ -0,0 +1,11 @@ +module a::m { + macro fun foo<$T>() {} + + fun t() { + // invalid for normal functions + foo!<&u64>(); + foo!<&mut u64>(); + foo!<()>(); + foo!<(u64, bool)>(); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes.move new file mode 100644 index 0000000000000..adaefe46a5091 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes.move @@ -0,0 +1,55 @@ +module a::m { + public struct A() has drop; + public struct B() has drop; + public struct C() has drop; + public struct D() has drop; + public struct X() has drop; + + fun a(_: u64): A { A() } + fun b(_: u64): B { B() } + fun c(_: u64): C { C() } + fun d(_: u64): D { D() } + + macro fun apply($x: u64, $f: |u64| -> u64): u64 { + use fun d as u64.foo; + let x = $x; + (x.foo(): D); + let res = $f({ + (x.foo(): D); + x + }); + (res.foo(): D); + res + } + + // we overload foo in each context + // the type annotation tests that the correct foo is used + use fun a as u64.foo; + fun test() { + apply!( + { + use fun b as u64.foo; + (1u64.foo(): B); + 1 + }, + |x| { + use fun c as u64.foo; + (x.foo(): C); + x + } + ); + apply!( + { + (1u64.foo(): A); + 1 + }, + |x| { + (x.foo(): A); + x + } + ); + } + + + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_captured_lambda.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_captured_lambda.move new file mode 100644 index 0000000000000..999910d6bd942 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_captured_lambda.move @@ -0,0 +1,25 @@ +module a::m { + public fun into(x: u64): u64 { x } + + use fun into as u64.into; + + public macro fun apply($f: |u64| -> u64, $x: u64): u64 { + $f($x.into()) + } +} + +module b::other { + use a::m::apply; + + public fun into(x: u8): u64 { (x as u64) } + use fun into as u8.into; + + public macro fun myapply($f: |u8| -> u64, $x: u8): u64 { + let x = $x; + apply!(|u| $f(x) + x.into() + u, x.into()) + } + + public fun t(): u64 { + myapply!(|x| x.into(), 1) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_cross_module.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_cross_module.move new file mode 100644 index 0000000000000..3df9143c50226 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_cross_module.move @@ -0,0 +1,36 @@ +module a::m { + public struct A() has drop; + public struct B() has drop; + public struct X() has drop; + + public fun a(_: u64): A { A() } + public fun b(_: u64): B { B() } + + use fun a as u64.foo; + + public macro fun apply($x: u64, $f: |u64| -> u64): u64 { + let x = $x; + $f({ + (x.foo(): A); + x + }); + (x.foo(): A); + { + use fun b as u64.foo; + let res = $f({ + (x.foo(): B); + x + }); + (res.foo(): B); + res + } + } + +} + +module b::other { + fun t() { + // the use funs should resolve even though they are defined in another module/not in scope + a::m::apply!(1, |x| x + 1); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_in_lambda_arg.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_in_lambda_arg.move new file mode 100644 index 0000000000000..8fac00892cac0 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macro_unique_use_fun_scopes_in_lambda_arg.move @@ -0,0 +1,17 @@ +module a::m { + public fun id(x: u64): u64 { x } + + use fun id as u64.id; + + public macro fun apply($f: |u64| -> u64, $x: u64): u64 { + $f($x.id()) + } +} + +module b::other { + use a::m::apply; + + public fun t(): u64 { + apply!(|x| x + 1, 1) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes.move new file mode 100644 index 0000000000000..d375b7c989fbd --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes.move @@ -0,0 +1,32 @@ +module a::m { + // macros have their own unique scopes + macro fun foo($x: u64, $f: || -> address): (u64, bool, address) { + let x = $x; + $f(); // try to capture x + let a = $f(); + (x, { let x = false; x }, a) + } + + macro fun bar<$T>($x: vector, $f: |$T| -> address, $v: $T): (u64, bool, address) { + let x = $x; + $f($v); // try to capture x + let x = get(x); + let a = $f($v); + foo!(x, || { let x = a; x }) + } + + fun t() { + foo!(0, || @0); + foo!(0, || { let x = @0; x }); + foo!(0, || { let (_, _, x) = foo!(0, || { let x = @0; x }); x }); + bar!(vector[0], |x| get(x), vector[@0]); + bar!(vector[0], |x| { let x = get(x); x }, vector[@0]); + bar!( + vector[0], + |x| { let (_, _, x) = bar!(vector[0], |x| { let x = get(x); x }, get(x)); x }, + vector[vector[@0]], + ); + } + + fun get(_: vector): T { abort 0 } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_block_labels.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_block_labels.move new file mode 100644 index 0000000000000..8ddbb688addf4 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_block_labels.move @@ -0,0 +1,103 @@ +#[allow(dead_code)] +module a::blocks { + // macros have their own unique scopes + macro fun foo($x: u64, $f: |u64| -> u64): (u64, bool) { + 'a: { + $f($x); // try to capture a + let x = $f(return 'a (0, false)); + (x, true) + } + } + + macro fun bar<$T>($x: vector, $f: |vector| -> u64): (u64, bool) { + 'a: { + $f($x); // try to capture a + let x = vector[$f(return 'a (0, false))]; + foo!(0, |_| 'a: { return'a get(x) }) + } + } + + fun t() { + foo!(0, |x| x); + foo!(0, |x| 'a: { return'a x }); + foo!(0, |x| 'a: { let (i, _) = foo!(0, |y| return'a x + y); i }); + bar!(vector[0], |x| get(x)); + bar!(vector[0], |x| 'a: { let x = get(x); return'a x }); + bar!( + vector[0], + |x| 'a: { let (i, _) = bar!(return'a get(x), |x| 'a: { let x = get(x); return'a x }); i }, + ); + } + + fun get(_: vector): T { abort 0 } +} + +#[allow(dead_code)] +module a::loops { + // macros have their own unique scopes + macro fun foo($x: u64, $f: |u64| -> u64): (u64, bool) { + 'a: loop { + $f($x); // try to capture a + let x = $f(break 'a (0, false)); + } + } + + macro fun bar<$T>($x: vector, $f: |vector| -> u64): (u64, bool) { + 'a: loop { + $f($x); // try to capture a + let x = vector[$f(break 'a (0, false))]; + foo!(0, |_| { break'a (get(x), true) }); + } + } + + fun t() { + foo!(0, |x| x); + foo!(0, |x| 'a: { return'a x }); + foo!(0, |x| 'a: { let (i, _) = foo!(0, |y| return'a x + y); i }); + bar!(vector[0], |x| get(x)); + bar!(vector[0], |x| 'a: { let x = get(x); return'a x }); + bar!( + vector[0], + |x| 'a: { let (i, _) = bar!(return'a get(x), |x| 'a: { let x = get(x); return'a x }); i }, + ); + } + + fun get(_: vector): T { abort 0 } +} + + + +#[allow(dead_code)] +module a::whiles { + // macros have their own unique scopes + macro fun foo($x: u64, $f: |u64| -> u64): (u64, bool) { + 'a: while (true) { + $f($x); // try to capture a + let x = $f(break 'a); + }; + (0, false) + } + + macro fun bar<$T>($x: vector, $f: |vector| -> u64): (u64, bool) { + 'a: while (true) { + $f($x); // try to capture a + let x = vector[$f(break 'a)]; + foo!(0, |_| { break'a }); + }; + (0, false) + } + + fun t() { + foo!(0, |x| x); + foo!(0, |x| 'a: { return'a x }); + foo!(0, |x| 'a: { let (i, _) = foo!(0, |y| return'a x + y); i }); + bar!(vector[0], |x| get(x)); + bar!(vector[0], |x| 'a: { let x = get(x); return'a x }); + bar!( + vector[0], + |x| 'a: { let (i, _) = bar!(return'a get(x), |x| 'a: { let x = get(x); return'a x }); i }, + ); + } + + fun get(_: vector): T { abort 0 } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_by_name_arg.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_by_name_arg.move new file mode 100644 index 0000000000000..34128de7d9d4e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_by_name_arg.move @@ -0,0 +1,38 @@ +module a::m { + // macros have their own unique scopes + macro fun foo($x: u64, $f: address): (u64, bool, address) { + let x = $x; + $f; // try to capture x + let a = $f; + (x, { let x = false; x }, a) + } + + macro fun bar($x: vector, $f: address): (u64, bool, address) { + let x = $x; + $f; // try to capture x + let x = get(x); + let a = $f; + foo!(x, { let x = a; x }) + } + + fun t() { + foo!(0, @0); + foo!(0, { let x = @0; x }); + foo!(0, { let (_, _, x) = foo!(0, { let x = @0; x }); x }); + bar!(vector[0], { let x = vector[@0]; get(x) }); + bar!(vector[0], { let x = vector[@0]; let x = get(x); x }); + bar!( + vector[0], + { + let x = vector[vector[@0]]; + let (_, _, x) = bar!( + vector[0], + { let x = get(x); let x = get(x); x }, + ); + x + }, + ); + } + + fun get(_: vector): T { abort 0 } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_nested.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_nested.move new file mode 100644 index 0000000000000..f3d24602696fb --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_nested.move @@ -0,0 +1,40 @@ +module a::m { + // macros have their own unique scopes + macro fun for($start: u64, $stop: u64, $body: |u64|) { + let mut i = $start; + let stop = $stop; + while (i < stop) { + $body(i); + i = i + 1 + } + } + + macro fun for_each<$T>($v: &vector<$T>, $body: |&$T|) { + let v = $v; + let mut i = 0; + let n = v.length(); + while (i < n) { + $body(v.borrow(i)); + i = i + 1 + } + } + + macro fun new<$T>($len: u64, $f: |u64| -> $T): vector<$T> { + let len = $len; + let mut v = vector[]; + for!(0, len, |i| v.push_back($f(i))); + v + } + + macro fun sum($v: &vector): u64 { + let mut s = 0; + for_each!($v, |i| s = s + *i); + s + } + + entry fun main() { + let v = new!(10, |i| i); + assert!(sum!(&v) == 45, 0); + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.exp new file mode 100644 index 0000000000000..48c4f801b290c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.exp @@ -0,0 +1,12 @@ +error[E03009]: unbound variable + ┌─ tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.move:15:17 + │ +15 │ foo!(|| x); + │ ^ Unbound variable 'x' + +error[E03009]: unbound variable + ┌─ tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.move:16:17 + │ +16 │ bar!(|| x); + │ ^ Unbound variable 'x' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.move new file mode 100644 index 0000000000000..8897ae0e71376 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture.move @@ -0,0 +1,18 @@ +module a::m { + // macros have their own unique scopes + macro fun foo($f: || -> u64): u64 { + let x = 0u64; + $f() // try to capture x + } + + macro fun bar($f: || -> u64): u64 { + let x = 0u64; + foo!(|| $f()) // try to capture x + } + + fun t() { + // x is not in scope in either example + foo!(|| x); + bar!(|| x); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.exp new file mode 100644 index 0000000000000..9c53523587d72 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.exp @@ -0,0 +1,48 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move:3:23 + │ + 3 │ macro fun foo($f: u64): u64 { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: 'u64' + · +14 │ let x = vector[]; + │ ------------- Given: 'vector' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move:3:23 + │ + 3 │ macro fun foo($f: u64): u64 { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: 'u64' + · +18 │ let x = vector[]; + │ ------------- Given: 'vector' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move:8:23 + │ + 8 │ macro fun bar($f: u64): u64 { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: 'u64' + · +14 │ let x = vector[]; + │ ------------- Given: 'vector' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move:8:23 + │ + 8 │ macro fun bar($f: u64): u64 { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: 'u64' + · +18 │ let x = vector[]; + │ ------------- Given: 'vector' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move new file mode 100644 index 0000000000000..cb4ab924380c0 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_have_unique_scopes_unhygienic_capture_by_name_arg.move @@ -0,0 +1,22 @@ +module a::m { + // macros have their own unique scopes + macro fun foo($f: u64): u64 { + let x = 0u64; + $f // try to capture x + } + + macro fun bar($f: u64): u64 { + let x = 0u64; + foo!($f) // try to capture x + } + + fun t() { + let x = vector[]; + // if we capture x, these will type check + foo!(x); + bar!(x); + let x = vector[]; + foo!({ x }); + bar!({ x }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked.move new file mode 100644 index 0000000000000..de0c896371f95 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked.move @@ -0,0 +1,15 @@ +module a::m { + macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> $U) { + $f(0); + $g(0, 1); + $h(); + } + + fun t() { + foo!>( + |_| vector[], + |a, b| vector[(a as u8), (b as u8)], + || b"hello", + ) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp new file mode 100644 index 0000000000000..fd7befe3e472e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp @@ -0,0 +1,39 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:10:17 + │ + 9 │ foo!>( + │ --- ---------- Expected: 'vector' + │ │ + │ Given: 'u64' +10 │ |x| x, // invalid + │ ^ Invalid type annotation + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:16:20 + │ +14 │ foo!>( + │ --- -- Expected: 'u8' + │ │ + │ Given: 'u64' +15 │ |_| vector[], +16 │ |a, b| vector[a, b], // invalid + │ ^^^^^^^^^^^^ Invalid type annotation + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:22:16 + │ +19 │ foo!>( + │ -- Expected: 'u8' + · +22 │ || vector[vector[]], // invalid + │ ^^^^^^^^^^^^^^^^ + │ │ │ + │ │ Given: 'vector<_>' + │ Invalid type annotation + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:22:23 + │ +22 │ || vector[vector[]], // invalid + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.move new file mode 100644 index 0000000000000..719a8ebe1923b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.move @@ -0,0 +1,25 @@ +module a::m { + macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> $U) { + $f(0); + $g(0, 1); + $h(); + } + + fun t() { + foo!>( + |x| x, // invalid + |a, b| vector[(a as u8), (b as u8)], + || b"hello", + ); + foo!>( + |_| vector[], + |a, b| vector[a, b], // invalid + || b"hello", + ); + foo!>( + |_| vector[], + |a, b| vector[(a as u8), (b as u8)], + || vector[vector[]], // invalid + ) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp new file mode 100644 index 0000000000000..7f6d5eeff0ee5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp @@ -0,0 +1,116 @@ +error[E04017]: too many arguments + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:3:9 + │ +3 │ $f(0); + │ ^^^^^ + │ │ │ + │ │ Found 1 argument(s) here + │ Invalid lambda call of '$f'. The call expected 0 argument(s) but got 1 + +error[E04016]: too few arguments + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:3:9 + │ +3 │ $f(0); + │ ^^^^^ + │ │ │ + │ │ Found 1 argument(s) here + │ Invalid lambda call of '$f'. The call expected 2 argument(s) but got 1 + +error[E04016]: too few arguments + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:4:9 + │ +4 │ $g(0, 1); + │ ^^^^^^^^ + │ │ │ + │ │ Found 2 argument(s) here + │ Invalid lambda call of '$g'. The call expected 3 argument(s) but got 2 + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:9:9 + │ + 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + │ ---------- Expected a lambda with 1 arguments: '|u64| -> vector' + · + 9 │ ╭ foo!>( +10 │ │ || vector[], // invalid + │ │ ----------- Given lambda with 0 arguments: '|| -> _' +11 │ │ |a, b| vector[(a as u8), (b as u8)], +12 │ │ || (b"hello", b"world"), +13 │ │ ); + │ ╰─────────^ Invalid call of 'a::m::foo'. Invalid argument for parameter '$f' + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:10:16 + │ +10 │ || vector[], // invalid + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:14:9 + │ + 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + │ ---------- Expected a lambda with 1 arguments: '|u64| -> vector' + · +14 │ ╭ foo!>( +15 │ │ |_, _| vector[], // invalid + │ │ --------------- Given lambda with 2 arguments: '|_, _| -> _' +16 │ │ |a, b| vector[(a as u8), (b as u8)], +17 │ │ || (b"hello", b"world"), +18 │ │ ); + │ ╰─────────^ Invalid call of 'a::m::foo'. Invalid argument for parameter '$f' + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:15:20 + │ +15 │ |_, _| vector[], // invalid + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:20:17 + │ +19 │ foo!>( + │ ---------- Expected: 'vector' +20 │ |_| (vector[], vector[]), // invalid + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ Invalid type annotation + │ Given: '(vector, vector)' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:24:9 + │ + 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + │ -------------- Expected a lambda with 2 arguments: '|u64, u64| -> vector' + · +24 │ ╭ foo!>( +25 │ │ |_| vector[], +26 │ │ |a, b, _| vector[(a as u8), (b as u8)], // invalid + │ │ -------------------------------------- Given lambda with 3 arguments: '|_, _, _| -> _' +27 │ │ || (b"hello", b"world"), +28 │ │ ); + │ ╰─────────^ Invalid call of 'a::m::foo'. Invalid argument for parameter '$g' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:32:16 + │ + 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + │ -------- Expected expression list of length 2: '(vector, vector)' + · +32 │ || (b"hello", b"world", b"!"), // invalid + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ Invalid type annotation + │ Given expression list of length 3: '(vector, vector, vector)' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:37:16 + │ + 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + │ -------- Expected: '(vector, vector)' + · +37 │ || b"hello", // invalid + │ ^^^^^^^^ + │ │ + │ Invalid type annotation + │ Given: 'vector' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move new file mode 100644 index 0000000000000..ad93f2441b74a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move @@ -0,0 +1,40 @@ +module a::m { + macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + $f(0); + $g(0, 1); + $h(); + } + + fun t() { + foo!>( + || vector[], // invalid + |a, b| vector[(a as u8), (b as u8)], + || (b"hello", b"world"), + ); + foo!>( + |_, _| vector[], // invalid + |a, b| vector[(a as u8), (b as u8)], + || (b"hello", b"world"), + ); + foo!>( + |_| (vector[], vector[]), // invalid + |a, b| vector[(a as u8), (b as u8)], + || (b"hello", b"world"), + ); + foo!>( + |_| vector[], + |a, b, _| vector[(a as u8), (b as u8)], // invalid + || (b"hello", b"world"), + ); + foo!>( + |_| vector[], + |a, b| vector[(a as u8), (b as u8)], + || (b"hello", b"world", b"!"), // invalid + ); + foo!>( + |_| vector[], + |a, b| vector[(a as u8), (b as u8)], + || b"hello", // invalid + ); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked.move new file mode 100644 index 0000000000000..de5d906f2849e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked.move @@ -0,0 +1,6 @@ +module a::m { + // this should give an error if we checked macros bodies before expanding + macro fun bad() { + 1 + b"2" + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.exp new file mode 100644 index 0000000000000..902e7bc8427dd --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.exp @@ -0,0 +1,9 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_not_type_checked_called.move:4:11 + │ +4 │ 1 + b"2" + │ - ^ ---- Found: 'vector'. It is not compatible with the other type. + │ │ │ + │ │ Incompatible arguments to '+' + │ Found: integer. It is not compatible with the other type. + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.move new file mode 100644 index 0000000000000..d55e9bb300e63 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_not_type_checked_called.move @@ -0,0 +1,12 @@ +module a::m { + // this will give a type error when it is called + macro fun bad() { + 1 + b"2" + } + + fun t() { + bad!(); + bad!(); // only one error expected since all of the source locations are the same + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp new file mode 100644 index 0000000000000..62637a5b89cd0 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp @@ -0,0 +1,78 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:6:8 + │ + 4 │ $y: u64, + │ --- Given: 'u64' + 5 │ $z: &mut u64 + 6 │ ): (u64, $T, $U) { + │ ^^^^^^^^^^^^^ Invalid type annotation + · +19 │ foo!(1, 2u64, &mut 3u64); + │ -- Expected: 'u8' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:10:42 + │ +10 │ macro fun ref<$T, $U>($f: || -> $T): &$U { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: '&u64' + · +20 │ ref!(|| &1); + │ --- Given: 'u64' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:14:45 + │ +14 │ macro fun double<$T, $U>($f: || -> $T): ($U, $U) { + │ ^^^^^^^^ + │ │ + │ Invalid type annotation + │ Expected: '(u64, u64)' + · +21 │ double!(|| (0, 0)); + │ --- Given: 'u64' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:14:45 + │ +14 │ macro fun double<$T, $U>($f: || -> $T): ($U, $U) { + │ ^^^^^^^^ + │ │ + │ Invalid type annotation + │ Expected: '(u64, u64)' + · +22 │ double!(|| 0); + │ --- Given: 'u64' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:20:26 + │ +20 │ ref!(|| &1); + │ --- ^^ + │ │ │ + │ │ Invalid type annotation + │ │ Given: '&{integer}' + │ Expected: 'u64' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:21:29 + │ +21 │ double!(|| (0, 0)); + │ --- ^^^^^^ + │ │ │ + │ │ Invalid type annotation + │ │ Given: '({integer}, {integer})' + │ Expected: 'u64' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:23:36 + │ +23 │ double!<(u64, u64), u64>(|| (&0, &0)); + │ --- ^^^^^^^^ + │ │ ││ + │ │ │Given: '&{integer}' + │ │ Invalid type annotation + │ Expected: 'u64' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.move new file mode 100644 index 0000000000000..41fa418a32b61 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.move @@ -0,0 +1,25 @@ +module a::m { + macro fun foo<$T, $U>( + $x: u64, + $y: u64, + $z: &mut u64 + ): (u64, $T, $U) { + ($x, $y, $z) + } + + macro fun ref<$T, $U>($f: || -> $T): &$U { + $f() + } + + macro fun double<$T, $U>($f: || -> $T): ($U, $U) { + $f() + } + + fun t() { + foo!(1, 2u64, &mut 3u64); + ref!(|| &1); + double!(|| (0, 0)); + double!(|| 0); + double!<(u64, u64), u64>(|| (&0, &0)); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_valid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_valid.move new file mode 100644 index 0000000000000..a760018e74b1b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_return_checked_valid.move @@ -0,0 +1,28 @@ +module a::m { + macro fun foo<$T, $U>( + $x: u64, + $y: u64, + $z: &mut u64 + ): (u64, $T, $U) { + ($x, $y, $z) + } + + macro fun ref<$T, $U>($f: || -> $T): &$U { + $f() + } + + macro fun double<$T, $U>($f: || -> $T): ($U, $U) { + $f() + } + + fun t() { + foo!(1, 2, &mut 3); + ref!<&u64, u64>(|| &1); + ref!<&u64, u64>(|| &mut 1); + ref!<&u64, u64>(|| &mut 1); + ref!<&mut u64, u64>(|| &mut 1); + double!<(u64, u64), u64>(|| (0, 0)); + double!<(&u64, &u64), &u64>(|| (&0, &0)); + double!<(&u64, &u64), &u64>(|| (&mut 0, &mut 0)); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid.move new file mode 100644 index 0000000000000..159b5ebaab97f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid.move @@ -0,0 +1,20 @@ +module a::m { + public struct None() + public struct NeedsCopy {} has copy, drop, store; + + macro fun foo<$T: copy, $U>( + $_a: u64, + $_r: &mut u64, + $_n: NeedsCopy<$T>, + ) { + let _: NeedsCopy<$U> = NeedsCopy {}; + } + + #[allow(dead_code)] + fun t() { + // simple args don't check + foo!>(false, &mut 1, NeedsCopy {}); + foo!>(0, &mut false, NeedsCopy {}); + foo!>(0, &0, NeedsCopy {}); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.exp new file mode 100644 index 0000000000000..9b194ced897d9 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.exp @@ -0,0 +1,127 @@ +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:8:14 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · + 8 │ $_n: NeedsCopy<$T>, + │ ^^^^^^^^^^^^^ 'copy' constraint not satisifed + · +24 │ foo!>(0, &mut 1, NeedsCopy {}); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:10:16 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · +10 │ let _: NeedsCopy<$U> = NeedsCopy {}; + │ ^^^^^^^^^^^^^ 'copy' constraint not satisifed + · +26 │ foo!(0, &mut 1, NeedsCopy {}); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:10:32 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · +10 │ let _: NeedsCopy<$U> = NeedsCopy {}; + │ ^^^^^^^^^^^^ 'copy' constraint not satisifed + · +26 │ foo!(0, &mut 1, NeedsCopy {}); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:14:9 + │ +14 │ NeedsCopy {} + │ ^^^^^^^^^^^^ Could not infer this type. Try adding an annotation + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:17:27 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · +17 │ macro fun ret2<$T>(): NeedsCopy<$T> { + │ ^^^^^^^^^^^^^ 'copy' constraint not satisifed + · +28 │ ret2!(); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:18:9 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · +18 │ NeedsCopy {} + │ ^^^^^^^^^^^^ 'copy' constraint not satisifed + · +28 │ ret2!(); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:24:9 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + · + 5 │ macro fun foo<$T: copy, $U>( + │ ---- 'copy' constraint declared here + · +24 │ foo!>(0, &mut 1, NeedsCopy {}); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ + │ │ The type 'a::m::None' does not have the ability 'copy' + │ 'copy' constraint not satisifed + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:25:19 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · +25 │ foo!>(0, &mut 1, NeedsCopy {}); + │ ^^^^^^^^^^^^^^^ + │ │ │ + │ │ The type 'a::m::None' does not have the ability 'copy' + │ 'copy' constraint not satisifed + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:27:9 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'drop' ability would need to be added here + · +27 │ ret!(); + │ ^^^^^^^^^^^^ + │ │ │ + │ │ The type 'a::m::None' does not have the ability 'drop' + │ Cannot ignore values without the 'drop' ability. The value must be used + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints.move:27:14 + │ +27 │ ret!(); + │ -----^^^^--- + │ │ │ + │ │ Invalid type annotation + │ │ Expected: 'a::m::None' + │ Given: 'a::m::NeedsCopy<_>' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.move new file mode 100644 index 0000000000000..fbc502cd2ab71 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints.move @@ -0,0 +1,30 @@ +module a::m { + public struct None() + public struct NeedsCopy {} has copy, drop, store; + + macro fun foo<$T: copy, $U>( + $_a: u64, + $_r: &mut u64, + $_n: NeedsCopy<$T>, + ) { + let _: NeedsCopy<$U> = NeedsCopy {}; + } + + macro fun ret<$T>(): $T { + NeedsCopy {} + } + + macro fun ret2<$T>(): NeedsCopy<$T> { + NeedsCopy {} + } + + #[allow(dead_code)] + fun t() { + // type args don't satisify constraints + foo!>(0, &mut 1, NeedsCopy {}); + foo!>(0, &mut 1, NeedsCopy {}); + foo!(0, &mut 1, NeedsCopy {}); + ret!(); + ret2!(); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.exp new file mode 100644 index 0000000000000..2de9b2db7207d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.exp @@ -0,0 +1,51 @@ +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move:7:26 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · + 7 │ macro fun bar<$T>(_: NeedsCopy<$T>) {} + │ ^^^^^^^^^^^^^ 'copy' constraint not satisifed + · +14 │ bar!(NeedsCopy {}); + │ ---- The type 'a::m::None' does not have the ability 'copy' + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move:9:26 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + 3 │ public struct NeedsCopy {} has copy, drop, store; + │ ---- 'copy' constraint declared here + · + 9 │ macro fun baz<$T>(): NeedsCopy<$T> { abort 0 } + │ ^^^^^^^^^^^^^ 'copy' constraint not satisifed + · +18 │ baz!(); // TODO do not complain about dead code? + │ ---- The type 'a::m::None' does not have the ability 'copy' + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move:9:42 + │ +9 │ macro fun baz<$T>(): NeedsCopy<$T> { abort 0 } + │ ^^^^^^^ Expected a value. Any code surrounding or after this expression will not be reached + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move:13:9 + │ + 2 │ public struct None() + │ ---- To satisfy the constraint, the 'copy' ability would need to be added here + · + 5 │ macro fun foo<$T: copy>() {} + │ ---- 'copy' constraint declared here + · +13 │ foo!(); + │ ^^^^^^^^^^^^ + │ │ │ + │ │ The type 'a::m::None' does not have the ability 'copy' + │ 'copy' constraint not satisifed + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move new file mode 100644 index 0000000000000..63a5aa7d2fc30 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_invalid_constraints_simple.move @@ -0,0 +1,21 @@ +module a::m { + public struct None() + public struct NeedsCopy {} has copy, drop, store; + + macro fun foo<$T: copy>() {} + + macro fun bar<$T>(_: NeedsCopy<$T>) {} + + macro fun baz<$T>(): NeedsCopy<$T> { abort 0 } + + #[allow(dead_code)] + fun t() { + foo!(); + bar!(NeedsCopy {}); + } + + fun t2() { + baz!(); // TODO do not complain about dead code? + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_valid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_valid.move new file mode 100644 index 0000000000000..f02a23b8f4c93 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_types_checked_valid.move @@ -0,0 +1,17 @@ +module a::m { + public struct None() + public struct NeedsCopy {} has copy, drop, store; + + macro fun foo<$T: copy, $U>( + $_a: u64, + $_r: &mut u64, + $_n: NeedsCopy<$T>, + ) { + let _: NeedsCopy<$U> = NeedsCopy {}; + } + + #[allow(dead_code)] + fun t() { + foo!>(0, &mut 1, NeedsCopy {}); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked.move new file mode 100644 index 0000000000000..bee450ed61ca2 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked.move @@ -0,0 +1,16 @@ +module a::m { + + fun private() {} + public(package) fun package() {} + + // these should be errors if we checked macros bodies before expanding + + public macro fun t0() { + package(); + private(); + } + + public(package) fun t1() { + private(); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.exp new file mode 100644 index 0000000000000..0ba6027f7ba0a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.exp @@ -0,0 +1,36 @@ +error[E04001]: restricted visibility + ┌─ tests/move_2024/typing/macros_visibility_not_checked_called.move:9:9 + │ +4 │ public(package) fun package() {} + │ --------------- A 'public(package)' function can only be called from the same address and package as module 'a::m' in package ''. This call is from address 'b' in package '' + · +9 │ package(); + │ ^^^^^^^^^ Invalid call to 'public(package)' visible function 'a::m::package' + +error[E04001]: restricted visibility + ┌─ tests/move_2024/typing/macros_visibility_not_checked_called.move:10:9 + │ + 3 │ fun private() {} + │ ------- This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module + · +10 │ private(); + │ ^^^^^^^^^ Invalid call to internal function 'a::m::private' + +error[E04001]: restricted visibility + ┌─ tests/move_2024/typing/macros_visibility_not_checked_called.move:14:9 + │ + 3 │ fun private() {} + │ ------- This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module + · +14 │ private(); + │ ^^^^^^^^^ Invalid call to internal function 'a::m::private' + +error[E04001]: restricted visibility + ┌─ tests/move_2024/typing/macros_visibility_not_checked_called.move:30:9 + │ +13 │ public(package) macro fun t1() { + │ --------------- A 'public(package)' function can only be called from the same address and package as module 'a::m' in package ''. This call is from address 'b' in package '' + · +30 │ a::m::t1!(); + │ ^^^^^^^^^^^ Invalid call to 'public(package)' visible function 'a::m::t1' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.move new file mode 100644 index 0000000000000..91ef27a1cfc57 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/macros_visibility_not_checked_called.move @@ -0,0 +1,32 @@ +module a::m { + + fun private() {} + public(package) fun package() {} + + // these will give errors after expanding + + public macro fun t0() { + package(); + private(); + } + + public(package) macro fun t1() { + private(); + } +} + +// same package +module a::other { + public fun call() { + a::m::t0!(); + a::m::t1!(); + } +} + +// different package +module b::other { + public fun call() { + a::m::t0!(); + a::m::t1!(); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.exp new file mode 100644 index 0000000000000..0c315db414e81 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.exp @@ -0,0 +1,27 @@ +error[E04016]: too few arguments + ┌─ tests/move_2024/typing/mismatched_lambda_arity.move:3:9 + │ +3 │ $f(); + │ ^^^^ + │ │ │ + │ │ Found 0 argument(s) here + │ Invalid lambda call of '$f'. The call expected 2 argument(s) but got 0 + +error[E04016]: too few arguments + ┌─ tests/move_2024/typing/mismatched_lambda_arity.move:4:9 + │ +4 │ $f(0); + │ ^^^^^ + │ │ │ + │ │ Found 1 argument(s) here + │ Invalid lambda call of '$f'. The call expected 2 argument(s) but got 1 + +error[E04017]: too many arguments + ┌─ tests/move_2024/typing/mismatched_lambda_arity.move:5:9 + │ +5 │ $f(0, 1, 2); + │ ^^^^^^^^^^^ + │ │ │ + │ │ Found 3 argument(s) here + │ Invalid lambda call of '$f'. The call expected 2 argument(s) but got 3 + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.move new file mode 100644 index 0000000000000..7d6b61a62f35d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/mismatched_lambda_arity.move @@ -0,0 +1,11 @@ +module a::m { + macro fun foo($f: |u64, u64| -> u64) { + $f(); + $f(0); + $f(0, 1, 2); + } + + fun t() { + foo!(|x, y| x + y) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/module_call_visibility_package_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/module_call_visibility_package_invalid.exp index c9f56664072f1..93281c2a9c367 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/typing/module_call_visibility_package_invalid.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/module_call_visibility_package_invalid.exp @@ -5,7 +5,7 @@ error[E04001]: restricted visibility │ --------------- A 'public(package)' function can only be called from the same address and package as module '0x42::mod_0' in package ''. This call is from address '0x54' in package '' · 10 │ public(package) fun f_package_call_package() { mod_0::f_pkg_public() } - │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::mod_0::f_pkg_public' + │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function '0x42::mod_0::f_pkg_public' error[E04001]: restricted visibility ┌─ tests/move_2024/typing/module_call_visibility_package_invalid.move:11:42 @@ -14,7 +14,7 @@ error[E04001]: restricted visibility │ --------------- A 'public(package)' function can only be called from the same address and package as module '0x42::mod_0' in package ''. This call is from address '0x54' in package '' · 11 │ public fun f_public_call_package() { mod_0::f_pkg_public() } - │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::mod_0::f_pkg_public' + │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function '0x42::mod_0::f_pkg_public' error[E04001]: restricted visibility ┌─ tests/move_2024/typing/module_call_visibility_package_invalid.move:12:36 @@ -23,7 +23,7 @@ error[E04001]: restricted visibility │ --------------- A 'public(package)' function can only be called from the same address and package as module '0x42::mod_0' in package ''. This call is from address '0x54' in package '' · 12 │ fun f_private_call_package() { mod_0::f_pkg_public() } - │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::mod_0::f_pkg_public' + │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function '0x42::mod_0::f_pkg_public' error[E04001]: restricted visibility ┌─ tests/move_2024/typing/module_call_visibility_package_invalid.move:19:50 @@ -32,7 +32,7 @@ error[E04001]: restricted visibility │ --------------- A 'public(package)' function can only be called from the same address and package as module '0x42::mod_0' in package ''. This call is from address '0x54' in package '' · 19 │ public(friend) fun f_friend_call_package() { mod_0::f_pkg_public() } - │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::mod_0::f_pkg_public' + │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function '0x42::mod_0::f_pkg_public' error[E04001]: restricted visibility ┌─ tests/move_2024/typing/module_call_visibility_package_invalid.move:20:42 @@ -41,7 +41,7 @@ error[E04001]: restricted visibility │ --------------- A 'public(package)' function can only be called from the same address and package as module '0x42::mod_0' in package ''. This call is from address '0x54' in package '' · 20 │ public fun f_public_call_package() { mod_0::f_pkg_public() } - │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::mod_0::f_pkg_public' + │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function '0x42::mod_0::f_pkg_public' error[E04001]: restricted visibility ┌─ tests/move_2024/typing/module_call_visibility_package_invalid.move:21:36 @@ -50,5 +50,5 @@ error[E04001]: restricted visibility │ --------------- A 'public(package)' function can only be called from the same address and package as module '0x42::mod_0' in package ''. This call is from address '0x54' in package '' · 21 │ fun f_private_call_package() { mod_0::f_pkg_public() } - │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::mod_0::f_pkg_public' + │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call to 'public(package)' visible function '0x42::mod_0::f_pkg_public' diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.exp new file mode 100644 index 0000000000000..d2ab526ed6416 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.exp @@ -0,0 +1,42 @@ +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move:8:9 + │ + 4 │ bar!(foo!($f)) + │ -------------- From this macro expansion + · + 8 │ foo!(bar!($f)) + │ ^^^^^^^^^^^^^^ Recursive macro expansion. 'a::m::foo' cannot recursively expand itself + · +28 │ foo!(0); + │ ------- 'a::m::foo' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move:12:14 + │ +12 │ $f + arg!($f) + │ ^^^^^^^^ Recursive macro expansion. 'a::m::arg' cannot recursively expand itself + · +29 │ arg!({ let x = 0; x }); + │ ---------------------- 'a::m::arg' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move:16:14 + │ +16 │ $f + arg_eta!({ $f }) + │ ^^^^^^^^^^^^^^^^ Recursive macro expansion. 'a::m::arg_eta' cannot recursively expand itself + · +30 │ arg_eta!({ let x = 0; x }); + │ -------------------------- 'a::m::arg_eta' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move:20:23 + │ +20 │ $f + apply!({ arg_apply!($f) }) + │ ---------^^^^^^^^^^^^^^--- + │ │ │ + │ │ Recursive macro expansion. 'a::m::arg_apply' cannot recursively expand itself + │ From this macro expansion + · +31 │ arg_apply!({ let x = 0; x }); + │ ---------------------------- 'a::m::arg_apply' previously expanded here + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move new file mode 100644 index 0000000000000..bc8b60f7ad92a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_invalid.move @@ -0,0 +1,33 @@ +module a::m { + // invalid cycle through by-name arguments + macro fun foo($f: u64): u64 { + bar!(foo!($f)) + } + + macro fun bar($f: u64): u64 { + foo!(bar!($f)) + } + + macro fun arg($f: u64): u64 { + $f + arg!($f) + } + + macro fun arg_eta($f: u64): u64 { + $f + arg_eta!({ $f }) + } + + macro fun arg_apply($f: u64): u64 { + $f + apply!({ arg_apply!($f) }) + } + + macro fun apply($f: u64): u64 { + $f + } + + fun t() { + foo!(0); + arg!({ let x = 0; x }); + arg_eta!({ let x = 0; x }); + arg_apply!({ let x = 0; x }); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_valid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_valid.move new file mode 100644 index 0000000000000..783cbb22163f1 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_by_name_arg_valid.move @@ -0,0 +1,18 @@ +module a::m { + macro fun apply1($f: u64): u64 { + $f + $f + } + + macro fun apply2($f: u64): u64 { + $f + $f + } + + + fun t() { + // macros can call themselves, as long as the number of calls is finite/explicit + apply1!(apply1!(0)); + apply1!(apply1!(apply1!(0))); + apply1!(apply1!(apply1!(apply1!(0)))); + apply2!(apply1!(apply1!(apply2!(1)))); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.exp new file mode 100644 index 0000000000000..bc5859f111781 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.exp @@ -0,0 +1,48 @@ +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid.move:4:13 + │ + 4 │ 1 + self_cyle!() + │ ^^^^^^^^^^^^ Recursive macro expansion. 'a::m::self_cyle' cannot recursively expand itself + · +39 │ self_cyle!(); + │ ------------ 'a::m::self_cyle' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid.move:17:9 + │ + 9 │ cycle2!() + │ --------- From this macro expansion + · +13 │ cycle3!() + │ --------- From this macro expansion + · +17 │ cycle1!() + │ ^^^^^^^^^ Recursive macro expansion. 'a::m::cycle1' cannot recursively expand itself + · +40 │ cycle1!(); + │ --------- 'a::m::cycle1' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid.move:22:19 + │ +22 │ apply!(|| cycle_app!(|| $f())) + │ ----------^^^^^^^^^^^^^^^^^^^- + │ │ │ + │ │ Recursive macro expansion. 'a::m::cycle_app' cannot recursively expand itself + │ From this macro expansion + · +41 │ cycle_app!(|| 1); + │ ---------------- 'a::m::cycle_app' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid.move:31:18 + │ +31 │ by_name!(cycle_by_name!($f)) + │ ---------^^^^^^^^^^^^^^^^^^- + │ │ │ + │ │ Recursive macro expansion. 'a::m::cycle_by_name' cannot recursively expand itself + │ From this macro expansion + · +42 │ cycle_by_name!(1); + │ ----------------- 'a::m::cycle_by_name' previously expanded here + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.move new file mode 100644 index 0000000000000..26a38823d15a6 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid.move @@ -0,0 +1,44 @@ +module a::m { + // invalid cycle + macro fun self_cyle(): u64 { + 1 + self_cyle!() + } + + // invalid cycle of more than 1 node + macro fun cycle1(): u64 { + cycle2!() + } + + macro fun cycle2(): u64 { + cycle3!() + } + + macro fun cycle3(): u64 { + cycle1!() + } + + // invalid cycle through lambda + macro fun cycle_app($f: || -> u64): u64 { + apply!(|| cycle_app!(|| $f())) + } + + macro fun apply($f: || -> u64): u64 { + $f() + } + + // invalid cycle through by-name arg + macro fun cycle_by_name($f: u64): u64 { + by_name!(cycle_by_name!($f)) + } + + macro fun by_name($f: u64): u64 { + $f + } + + fun t() { + self_cyle!(); + cycle1!(); + cycle_app!(|| 1); + cycle_by_name!(1); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.exp new file mode 100644 index 0000000000000..bab476467fa9e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.exp @@ -0,0 +1,42 @@ +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid_lambdas.move:8:9 + │ + 4 │ bar!(|| foo!($f)) + │ ----------------- From this macro expansion + · + 8 │ foo!(|| bar!($f)) + │ ^^^^^^^^^^^^^^^^^ Recursive macro expansion. 'a::m::foo' cannot recursively expand itself + · +28 │ foo!(|| 0); + │ ---------- 'a::m::foo' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid_lambdas.move:12:12 + │ +12 │ $f(arg!($f)) + │ ^^^^^^^^ Recursive macro expansion. 'a::m::arg' cannot recursively expand itself + · +29 │ arg!(|x| x); + │ ----------- 'a::m::arg' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid_lambdas.move:16:12 + │ +16 │ $f(arg_eta!(|x| $f(x))) + │ ^^^^^^^^^^^^^^^^^^^ Recursive macro expansion. 'a::m::arg_eta' cannot recursively expand itself + · +30 │ arg_eta!(|x| x); + │ --------------- 'a::m::arg_eta' previously expanded here + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/recursive_macros_invalid_lambdas.move:20:22 + │ +20 │ $f(apply!(|| arg_apply!($f))) + │ ----------^^^^^^^^^^^^^^- + │ │ │ + │ │ Recursive macro expansion. 'a::m::arg_apply' cannot recursively expand itself + │ From this macro expansion + · +31 │ arg_apply!(|x| x); + │ ----------------- 'a::m::arg_apply' previously expanded here + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.move new file mode 100644 index 0000000000000..d6fdc5a50ba56 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_invalid_lambdas.move @@ -0,0 +1,33 @@ +module a::m { + // invalid cycle through lambda + macro fun foo($f: || -> u64): u64 { + bar!(|| foo!($f)) + } + + macro fun bar($f: || -> u64): u64 { + foo!(|| bar!($f)) + } + + macro fun arg($f: |u64| -> u64): u64 { + $f(arg!($f)) + } + + macro fun arg_eta($f: |u64| -> u64): u64 { + $f(arg_eta!(|x| $f(x))) + } + + macro fun arg_apply($f: |u64| -> u64): u64 { + $f(apply!(|| arg_apply!($f))) + } + + macro fun apply($f: || -> u64): u64 { + $f() + } + + fun t() { + foo!(|| 0); + arg!(|x| x); + arg_eta!(|x| x); + arg_apply!(|x| x); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_valid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_valid.move new file mode 100644 index 0000000000000..0a61957969db9 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/recursive_macros_valid.move @@ -0,0 +1,18 @@ +module a::m { + macro fun apply1($f: || -> u64): u64 { + $f() + } + + macro fun apply2($f: || -> u64): u64 { + $f() + } + + + fun t() { + // macros can call themselves, as long as the number of calls is finite/explicit + apply1!(|| apply1!(|| 0)); + apply1!(|| apply1!(|| apply1!(|| 0))); + apply1!(|| apply1!(|| apply1!(|| apply1!(|| 0)))); + apply2!(|| apply1!(|| apply1!(|| apply2!(|| 1)))); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_let_mut_leading_underscore.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_let_mut_leading_underscore.move new file mode 100644 index 0000000000000..064f72bfdae0d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_let_mut_leading_underscore.move @@ -0,0 +1,6 @@ +module a::m { + // suppress unused mut with a leading _ + fun foo(mut _x: u64) { + let mut _y = 0; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.exp new file mode 100644 index 0000000000000..66cf7d6eb8ff5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.exp @@ -0,0 +1,32 @@ +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg.move:12:17 + │ +12 │ ignore!(None(), || (), None(), || ()); + │ ^^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg.move:12:25 + │ +12 │ ignore!(None(), || (), None(), || ()); + │ ^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg.move:12:32 + │ +12 │ ignore!(None(), || (), None(), || ()); + │ ^^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg.move:12:40 + │ +12 │ ignore!(None(), || (), None(), || ()); + │ ^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.move new file mode 100644 index 0000000000000..ff2cf6f956867 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg.move @@ -0,0 +1,14 @@ +module a::m { + public struct None() + + macro fun ignore( + _: None, + _: ||, + $_n: None, + $_f: ||, + ) {} + + fun t() { + ignore!(None(), || (), None(), || ()); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.exp new file mode 100644 index 0000000000000..06210a7b85891 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.exp @@ -0,0 +1,14 @@ +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/unused_macro_arg_affects_inference.move:14:9 + │ +14 │ X().foo!(None()) + │ ^^^ Could not infer this type. Try adding an annotation + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg_affects_inference.move:14:18 + │ +14 │ X().foo!(None()) + │ ^^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.move new file mode 100644 index 0000000000000..7089374663796 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_affects_inference.move @@ -0,0 +1,16 @@ +module a::m { + public struct X() has copy, drop; + public struct None() + + + macro fun foo<$T, $U: copy>( + _: X<$T>, + _: $T, + ) { + } + + fun t() { + // we cannot infer the type argument because the None() is not used + X().foo!(None()) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.exp new file mode 100644 index 0000000000000..8e2fa596f0fed --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.exp @@ -0,0 +1,32 @@ +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg_invalid_constraint.move:16:21 + │ +16 │ needs_copy!(X(), None(), X(), None()); + │ ^^^^^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg_invalid_constraint.move:16:32 + │ +16 │ needs_copy!(X(), None(), X(), None()); + │ ^^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg_invalid_constraint.move:16:40 + │ +16 │ needs_copy!(X(), None(), X(), None()); + │ ^^^^^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/unused_macro_arg_invalid_constraint.move:16:51 + │ +16 │ needs_copy!(X(), None(), X(), None()); + │ ^^^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.move new file mode 100644 index 0000000000000..6db93f8d6e3a5 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_invalid_constraint.move @@ -0,0 +1,18 @@ +module a::m { + public struct X() has copy, drop; + public struct None() + + + macro fun needs_copy<$T, $U: copy>( + _: $T, + _: $U, + $_t: $T, + $_u: $U, + ) { + } + + fun t() { + // these would all give constraint errors if they were used + needs_copy!(X(), None(), X(), None()); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.exp new file mode 100644 index 0000000000000..b8ef1fad55728 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.exp @@ -0,0 +1,12 @@ +error[E05001]: ability constraint not satisfied + ┌─ tests/move_2024/typing/unused_macro_arg_method_call.move:5:9 + │ +2 │ public struct None() + │ ---- To satisfy the constraint, the 'drop' ability would need to be added here + · +5 │ _: None, + │ ^ Cannot ignore values without the 'drop' ability. The value must be used + · +9 │ None().ignore!() + │ ------ The type 'a::m::None' does not have the ability 'drop' + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.move new file mode 100644 index 0000000000000..2ab5bfb5d7a8e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_macro_arg_method_call.move @@ -0,0 +1,11 @@ +module a::m { + public struct None() + + macro fun ignore( + _: None, + ) {} + + fun t() { + None().ignore!() + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.exp new file mode 100644 index 0000000000000..d568ee00fa9cc --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.exp @@ -0,0 +1,32 @@ +warning[W09001]: unused alias + ┌─ tests/move_2024/typing/unused_use_fun.move:5:5 + │ +5 │ use fun foo as u64.f; + │ ^^^^^^^^^^^^^^^^^^^^^ Unused 'use fun' of 'u64.f'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/typing/unused_use_fun.move:8:9 + │ +8 │ use fun foo as u64.f; + │ ^^^^^^^^^^^^^^^^^^^^^ Unused 'use fun' of 'u64.f'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/typing/unused_use_fun.move:19:23 + │ +19 │ use a::x::drop as f; + │ ^ Unused 'use' of alias 'f'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/typing/unused_use_fun.move:22:27 + │ +22 │ use a::x::drop as f; + │ ^ Unused 'use' of alias 'f'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.move new file mode 100644 index 0000000000000..0bbfd19053c23 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun.move @@ -0,0 +1,24 @@ +// explicit unused +module a::m { + fun foo(_: u64) {} + + use fun foo as u64.f; + + fun t() { + use fun foo as u64.f; + } +} + +// implicit unused method alias +module a::x { + public struct X() has drop; + public fun drop(_: X) {} +} + +module b::other { + use a::x::drop as f; + + fun t() { + use a::x::drop as f; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.exp new file mode 100644 index 0000000000000..11383880d32dc --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.exp @@ -0,0 +1,16 @@ +warning[W09001]: unused alias + ┌─ tests/move_2024/typing/unused_use_fun_silenced_by_macros.move:9:9 + │ +9 │ use fun foo as u64.f; + │ ^^^^^^^^^^^^^^^^^^^^^ Unused 'use fun' of 'u64.f'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[W09001]: unused alias + ┌─ tests/move_2024/typing/unused_use_fun_silenced_by_macros.move:27:27 + │ +27 │ use a::x::drop as f; + │ ^ Unused 'use' of alias 'f'. Consider removing it + │ + = This warning can be suppressed with '#[allow(unused_use)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.move new file mode 100644 index 0000000000000..15a3984516312 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/unused_use_fun_silenced_by_macros.move @@ -0,0 +1,43 @@ +// explicit unused, silenced by a public macro +module a::m { + fun foo(_: u64) {} + + use fun foo as u64.f; + + fun t() { + // this one will still be unused since it is not in the module scope + use fun foo as u64.f; + } + + // this silences the unused use fun in the module, since we lose track its non-local usage + public(package) macro fun some_macro() {} +} + +// implicit unused method alias, silenced by a public macro +module a::x { + public struct X() has drop; + public fun drop(_: X) {} +} + +module b::other { + use a::x::drop as f; + + fun t() { + // this one will still be unused since it is not in the module scope + use a::x::drop as f; + } + + // this silences the unused use fun in the module, since we lose track its non-local usage + public macro fun some_macro() {} +} + +// explicit and implicit method alias, silenced by a private macro +module a::another { + use a::x::drop as fdrop; + + fun foo(_: u64) {} + + use fun foo as u64.foo; + + macro fun f() {} +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.exp new file mode 100644 index 0000000000000..3eb67c1438236 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.exp @@ -0,0 +1,36 @@ +error[E04031]: invalid usage of lambda + ┌─ tests/move_2024/typing/use_lambda_outside_call_invalid.move:3:17 + │ +3 │ let x = $f; + │ ^^ Lambdas can only be used directly as arguments to 'macro' functions + +error[E02010]: invalid name + ┌─ tests/move_2024/typing/use_lambda_outside_call_invalid.move:7:23 + │ +7 │ macro fun bar(mut f: || -> u64) { + │ ----- ^ Invalid parameter name 'f'. 'macro' parameter names must start with '$' (or must be '_') + │ │ + │ Declared 'macro' here + │ + = 'macro' parameters start with '$' to indicate that their arguments are not evaluated before the macro is expanded, meaning the entire expression is substituted. This is different from regular function parameters that are evaluated before the function is called. + +error[E04031]: invalid usage of lambda + ┌─ tests/move_2024/typing/use_lambda_outside_call_invalid.move:8:13 + │ +8 │ f = || 0; + │ ^^^^ Lambdas can only be used directly as arguments to 'macro' functions + +warning[W09005]: dead or unreachable code + ┌─ tests/move_2024/typing/use_lambda_outside_call_invalid.move:17:14 + │ +17 │ bar!(|| 0); + │ ^^^^ Unused macro argument. Its expression will not be type checked and it will not evaluated + │ + = This warning can be suppressed with '#[allow(dead_code)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +error[E04032]: unable to expand macro function + ┌─ tests/move_2024/typing/use_lambda_outside_call_invalid.move:18:14 + │ +18 │ foo!(baz!(|| || 0)); + │ ^^^^^^^^^^^^^ Unable to bind lambda to parameter '$f'. The lambda must be passed directly + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.move new file mode 100644 index 0000000000000..4f03d4ad9ecd0 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_invalid.move @@ -0,0 +1,20 @@ +module a::m { + macro fun foo($f: || -> u64) { + let x = $f; + x(); + } + + macro fun bar(mut f: || -> u64) { + f = || 0; + } + + macro fun baz($f: || -> || -> u64): || -> u64 { + $f() + } + + fun t() { + foo!(|| 0); + bar!(|| 0); + foo!(baz!(|| || 0)); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_valid.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_valid.move new file mode 100644 index 0000000000000..cf3c3079ae68a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/use_lambda_outside_call_valid.move @@ -0,0 +1,41 @@ +module a::m { + + // chain of calls + macro fun apply1($f: || -> u64): u64 { + $f() + } + + macro fun apply2($f: || -> u64): u64 { + apply1!($f) + } + + macro fun apply3($f: || -> u64): u64 { + apply2!(|| apply2!($f)) + } + + macro fun apply_all($f: || -> u64): u64 { + apply3!(|| apply2!(|| apply1!($f))) + } + + // double lambdas + macro fun dub1($f: || -> u64, $g: || -> u64): u64 { + $f() + $g() + } + + macro fun dub2($f: || -> u64, $g: || -> u64): u64 { + dub1!($f, || $g()) + + dub1!(|| $f(), $g) + + dub1!(|| dub1!($f, $g), || dub1!($f, $g)) + + dub1!(|| dub1!($f, || $g()), || dub1!(|| $f(), $g)) + + dub1!(|| dub1!(|| $f(), || $g()), || dub1!(|| $f(), || $g())) + } + + fun t() { + apply1!(|| 0); + apply2!(|| 0); + apply3!(|| 0); + apply_all!(|| 0); + dub1!(|| 0, || 0); + dub2!(|| 0, || 0); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/expansion/invalid_local_name.exp b/external-crates/move/crates/move-compiler/tests/move_check/expansion/invalid_local_name.exp index ce39eb6ab6e06..b4d77bfc7d38d 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/expansion/invalid_local_name.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/expansion/invalid_local_name.exp @@ -2,7 +2,7 @@ error[E02010]: invalid name ┌─ tests/move_check/expansion/invalid_local_name.move:4:11 │ 4 │ fun t(No: u64) { - │ ^^ Invalid local variable name 'No'. Local variable names must start with 'a'..'z' (or '_') + │ ^^ Invalid parameter name 'No'. Local variable names must start with 'a'..'z' or '_' error[E03005]: unbound unscoped name ┌─ tests/move_check/expansion/invalid_local_name.move:5:9 @@ -14,13 +14,13 @@ error[E02010]: invalid name ┌─ tests/move_check/expansion/invalid_local_name.move:9:13 │ 9 │ let No; - │ ^^ Invalid local variable name 'No'. Local variable names must start with 'a'..'z' (or '_') + │ ^^ Invalid local variable name 'No'. Local variable names must start with 'a'..'z' or '_' error[E02010]: invalid name ┌─ tests/move_check/expansion/invalid_local_name.move:14:13 │ 14 │ let No = 100; - │ ^^ Invalid local variable name 'No'. Local variable names must start with 'a'..'z' (or '_') + │ ^^ Invalid local variable name 'No'. Local variable names must start with 'a'..'z' or '_' error[E03005]: unbound unscoped name ┌─ tests/move_check/expansion/invalid_local_name.move:15:13 diff --git a/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.exp b/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.exp new file mode 100644 index 0000000000000..50370b7b64bdb --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.exp @@ -0,0 +1,156 @@ +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:4:9 + │ +4 │ _, + │ ^ Invalid type parameter name '_'. '_' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:5:9 + │ +5 │ address, + │ ^^^^^^^ Invalid type parameter name 'address'. 'address' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:6:9 + │ +6 │ signer, + │ ^^^^^^ Invalid type parameter name 'signer'. 'signer' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:7:9 + │ +7 │ u8, + │ ^^ Invalid type parameter name 'u8'. 'u8' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:8:9 + │ +8 │ u16, + │ ^^^ Invalid type parameter name 'u16'. 'u16' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:9:9 + │ +9 │ u32, + │ ^^^ Invalid type parameter name 'u32'. 'u32' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:10:9 + │ +10 │ u64, + │ ^^^ Invalid type parameter name 'u64'. 'u64' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:11:9 + │ +11 │ u128, + │ ^^^^ Invalid type parameter name 'u128'. 'u128' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:12:9 + │ +12 │ u256, + │ ^^^^ Invalid type parameter name 'u256'. 'u256' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:13:9 + │ +13 │ vector, + │ ^^^^^^ Invalid type parameter name 'vector'. 'vector' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:14:9 + │ +14 │ freeze, + │ ^^^^^^ Invalid type parameter name 'freeze'. 'freeze' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:15:9 + │ +15 │ assert, + │ ^^^^^^ Invalid type parameter name 'assert'. 'assert' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:16:9 + │ +16 │ Self, + │ ^^^^ Invalid type parameter name 'Self'. 'Self' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:19:9 + │ +19 │ _, + │ ^ Invalid type parameter name '_'. '_' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:20:9 + │ +20 │ address, + │ ^^^^^^^ Invalid type parameter name 'address'. 'address' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:21:9 + │ +21 │ signer, + │ ^^^^^^ Invalid type parameter name 'signer'. 'signer' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:22:9 + │ +22 │ u8, + │ ^^ Invalid type parameter name 'u8'. 'u8' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:23:9 + │ +23 │ u16, + │ ^^^ Invalid type parameter name 'u16'. 'u16' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:24:9 + │ +24 │ u32, + │ ^^^ Invalid type parameter name 'u32'. 'u32' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:25:9 + │ +25 │ u64, + │ ^^^ Invalid type parameter name 'u64'. 'u64' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:26:9 + │ +26 │ u128, + │ ^^^^ Invalid type parameter name 'u128'. 'u128' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:27:9 + │ +27 │ u256, + │ ^^^^ Invalid type parameter name 'u256'. 'u256' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:28:9 + │ +28 │ vector, + │ ^^^^^^ Invalid type parameter name 'vector'. 'vector' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:29:9 + │ +29 │ freeze, + │ ^^^^^^ Invalid type parameter name 'freeze'. 'freeze' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:30:9 + │ +30 │ assert, + │ ^^^^^^ Invalid type parameter name 'assert'. 'assert' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_check/expansion/restricted_type_parameter_names.move:31:9 + │ +31 │ Self, + │ ^^^^ Invalid type parameter name 'Self'. 'Self' is restricted and cannot be used to name a type parameter + diff --git a/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.move b/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.move new file mode 100644 index 0000000000000..edb3dc27d7ef2 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names.move @@ -0,0 +1,33 @@ +#[allow(unused_type_parameter)] +module a::m { + struct S< + _, + address, + signer, + u8, + u16, + u32, + u64, + u128, + u256, + vector, + freeze, + assert, + Self, + > {} + fun foo_< + _, + address, + signer, + u8, + u16, + u32, + u64, + u128, + u256, + vector, + freeze, + assert, + Self, + >() {} +} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names_peculiar.move b/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names_peculiar.move new file mode 100644 index 0000000000000..522489229cf0e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/expansion/restricted_type_parameter_names_peculiar.move @@ -0,0 +1,16 @@ +#[allow(unused_type_parameter)] +module a::m { + // none of these are invlaid ... but they are weird + struct S< + __, + _u64, + _T, + x, + > {} + fun foo_< + __, + _u64, + _T, + x, + >() {} +} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.exp b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.exp new file mode 100644 index 0000000000000..28e447cbc6c2b --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.exp @@ -0,0 +1,29 @@ +error[E04007]: incompatible types + ┌─ tests/move_check/feature_gate/macro_call.move:4:9 + │ +2 │ public fun foo(_: u64) {} + │ --- Expected: 'u64' +3 │ fun bar() { +4 │ foo!(|| ()) + │ ^^^^^^^^^^^ + │ │ │ + │ │ Given: '|| -> _' + │ Invalid call of 'a::m::foo'. Invalid argument for parameter '_' + +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/feature_gate/macro_call.move:4:12 + │ +4 │ foo!(|| ()) + │ ^ 'macro' functions are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E04029]: invalid function call + ┌─ tests/move_check/feature_gate/macro_call.move:4:12 + │ +2 │ public fun foo(_: u64) {} + │ --- Normal (non-'macro') function is declared here +3 │ fun bar() { +4 │ foo!(|| ()) + │ ^ 'foo' is not a macro function and cannot be called with a `!`. Try replacing with 'foo' + diff --git a/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.move b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.move new file mode 100644 index 0000000000000..ae7e3f51cc901 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_call.move @@ -0,0 +1,6 @@ +module a::m { + public fun foo(_: u64) {} + fun bar() { + foo!(|| ()) + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.exp b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.exp new file mode 100644 index 0000000000000..73f1ba4da3975 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.exp @@ -0,0 +1,8 @@ +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/feature_gate/macro_definition.move:2:12 + │ +2 │ public macro fun do($f: || ()) { $f() } + │ ^^^^^ 'macro' functions are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + diff --git a/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.move b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.move new file mode 100644 index 0000000000000..3ef30370e21e8 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_definition.move @@ -0,0 +1,3 @@ +module a::m { + public macro fun do($f: || ()) { $f() } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.exp b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.exp new file mode 100644 index 0000000000000..fcc9719282974 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.exp @@ -0,0 +1,16 @@ +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/feature_gate/macro_lambda.move:2:17 + │ +2 │ fun tfun(_: |u64| u64) {} + │ ^^^^^^^^^ 'macro' functions are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/feature_gate/macro_lambda.move:4:17 + │ +4 │ let _ = |x| x; + │ ^^^^^ 'macro' functions are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + diff --git a/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.move b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.move new file mode 100644 index 0000000000000..208c8c01e2a82 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/feature_gate/macro_lambda.move @@ -0,0 +1,7 @@ +module a::m { + fun tfun(_: |u64| u64) {} + fun lambda() { + let _ = |x| x; + } + +} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/constant_native.exp b/external-crates/move/crates/move-compiler/tests/move_check/parser/constant_native.exp index 93a09b33dc350..c73dc03cf9847 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/constant_native.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/constant_native.exp @@ -2,5 +2,5 @@ error[E01003]: invalid modifier ┌─ tests/move_check/parser/constant_native.move:3:5 │ 3 │ native const Foo: u64 = 0; - │ ^^^^^^ Invalid constant declaration. 'native' constants are not supported + │ ^^^^^^ Invalid constant declaration. 'native' is used only on functions or structs diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/entry_struct.exp b/external-crates/move/crates/move-compiler/tests/move_check/parser/entry_struct.exp index db53d092459da..59d08038acdd6 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/entry_struct.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/entry_struct.exp @@ -2,5 +2,5 @@ error[E01003]: invalid modifier ┌─ tests/move_check/parser/entry_struct.move:3:5 │ 3 │ entry struct S {} - │ ^^^^^ Invalid constant declaration. 'entry' is used only on functions + │ ^^^^^ Invalid struct declaration. 'entry' is used only on functions diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.exp b/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.exp index b6e03e58f1a31..6623012bb27a4 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.exp @@ -1,40 +1,148 @@ error[E02001]: duplicate declaration, item, or annotation ┌─ tests/move_check/parser/function_conflicting_visibility.move:2:20 │ -2 │ public(friend) public(package) fun f() {} - │ -------------- ^^^^^^^^^^^^^^^ Duplicate visibility modifier +2 │ public(friend) public(package) fun t0() {} + │ -------------- ^^^^^^^^^^^^^^^ Duplicate 'public' modifier │ │ - │ Visibility modifier previously given here + │ 'public' modifier previously given here error[E13001]: feature is not supported in specified edition ┌─ tests/move_check/parser/function_conflicting_visibility.move:2:20 │ -2 │ public(friend) public(package) fun f() {} +2 │ public(friend) public(package) fun t0() {} │ ^^^^^^^^^^^^^^^ 'public(package)' is not supported by current edition 'legacy', only '2024.alpha' support this feature │ = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. error[E02016]: invalid visibility modifier - ┌─ tests/move_check/parser/function_conflicting_visibility.move:2:20 - │ -2 │ public(friend) public(package) fun f() {} - │ ^^^^^^^^^^^^^^^ Cannot mix 'package' and 'friend' visibilities in the same module -3 │ public(package) public(friend) fun g() {} - │ -------------- 'friend' visibility used here + ┌─ tests/move_check/parser/function_conflicting_visibility.move:2:20 + │ + 2 │ public(friend) public(package) fun t0() {} + │ ^^^^^^^^^^^^^^^ Cannot mix 'package' and 'friend' visibilities in the same module + · +11 │ public(friend) public(friend) fun s2() {} + │ -------------- 'friend' visibility used here error[E02001]: duplicate declaration, item, or annotation ┌─ tests/move_check/parser/function_conflicting_visibility.move:3:21 │ -3 │ public(package) public(friend) fun g() {} - │ --------------- ^^^^^^^^^^^^^^ Duplicate visibility modifier +3 │ public(package) public(friend) fun t1() {} + │ --------------- ^^^^^^^^^^^^^^ Duplicate 'public' modifier │ │ - │ Visibility modifier previously given here + │ 'public' modifier previously given here error[E02016]: invalid visibility modifier ┌─ tests/move_check/parser/function_conflicting_visibility.move:3:21 │ -2 │ public(friend) public(package) fun f() {} - │ --------------- 'public(package)' visibility used here -3 │ public(package) public(friend) fun g() {} +3 │ public(package) public(friend) fun t1() {} │ ^^^^^^^^^^^^^^ Cannot mix 'friend' and 'package' visibilities in the same module +4 │ public public(package) fun t2() {} + │ --------------- 'public(package)' visibility used here + +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_check/parser/function_conflicting_visibility.move:4:12 + │ +4 │ public public(package) fun t2() {} + │ ------ ^^^^^^^^^^^^^^^ Duplicate 'public' modifier + │ │ + │ 'public' modifier previously given here + +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/parser/function_conflicting_visibility.move:4:12 + │ +4 │ public public(package) fun t2() {} + │ ^^^^^^^^^^^^^^^ 'public(package)' is not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E02016]: invalid visibility modifier + ┌─ tests/move_check/parser/function_conflicting_visibility.move:4:12 + │ + 4 │ public public(package) fun t2() {} + │ ^^^^^^^^^^^^^^^ Cannot mix 'package' and 'friend' visibilities in the same module + · +11 │ public(friend) public(friend) fun s2() {} + │ -------------- 'friend' visibility used here + +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_check/parser/function_conflicting_visibility.move:5:21 + │ +5 │ public(package) public fun t3() {} + │ --------------- ^^^^^^ Duplicate 'public' modifier + │ │ + │ 'public' modifier previously given here + +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_check/parser/function_conflicting_visibility.move:6:12 + │ +6 │ public public(friend) fun t4() {} + │ ------ ^^^^^^^^^^^^^^ Duplicate 'public' modifier + │ │ + │ 'public' modifier previously given here + +error[E02016]: invalid visibility modifier + ┌─ tests/move_check/parser/function_conflicting_visibility.move:6:12 + │ +4 │ public public(package) fun t2() {} + │ --------------- 'public(package)' visibility used here +5 │ public(package) public fun t3() {} +6 │ public public(friend) fun t4() {} + │ ^^^^^^^^^^^^^^ Cannot mix 'friend' and 'package' visibilities in the same module + +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_check/parser/function_conflicting_visibility.move:7:20 + │ +7 │ public(friend) public fun t5() {} + │ -------------- ^^^^^^ Duplicate 'public' modifier + │ │ + │ 'public' modifier previously given here + +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_check/parser/function_conflicting_visibility.move:9:12 + │ +9 │ public public fun s0() {} + │ ------ ^^^^^^ Duplicate 'public' modifier + │ │ + │ 'public' modifier previously given here + +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_check/parser/function_conflicting_visibility.move:10:21 + │ +10 │ public(package) public(package) fun s1() {} + │ --------------- ^^^^^^^^^^^^^^^ Duplicate 'public' modifier + │ │ + │ 'public' modifier previously given here + +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/parser/function_conflicting_visibility.move:10:21 + │ +10 │ public(package) public(package) fun s1() {} + │ ^^^^^^^^^^^^^^^ 'public(package)' is not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E02016]: invalid visibility modifier + ┌─ tests/move_check/parser/function_conflicting_visibility.move:10:21 + │ +10 │ public(package) public(package) fun s1() {} + │ ^^^^^^^^^^^^^^^ Cannot mix 'package' and 'friend' visibilities in the same module +11 │ public(friend) public(friend) fun s2() {} + │ -------------- 'friend' visibility used here + +error[E02001]: duplicate declaration, item, or annotation + ┌─ tests/move_check/parser/function_conflicting_visibility.move:11:20 + │ +11 │ public(friend) public(friend) fun s2() {} + │ -------------- ^^^^^^^^^^^^^^ Duplicate 'public' modifier + │ │ + │ 'public' modifier previously given here + +error[E02016]: invalid visibility modifier + ┌─ tests/move_check/parser/function_conflicting_visibility.move:11:20 + │ + 4 │ public public(package) fun t2() {} + │ --------------- 'public(package)' visibility used here + · +11 │ public(friend) public(friend) fun s2() {} + │ ^^^^^^^^^^^^^^ Cannot mix 'friend' and 'package' visibilities in the same module diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.move b/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.move index 76b34aeb1b3c6..32065b376e680 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.move +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/function_conflicting_visibility.move @@ -1,4 +1,12 @@ module 0x0::M { - public(friend) public(package) fun f() {} - public(package) public(friend) fun g() {} + public(friend) public(package) fun t0() {} + public(package) public(friend) fun t1() {} + public public(package) fun t2() {} + public(package) public fun t3() {} + public public(friend) fun t4() {} + public(friend) public fun t5() {} + + public public fun s0() {} + public(package) public(package) fun s1() {} + public(friend) public(friend) fun s2() {} } diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.exp b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.exp new file mode 100644 index 0000000000000..cd037601e195f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.exp @@ -0,0 +1,9 @@ +error[E01002]: unexpected token + ┌─ tests/move_check/parser/spec_lambda_return_missing.move:2:22 + │ +2 │ spec fun do(f: ||) { } + │ ^ + │ │ + │ Unexpected ')' + │ Expected a type name + diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.move b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.move new file mode 100644 index 0000000000000..71e100ddeed47 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_lambda_return_missing.move @@ -0,0 +1,3 @@ +module a::m { + spec fun do(f: ||) { } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.exp b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.exp deleted file mode 100644 index 0e1c31793676e..0000000000000 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.exp +++ /dev/null @@ -1,14 +0,0 @@ -warning[W09002]: unused variable - ┌─ tests/move_check/parser/spec_parsing_fun_type_fail.move:2:26 - │ -2 │ fun fun_type_in_prog(p: |u64|u64) { - │ ^ Unused parameter 'p'. Consider removing or prefixing with an underscore: '_p' - │ - = This warning can be suppressed with '#[allow(unused_variable)]' applied to the 'module' or module member ('const', 'fun', or 'struct') - -error[E00002]: DEPRECATED. unexpected spec item - ┌─ tests/move_check/parser/spec_parsing_fun_type_fail.move:2:29 - │ -2 │ fun fun_type_in_prog(p: |u64|u64) { - │ ^^^^^^^^ Specification blocks are deprecated - diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.move b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.move deleted file mode 100644 index 8fa45abafdb76..0000000000000 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_fun_type_fail.move +++ /dev/null @@ -1,4 +0,0 @@ -module 0x8675309::M { - fun fun_type_in_prog(p: |u64|u64) { - } -} diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_lambda_fail.exp b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_lambda_fail.exp index 53f4473c7d330..64637851f7e53 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_lambda_fail.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_lambda_fail.exp @@ -1,14 +1,8 @@ -warning[W09002]: unused variable - ┌─ tests/move_check/parser/spec_parsing_lambda_fail.move:2:24 - │ -2 │ fun lambda_in_prog(x: u64) { - │ ^ Unused parameter 'x'. Consider removing or prefixing with an underscore: '_x' - │ - = This warning can be suppressed with '#[allow(unused_variable)]' applied to the 'module' or module member ('const', 'fun', or 'struct') - -error[E00002]: DEPRECATED. unexpected spec item +error[E13001]: feature is not supported in specified edition ┌─ tests/move_check/parser/spec_parsing_lambda_fail.move:3:15 │ 3 │ let _ = |y| x + y; - │ ^^^^^^^^^ Specification blocks are deprecated + │ ^^^^^^^^^ 'macro' functions are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_ok.move b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_ok.move index b6ad9da6fa48f..44c8fc1bce5e0 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_ok.move +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/spec_parsing_ok.move @@ -12,7 +12,7 @@ module 0x8675309::M { global expected_coin_sum: u64; global other: bool; - native fun all(x: SomeCollection, predicate: |T|bool): bool; + native fun all(x: SomeCollection, predicate: |T| bool): bool; fun spec_fun_using_spec_constructs(x: u64, y: u64) : u64 { // This function would crash in phases after expansion if we would pass it on as a regular function. Testing diff --git a/external-crates/move/crates/move-compiler/tests/move_check/parser/use_with_modifiers.exp b/external-crates/move/crates/move-compiler/tests/move_check/parser/use_with_modifiers.exp index 12708300c2d21..6ff13168c28fa 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/parser/use_with_modifiers.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/parser/use_with_modifiers.exp @@ -20,7 +20,7 @@ error[E01003]: invalid modifier ┌─ tests/move_check/parser/use_with_modifiers.move:6:5 │ 6 │ native use a::m as m4; - │ ^^^^^^ Invalid use declaration. Unexpected 'native' modifier + │ ^^^^^^ Invalid use declaration. 'native' is used only on functions or structs error[E01003]: invalid modifier ┌─ tests/move_check/parser/use_with_modifiers.move:7:5 @@ -32,7 +32,7 @@ error[E01003]: invalid modifier ┌─ tests/move_check/parser/use_with_modifiers.move:7:12 │ 7 │ public native entry use a::m as m5; - │ ^^^^^^ Invalid use declaration. Unexpected 'native' modifier + │ ^^^^^^ Invalid use declaration. 'native' is used only on functions or structs error[E01003]: invalid modifier ┌─ tests/move_check/parser/use_with_modifiers.move:7:19 diff --git a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop.exp b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop.exp index b1ef9f7258e60..e16cac9257ded 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop.exp @@ -2,5 +2,5 @@ error[E04014]: invalid loop control ┌─ tests/move_check/translated_ir_tests/move/commands/break_outside_loop.move:3:5 │ 3 │ break - │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body + │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body or lambda diff --git a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_else.exp b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_else.exp index d67f527825360..e7ee9ac50ccf5 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_else.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_else.exp @@ -2,5 +2,5 @@ error[E04014]: invalid loop control ┌─ tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_else.move:3:24 │ 3 │ if (false) () else break; - │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body + │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body or lambda diff --git a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_if.exp b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_if.exp index af09278df4a15..34da87695f681 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_if.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_if.exp @@ -2,5 +2,5 @@ error[E04014]: invalid loop control ┌─ tests/move_check/translated_ir_tests/move/commands/break_outside_loop_in_if.move:3:15 │ 3 │ if (true) break - │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body + │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body or lambda diff --git a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop.exp b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop.exp index f432e37bc84fd..0a02ba6ed9548 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop.exp @@ -2,5 +2,5 @@ error[E04014]: invalid loop control ┌─ tests/move_check/translated_ir_tests/move/commands/continue_outside_loop.move:3:5 │ 3 │ continue - │ ^^^^^^^^ Invalid usage of 'continue'. 'continue' can only be used inside a loop body + │ ^^^^^^^^ Invalid usage of 'continue'. 'continue' can only be used inside a loop body or lambda diff --git a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop_in_if.exp b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop_in_if.exp index 3fcbd596b8e32..057e508598d23 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop_in_if.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/translated_ir_tests/move/commands/continue_outside_loop_in_if.exp @@ -2,5 +2,5 @@ error[E04014]: invalid loop control ┌─ tests/move_check/translated_ir_tests/move/commands/continue_outside_loop_in_if.move:3:15 │ 3 │ if (true) continue; - │ ^^^^^^^^ Invalid usage of 'continue'. 'continue' can only be used inside a loop body + │ ^^^^^^^^ Invalid usage of 'continue'. 'continue' can only be used inside a loop body or lambda diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/break_outside_loop.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/break_outside_loop.exp index 3de5cf1dabb6a..3aea67b73d791 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/break_outside_loop.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/break_outside_loop.exp @@ -2,17 +2,17 @@ error[E04014]: invalid loop control ┌─ tests/move_check/typing/break_outside_loop.move:6:9 │ 6 │ break - │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body + │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body or lambda error[E04014]: invalid loop control ┌─ tests/move_check/typing/break_outside_loop.move:10:9 │ 10 │ break; - │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body + │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body or lambda error[E04014]: invalid loop control ┌─ tests/move_check/typing/break_outside_loop.move:14:21 │ 14 │ if (x >= 5) break; - │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body + │ ^^^^^ Invalid usage of 'break'. 'break' can only be used inside a loop body or lambda diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/cast_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/cast_invalid.exp index da00dfbf0f1d1..486a13dac8166 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/cast_invalid.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/cast_invalid.exp @@ -35,58 +35,52 @@ error[E04003]: built-in operation not supported │ Found: '(u64, u64)'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' error[E04003]: built-in operation not supported - ┌─ tests/move_check/typing/cast_invalid.move:12:15 + ┌─ tests/move_check/typing/cast_invalid.move:12:10 │ 12 │ (0 as bool); - │ ^^^^ - │ │ - │ Invalid argument to 'as' - │ Found: 'bool'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ ^ ---- Found: 'bool'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ │ + │ Invalid argument to 'as' error[E04003]: built-in operation not supported - ┌─ tests/move_check/typing/cast_invalid.move:13:15 + ┌─ tests/move_check/typing/cast_invalid.move:13:10 │ 13 │ (0 as address); - │ ^^^^^^^ - │ │ - │ Invalid argument to 'as' - │ Found: 'address'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ ^ ------- Found: 'address'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ │ + │ Invalid argument to 'as' error[E04003]: built-in operation not supported - ┌─ tests/move_check/typing/cast_invalid.move:14:21 + ┌─ tests/move_check/typing/cast_invalid.move:14:16 │ 14 │ R{} = (0 as R); - │ ^ - │ │ - │ Invalid argument to 'as' - │ Found: '0x8675309::M::R'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ ^ - Found: '0x8675309::M::R'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ │ + │ Invalid argument to 'as' error[E04003]: built-in operation not supported - ┌─ tests/move_check/typing/cast_invalid.move:15:15 + ┌─ tests/move_check/typing/cast_invalid.move:15:10 │ 15 │ (0 as Cup); - │ ^^^^^^^ - │ │ - │ Invalid argument to 'as' - │ Found: '0x8675309::M::Cup'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ ^ ------- Found: '0x8675309::M::Cup'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ │ + │ Invalid argument to 'as' error[E04003]: built-in operation not supported - ┌─ tests/move_check/typing/cast_invalid.move:16:15 + ┌─ tests/move_check/typing/cast_invalid.move:16:10 │ 16 │ (0 as ()); - │ ^^ - │ │ - │ Invalid argument to 'as' - │ Found: '()'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ ^ -- Found: '()'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ │ + │ Invalid argument to 'as' error[E04003]: built-in operation not supported - ┌─ tests/move_check/typing/cast_invalid.move:17:15 + ┌─ tests/move_check/typing/cast_invalid.move:17:10 │ 17 │ (0 as (u64, u8)); - │ ^^^^^^^^^ - │ │ - │ Invalid argument to 'as' - │ Found: '(u64, u8)'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ ^ --------- Found: '(u64, u8)'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ │ + │ Invalid argument to 'as' error[E04003]: built-in operation not supported ┌─ tests/move_check/typing/cast_invalid.move:19:7 diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp index acb9657129e9e..070f38f013f7c 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/constant_unsupported_exps.exp @@ -83,7 +83,7 @@ error[E04001]: restricted visibility │ -------------- This function can only be called from a 'friend' of module '0x42::X' · 25 │ 0x42::X::f_friend(); - │ ^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::X::f_friend' + │ ^^^^^^^^^^^^^^^^^^^ Invalid call to 'public(friend)' visible function '0x42::X::f_friend' error[E04013]: invalid statement or expression in constant ┌─ tests/move_check/typing/constant_unsupported_exps.move:25:9 @@ -98,7 +98,7 @@ error[E04001]: restricted visibility │ --------- This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module · 26 │ 0x42::X::f_private(); - │ ^^^^^^^^^^^^^^^^^^^^ Invalid call to '0x42::X::f_private' + │ ^^^^^^^^^^^^^^^^^^^^ Invalid call to internal function '0x42::X::f_private' error[E04013]: invalid statement or expression in constant ┌─ tests/move_check/typing/constant_unsupported_exps.move:26:9 diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/continue_outside_loop.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/continue_outside_loop.exp index 189f797030150..a046a45e2fd95 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/continue_outside_loop.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/continue_outside_loop.exp @@ -2,5 +2,5 @@ error[E04014]: invalid loop control ┌─ tests/move_check/typing/continue_outside_loop.move:6:9 │ 6 │ continue - │ ^^^^^^^^ Invalid usage of 'continue'. 'continue' can only be used inside a loop body + │ ^^^^^^^^ Invalid usage of 'continue'. 'continue' can only be used inside a loop body or lambda diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/large_binop.move b/external-crates/move/crates/move-compiler/tests/move_check/typing/large_binop.move index 1ebf4ce446811..08d242156c454 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/large_binop.move +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/large_binop.move @@ -256,12 +256,11 @@ module 0x42::m { (b"hello" != b"bye") && (true && true) && (!false) || - (x"42" == x"42"); - // TODO fix this to parse - // (b"hello" != b"bye") == ( - // 0 == (((((((((((((((((((((((((((((((((((((((((((((0 + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) - // ) == ( - // 0 == (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + 0)))))))))))))))))))))))))))))))) - // ); + (x"42" == x"42") || + (b"hello" != b"bye") == ( + 0 == (((((((((((((((((((((((((((((((((((((((((((((0 + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + 0) + ) == ( + 0 == (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + (0 + 0)))))))))))))))))))))))))))))))) + ); } } diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_entry_function_was_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_entry_function_was_invalid.exp index 03093b840ef17..9acaf5d62a6dc 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_entry_function_was_invalid.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_entry_function_was_invalid.exp @@ -5,5 +5,5 @@ error[E04001]: restricted visibility │ --------- This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module · 26 │ public entry fun f_script_call_private() { X::f_private() } - │ ^^^^^^^^^^^^^^ Invalid call to '0x2::X::f_private' + │ ^^^^^^^^^^^^^^ Invalid call to internal function '0x2::X::f_private' diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_internal.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_internal.exp index a4663b8281f66..b33b1910f8e18 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_internal.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_internal.exp @@ -5,5 +5,5 @@ error[E04001]: restricted visibility │ --- This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module · 10 │ X::foo() - │ ^^^^^^^^ Invalid call to '0x2::X::foo' + │ ^^^^^^^^ Invalid call to internal function '0x2::X::foo' diff --git a/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_visibility_friend_invalid.exp b/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_visibility_friend_invalid.exp index a36e2c63c073d..8f6aed7534c52 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_visibility_friend_invalid.exp +++ b/external-crates/move/crates/move-compiler/tests/move_check/typing/module_call_visibility_friend_invalid.exp @@ -5,7 +5,7 @@ error[E04001]: restricted visibility │ -------------- This function can only be called from a 'friend' of module '0x2::X' · 18 │ public(friend) fun f_friend_call_friend() { X::f_friend() } - │ ^^^^^^^^^^^^^ Invalid call to '0x2::X::f_friend' + │ ^^^^^^^^^^^^^ Invalid call to 'public(friend)' visible function '0x2::X::f_friend' error[E04001]: restricted visibility ┌─ tests/move_check/typing/module_call_visibility_friend_invalid.move:22:52 @@ -14,7 +14,7 @@ error[E04001]: restricted visibility │ --------- This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module · 22 │ public(friend) fun f_friend_call_private_1() { X::f_private() } - │ ^^^^^^^^^^^^^^ Invalid call to '0x2::X::f_private' + │ ^^^^^^^^^^^^^^ Invalid call to internal function '0x2::X::f_private' error[E04001]: restricted visibility ┌─ tests/move_check/typing/module_call_visibility_friend_invalid.move:23:52 @@ -23,5 +23,5 @@ error[E04001]: restricted visibility │ --------- This function is internal to its module. Only 'public', 'public(friend)', and 'public(package)' functions can be called outside of their module · 23 │ public(friend) fun f_friend_call_private_2() { Y::f_private() } - │ ^^^^^^^^^^^^^^ Invalid call to '0x2::Y::f_private' + │ ^^^^^^^^^^^^^^ Invalid call to internal function '0x2::Y::f_private' diff --git a/external-crates/move/crates/move-stdlib/sources/vector.move b/external-crates/move/crates/move-stdlib/sources/vector.move index b5222a244da01..1eb179ba683c1 100644 --- a/external-crates/move/crates/move-stdlib/sources/vector.move +++ b/external-crates/move/crates/move-stdlib/sources/vector.move @@ -1,3 +1,4 @@ +#[defines_primitive(vector)] /// A variable-sized container that can hold any type. Indexing is 0-based, and /// vectors are growable. This module has many native functions. module std::vector { diff --git a/external-crates/move/crates/move-symbol-pool/src/lib.rs b/external-crates/move/crates/move-symbol-pool/src/lib.rs index 1db12a0e0a8ac..c171873ef7773 100644 --- a/external-crates/move/crates/move-symbol-pool/src/lib.rs +++ b/external-crates/move/crates/move-symbol-pool/src/lib.rs @@ -85,6 +85,8 @@ static_symbols!( "loop", "deny_list", "DenyList", + "%implicit", + "%macro", "lint", "migration", );