From 748e2c6df983d4e0eb1abbc4ae28f75433709c19 Mon Sep 17 00:00:00 2001 From: Vas Date: Thu, 15 Jun 2023 09:44:39 +0200 Subject: [PATCH 001/105] Document thread names for SGX compilation target --- library/std/src/sys/sgx/thread.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs index 1608b8cb642d..7ac9d1d64b42 100644 --- a/library/std/src/sys/sgx/thread.rs +++ b/library/std/src/sys/sgx/thread.rs @@ -121,8 +121,16 @@ impl Thread { rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); } + /// SGX should protect in-enclave data from the outside (attacker), + /// so there should be no data leakage to the OS, + /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. + /// + /// This is why the method is intentionally No-Op. pub fn set_name(_name: &CStr) { - // FIXME: could store this pointer in TLS somewhere + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic (target-agnostic) Rust thread code. + // This can be observed in the [`std::thread::tests::test_named_thread`] test, + // which succeeds as-is with the SGX target. } pub fn sleep(dur: Duration) { From 004971f3f07140fe2c9eb5c63c2a103b90018da6 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sat, 15 Jul 2023 23:55:57 +0900 Subject: [PATCH 002/105] Remove `crate` visibility modifier --- crates/parser/src/grammar.rs | 96 ++++++++----------- crates/parser/src/tests/prefix_entries.rs | 1 - .../inline/ok/0040_crate_keyword_vis.rast | 63 ------------ .../inline/ok/0040_crate_keyword_vis.rs | 3 - .../inline/ok/0125_crate_keyword_path.rast | 33 ------- .../inline/ok/0125_crate_keyword_path.rs | 1 - 6 files changed, 40 insertions(+), 157 deletions(-) delete mode 100644 crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast delete mode 100644 crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs delete mode 100644 crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast delete mode 100644 crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 1814e0e54c59..a868419821d8 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -211,70 +211,54 @@ impl BlockLike { const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]); fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool { - match p.current() { - T![pub] => { - let m = p.start(); - p.bump(T![pub]); - if p.at(T!['(']) { - match p.nth(1) { - // test crate_visibility - // pub(crate) struct S; - // pub(self) struct S; - // pub(super) struct S; + if !p.at(T![pub]) { + return false; + } - // test_err crate_visibility_empty_recover - // pub() struct S; + let m = p.start(); + p.bump(T![pub]); + if p.at(T!['(']) { + match p.nth(1) { + // test crate_visibility + // pub(crate) struct S; + // pub(self) struct S; + // pub(super) struct S; - // test pub_parens_typepath - // struct B(pub (super::A)); - // struct B(pub (crate::A,)); - T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => { - // If we are in a tuple struct, then the parens following `pub` - // might be an tuple field, not part of the visibility. So in that - // case we don't want to consume an identifier. + // test_err crate_visibility_empty_recover + // pub() struct S; - // test pub_tuple_field - // struct MyStruct(pub (u32, u32)); - // struct MyStruct(pub (u32)); - // struct MyStruct(pub ()); - if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) { - p.bump(T!['(']); - paths::use_path(p); - p.expect(T![')']); - } - } - // test crate_visibility_in - // pub(in super::A) struct S; - // pub(in crate) struct S; - T![in] => { - p.bump(T!['(']); - p.bump(T![in]); - paths::use_path(p); - p.expect(T![')']); - } - _ => {} + // test pub_parens_typepath + // struct B(pub (super::A)); + // struct B(pub (crate::A,)); + T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => { + // If we are in a tuple struct, then the parens following `pub` + // might be an tuple field, not part of the visibility. So in that + // case we don't want to consume an identifier. + + // test pub_tuple_field + // struct MyStruct(pub (u32, u32)); + // struct MyStruct(pub (u32)); + // struct MyStruct(pub ()); + if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) { + p.bump(T!['(']); + paths::use_path(p); + p.expect(T![')']); } } - m.complete(p, VISIBILITY); - true - } - // test crate_keyword_vis - // crate fn main() { } - // struct S { crate field: u32 } - // struct T(crate u32); - T![crate] => { - if p.nth_at(1, T![::]) { - // test crate_keyword_path - // fn foo() { crate::foo(); } - return false; + // test crate_visibility_in + // pub(in super::A) struct S; + // pub(in crate) struct S; + T![in] => { + p.bump(T!['(']); + p.bump(T![in]); + paths::use_path(p); + p.expect(T![')']); } - let m = p.start(); - p.bump(T![crate]); - m.complete(p, VISIBILITY); - true + _ => {} } - _ => false, } + m.complete(p, VISIBILITY); + true } fn opt_rename(p: &mut Parser<'_>) { diff --git a/crates/parser/src/tests/prefix_entries.rs b/crates/parser/src/tests/prefix_entries.rs index 11f9c34abdf1..2f3c7febc040 100644 --- a/crates/parser/src/tests/prefix_entries.rs +++ b/crates/parser/src/tests/prefix_entries.rs @@ -6,7 +6,6 @@ fn vis() { check(PrefixEntryPoint::Vis, "fn foo() {}", ""); check(PrefixEntryPoint::Vis, "pub(fn foo() {}", "pub"); check(PrefixEntryPoint::Vis, "pub(crate fn foo() {}", "pub(crate"); - check(PrefixEntryPoint::Vis, "crate fn foo() {}", "crate"); } #[test] diff --git a/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast b/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast deleted file mode 100644 index 07b0210e44d0..000000000000 --- a/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast +++ /dev/null @@ -1,63 +0,0 @@ -SOURCE_FILE - FN - VISIBILITY - CRATE_KW "crate" - WHITESPACE " " - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "main" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - R_CURLY "}" - WHITESPACE "\n" - STRUCT - STRUCT_KW "struct" - WHITESPACE " " - NAME - IDENT "S" - WHITESPACE " " - RECORD_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - RECORD_FIELD - VISIBILITY - CRATE_KW "crate" - WHITESPACE " " - NAME - IDENT "field" - COLON ":" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "u32" - WHITESPACE " " - R_CURLY "}" - WHITESPACE "\n" - STRUCT - STRUCT_KW "struct" - WHITESPACE " " - NAME - IDENT "T" - TUPLE_FIELD_LIST - L_PAREN "(" - TUPLE_FIELD - VISIBILITY - CRATE_KW "crate" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "u32" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs b/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs deleted file mode 100644 index e2b5f2161dfc..000000000000 --- a/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs +++ /dev/null @@ -1,3 +0,0 @@ -crate fn main() { } -struct S { crate field: u32 } -struct T(crate u32); diff --git a/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast b/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast deleted file mode 100644 index 8d9b61630ae6..000000000000 --- a/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast +++ /dev/null @@ -1,33 +0,0 @@ -SOURCE_FILE - FN - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "foo" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - EXPR_STMT - CALL_EXPR - PATH_EXPR - PATH - PATH - PATH_SEGMENT - NAME_REF - CRATE_KW "crate" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "foo" - ARG_LIST - L_PAREN "(" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE " " - R_CURLY "}" - WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs b/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs deleted file mode 100644 index 0f454d121d69..000000000000 --- a/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs +++ /dev/null @@ -1 +0,0 @@ -fn foo() { crate::foo(); } From 4704881b641884de50645637108b6b6f5b68aaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 17 Jul 2023 16:49:15 +0300 Subject: [PATCH 003/105] Merge commit '37f84c101bca43b11027f30ab0c2852f9325bc3d' into sync-from-ra --- .editorconfig | 13 +- .github/workflows/autopublish.yaml | 2 +- .github/workflows/metrics.yaml | 141 +- Cargo.lock | 474 +- Cargo.toml | 24 +- crates/base-db/Cargo.toml | 2 +- crates/base-db/src/fixture.rs | 114 +- crates/base-db/src/input.rs | 6 +- crates/cfg/Cargo.toml | 6 +- crates/cfg/src/lib.rs | 4 +- crates/flycheck/Cargo.toml | 4 +- crates/hir-def/Cargo.toml | 8 +- crates/hir-def/src/attr.rs | 40 +- crates/hir-def/src/body.rs | 4 +- crates/hir-def/src/body/lower.rs | 32 +- crates/hir-def/src/body/pretty.rs | 4 +- crates/hir-def/src/body/tests.rs | 12 +- crates/hir-def/src/body/tests/block.rs | 77 + crates/hir-def/src/data.rs | 34 +- crates/hir-def/src/data/adt.rs | 131 +- crates/hir-def/src/db.rs | 26 +- crates/hir-def/src/dyn_map/keys.rs | 5 +- crates/hir-def/src/expander.rs | 57 +- crates/hir-def/src/find_path.rs | 2 +- crates/hir-def/src/generics.rs | 16 +- crates/hir-def/src/hir/type_ref.rs | 4 +- crates/hir-def/src/import_map.rs | 645 +-- crates/hir-def/src/item_scope.rs | 13 +- crates/hir-def/src/item_tree.rs | 4 +- crates/hir-def/src/item_tree/pretty.rs | 4 +- crates/hir-def/src/lang_item.rs | 8 +- crates/hir-def/src/lib.rs | 127 +- crates/hir-def/src/lower.rs | 8 +- .../builtin_derive_macro.rs | 76 + .../macro_expansion_tests/builtin_fn_macro.rs | 22 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 36 + .../hir-def/src/macro_expansion_tests/mod.rs | 53 +- crates/hir-def/src/nameres.rs | 21 +- crates/hir-def/src/nameres/collector.rs | 75 +- crates/hir-def/src/nameres/diagnostics.rs | 11 +- crates/hir-def/src/nameres/path_resolution.rs | 126 +- crates/hir-def/src/path.rs | 7 +- crates/hir-def/src/path/lower.rs | 4 +- crates/hir-def/src/pretty.rs | 4 +- crates/hir-def/src/resolver.rs | 62 +- crates/hir-expand/Cargo.toml | 6 +- crates/hir-expand/src/ast_id_map.rs | 79 +- crates/hir-expand/src/builtin_attr_macro.rs | 4 +- crates/hir-expand/src/builtin_derive_macro.rs | 196 +- crates/hir-expand/src/builtin_fn_macro.rs | 14 +- crates/hir-expand/src/db.rs | 479 +- crates/hir-expand/src/eager.rs | 204 +- crates/hir-expand/src/fixup.rs | 16 +- crates/hir-expand/src/hygiene.rs | 24 +- crates/hir-expand/src/lib.rs | 231 +- crates/hir-expand/src/mod_path.rs | 4 +- crates/hir-expand/src/name.rs | 23 +- crates/hir-ty/Cargo.toml | 12 +- crates/hir-ty/src/autoderef.rs | 13 +- crates/hir-ty/src/chalk_db.rs | 68 +- crates/hir-ty/src/chalk_ext.rs | 3 +- crates/hir-ty/src/consteval.rs | 16 +- crates/hir-ty/src/consteval/tests.rs | 312 +- .../hir-ty/src/consteval/tests/intrinsics.rs | 329 +- crates/hir-ty/src/db.rs | 8 + crates/hir-ty/src/diagnostics.rs | 2 +- crates/hir-ty/src/diagnostics/decl_check.rs | 7 +- crates/hir-ty/src/display.rs | 64 +- crates/hir-ty/src/infer.rs | 10 +- crates/hir-ty/src/infer/closure.rs | 50 +- crates/hir-ty/src/infer/coerce.rs | 6 +- crates/hir-ty/src/infer/expr.rs | 15 +- crates/hir-ty/src/infer/mutability.rs | 16 +- crates/hir-ty/src/infer/pat.rs | 4 +- crates/hir-ty/src/infer/unify.rs | 16 +- crates/hir-ty/src/layout.rs | 106 +- crates/hir-ty/src/layout/adt.rs | 16 +- crates/hir-ty/src/layout/tests.rs | 78 +- crates/hir-ty/src/lib.rs | 11 +- crates/hir-ty/src/lower.rs | 10 +- crates/hir-ty/src/method_resolution.rs | 34 +- crates/hir-ty/src/mir.rs | 30 +- crates/hir-ty/src/mir/borrowck.rs | 19 +- crates/hir-ty/src/mir/eval.rs | 1346 +++-- crates/hir-ty/src/mir/eval/shim.rs | 540 +- crates/hir-ty/src/mir/eval/shim/simd.rs | 177 + crates/hir-ty/src/mir/eval/tests.rs | 30 +- crates/hir-ty/src/mir/lower.rs | 637 ++- crates/hir-ty/src/mir/lower/as_place.rs | 77 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 7 +- crates/hir-ty/src/mir/monomorphization.rs | 48 +- crates/hir-ty/src/mir/pretty.rs | 38 +- crates/hir-ty/src/tests.rs | 9 +- crates/hir-ty/src/tests/macros.rs | 8 +- crates/hir-ty/src/tests/method_resolution.rs | 46 + crates/hir-ty/src/tests/regression.rs | 4 +- crates/hir-ty/src/tests/traits.rs | 24 + crates/hir-ty/src/traits.rs | 2 +- crates/hir-ty/src/utils.rs | 4 +- crates/hir/Cargo.toml | 2 +- crates/hir/src/attrs.rs | 1 + crates/hir/src/db.rs | 6 +- crates/hir/src/diagnostics.rs | 2 +- crates/hir/src/display.rs | 8 +- crates/hir/src/has_source.rs | 22 +- crates/hir/src/lib.rs | 153 +- crates/hir/src/semantics.rs | 8 +- crates/hir/src/semantics/source_to_def.rs | 7 +- crates/hir/src/source_analyzer.rs | 2 +- .../src/handlers/add_missing_match_arms.rs | 133 +- .../src/handlers/desugar_doc_comment.rs | 4 +- .../src/handlers/expand_glob_import.rs | 162 +- .../src/handlers/extract_function.rs | 36 +- .../src/handlers/fix_visibility.rs | 113 +- .../src/handlers/generate_delegate_methods.rs | 166 +- .../src/handlers/generate_delegate_trait.rs | 1049 ++++ .../src/handlers/generate_derive.rs | 106 +- .../src/handlers/generate_function.rs | 4 +- ...getter.rs => generate_getter_or_setter.rs} | 438 +- .../src/handlers/generate_setter.rs | 175 - .../src/handlers/generate_trait_from_impl.rs | 429 ++ .../ide-assists/src/handlers/inline_call.rs | 141 +- .../src/handlers/move_const_to_impl.rs | 6 +- .../src/handlers/promote_local_to_const.rs | 56 +- .../src/handlers/pull_assignment_up.rs | 2 +- .../replace_derive_with_manual_impl.rs | 2 +- .../src/handlers/unwrap_result_return_type.rs | 16 +- .../handlers/wrap_return_type_in_result.rs | 31 +- crates/ide-assists/src/lib.rs | 13 +- crates/ide-assists/src/tests/generated.rs | 121 +- crates/ide-completion/src/completions/mod_.rs | 2 +- crates/ide-completion/src/context.rs | 4 +- crates/ide-completion/src/context/analysis.rs | 7 +- crates/ide-completion/src/lib.rs | 2 +- crates/ide-completion/src/tests/attribute.rs | 1 + crates/ide-db/Cargo.toml | 2 +- crates/ide-db/src/apply_change.rs | 4 +- crates/ide-db/src/helpers.rs | 18 +- crates/ide-db/src/imports/import_assets.rs | 8 +- crates/ide-db/src/items_locator.rs | 60 +- crates/ide-db/src/lib.rs | 4 +- crates/ide-db/src/path_transform.rs | 2 +- crates/ide-db/src/search.rs | 14 +- crates/ide-db/src/source_change.rs | 63 +- crates/ide-diagnostics/Cargo.toml | 1 + .../src/handlers/break_outside_of_loop.rs | 9 +- .../src/handlers/expected_function.rs | 9 +- .../src/handlers/field_shorthand.rs | 40 +- .../src/handlers/inactive_code.rs | 7 +- .../src/handlers/incoherent_impl.rs | 10 +- .../src/handlers/incorrect_case.rs | 112 +- .../src/handlers/invalid_derive_target.rs | 5 +- .../src/handlers/json_is_not_rust.rs | 5 +- .../src/handlers/macro_error.rs | 16 +- .../src/handlers/malformed_derive.rs | 5 +- .../src/handlers/mismatched_arg_count.rs | 4 +- .../src/handlers/missing_fields.rs | 4 +- .../src/handlers/missing_match_arms.rs | 9 +- .../src/handlers/missing_unsafe.rs | 9 +- .../src/handlers/moved_out_of_ref.rs | 9 +- .../src/handlers/mutability_errors.rs | 181 +- .../src/handlers/no_such_field.rs | 9 +- .../src/handlers/private_assoc_item.rs | 21 +- .../src/handlers/private_field.rs | 9 +- .../replace_filter_map_next_with_find_map.rs | 41 +- .../src/handlers/type_mismatch.rs | 4 +- .../src/handlers/typed_hole.rs | 5 +- .../src/handlers/undeclared_label.rs | 9 +- .../handlers/unimplemented_builtin_macro.rs | 10 +- .../src/handlers/unlinked_file.rs | 14 +- .../src/handlers/unreachable_label.rs | 9 +- .../src/handlers/unresolved_extern_crate.rs | 9 +- .../src/handlers/unresolved_field.rs | 21 +- .../src/handlers/unresolved_import.rs | 9 +- .../src/handlers/unresolved_macro_call.rs | 4 +- .../src/handlers/unresolved_method.rs | 9 +- .../src/handlers/unresolved_module.rs | 29 +- .../src/handlers/unresolved_proc_macro.rs | 4 +- .../src/handlers/useless_braces.rs | 75 +- crates/ide-diagnostics/src/lib.rs | 262 +- crates/ide-diagnostics/src/tests.rs | 13 +- crates/ide-ssr/src/search.rs | 2 +- crates/ide/src/doc_links.rs | 4 +- crates/ide/src/hover/render.rs | 8 +- crates/ide/src/hover/tests.rs | 35 +- crates/ide/src/inlay_hints/adjustment.rs | 2 +- crates/ide/src/inlay_hints/chaining.rs | 12 +- crates/ide/src/interpret_function.rs | 6 +- crates/ide/src/lib.rs | 11 +- crates/ide/src/moniker.rs | 16 +- crates/ide/src/navigation_target.rs | 10 +- crates/ide/src/runnables.rs | 57 +- crates/ide/src/static_index.rs | 70 +- crates/ide/src/syntax_highlighting.rs | 13 +- .../test_data/highlight_strings.html | 6 +- crates/ide/src/syntax_highlighting/tests.rs | 4 +- crates/ide/src/view_memory_layout.rs | 409 ++ crates/intern/Cargo.toml | 2 +- crates/load-cargo/Cargo.toml | 25 + crates/load-cargo/src/lib.rs | 441 ++ crates/mbe/src/benchmark.rs | 11 +- crates/mbe/src/expander/matcher.rs | 2 +- crates/mbe/src/lib.rs | 83 +- crates/mbe/src/syntax_bridge.rs | 148 +- crates/mbe/src/token_map.rs | 7 + crates/mbe/src/tt_iter.rs | 2 +- crates/parser/src/grammar/generic_args.rs | 19 +- crates/parser/src/grammar/generic_params.rs | 3 +- crates/parser/src/shortcuts.rs | 2 +- ...cover_from_missing_assoc_item_binding.rast | 48 + ...recover_from_missing_assoc_item_binding.rs | 1 + ...22_recover_from_missing_const_default.rast | 44 + ...0022_recover_from_missing_const_default.rs | 1 + crates/paths/src/lib.rs | 15 +- crates/proc-macro-api/Cargo.toml | 2 +- crates/proc-macro-api/src/version.rs | 11 +- crates/proc-macro-srv/Cargo.toml | 4 +- crates/profile/Cargo.toml | 2 +- crates/profile/src/tree.rs | 2 +- crates/project-model/Cargo.toml | 2 +- crates/project-model/src/build_scripts.rs | 17 +- crates/project-model/src/cargo_workspace.rs | 4 +- crates/project-model/src/lib.rs | 23 +- crates/project-model/src/manifest_path.rs | 8 +- crates/project-model/src/rustc_cfg.rs | 6 +- crates/project-model/src/sysroot.rs | 22 +- .../project-model/src/target_data_layout.rs | 3 +- crates/project-model/src/workspace.rs | 92 +- crates/rust-analyzer/Cargo.toml | 13 +- crates/rust-analyzer/src/bin/logger.rs | 92 +- crates/rust-analyzer/src/bin/main.rs | 64 +- crates/rust-analyzer/src/cli.rs | 18 +- .../rust-analyzer/src/cli/analysis_stats.rs | 162 +- crates/rust-analyzer/src/cli/diagnostics.rs | 10 +- crates/rust-analyzer/src/cli/flags.rs | 32 +- crates/rust-analyzer/src/cli/load_cargo.rs | 205 - crates/rust-analyzer/src/cli/lsif.rs | 59 +- crates/rust-analyzer/src/cli/run_tests.rs | 89 + crates/rust-analyzer/src/cli/scip.rs | 34 +- crates/rust-analyzer/src/cli/ssr.rs | 15 +- crates/rust-analyzer/src/config.rs | 1 + crates/rust-analyzer/src/dispatch.rs | 30 +- crates/rust-analyzer/src/from_proto.rs | 27 +- crates/rust-analyzer/src/global_state.rs | 12 +- .../src/handlers/notification.rs | 28 +- crates/rust-analyzer/src/handlers/request.rs | 257 +- .../src/integrated_benchmarks.rs | 2 +- crates/rust-analyzer/src/lib.rs | 13 +- crates/rust-analyzer/src/lsp_ext.rs | 27 + crates/rust-analyzer/src/main_loop.rs | 18 +- crates/rust-analyzer/src/reload.rs | 296 +- crates/rust-analyzer/src/to_proto.rs | 3 + .../rust-analyzer/tests/slow-tests/support.rs | 26 +- crates/sourcegen/src/lib.rs | 4 +- crates/stdx/Cargo.toml | 2 +- crates/syntax/Cargo.toml | 2 +- crates/syntax/src/ast/edit_in_place.rs | 49 +- crates/syntax/src/ast/make.rs | 93 + crates/syntax/src/lib.rs | 103 + crates/test-utils/src/fixture.rs | 95 +- crates/test-utils/src/minicore.rs | 69 +- crates/tt/src/buffer.rs | 6 +- crates/tt/src/lib.rs | 15 + crates/vfs-notify/Cargo.toml | 3 +- crates/vfs/Cargo.toml | 2 +- crates/vfs/src/vfs_path.rs | 8 +- docs/dev/lsp-extensions.md | 47 +- docs/dev/style.md | 13 + docs/user/manual.adoc | 37 +- editors/code/.eslintrc.js | 11 +- editors/code/.vscodeignore | 1 - editors/code/package-lock.json | 4650 ++++------------- editors/code/package.json | 77 +- .../rustdoc.markdown.injection.tmGrammar.json | 36 - editors/code/src/ast_inspector.ts | 26 +- editors/code/src/bootstrap.ts | 22 +- editors/code/src/client.ts | 76 +- editors/code/src/commands.ts | 386 +- editors/code/src/config.ts | 81 +- editors/code/src/ctx.ts | 48 +- editors/code/src/debug.ts | 37 +- editors/code/src/dependencies_provider.ts | 28 +- editors/code/src/diagnostics.ts | 20 +- editors/code/src/lsp_ext.ts | 55 +- editors/code/src/main.ts | 23 +- editors/code/src/nullable.ts | 19 + editors/code/src/persistent_state.ts | 2 +- editors/code/src/run.ts | 28 +- editors/code/src/snippets.ts | 16 +- editors/code/src/tasks.ts | 17 +- editors/code/src/toolchain.ts | 19 +- editors/code/src/undefinable.ts | 19 + editors/code/src/util.ts | 36 +- editors/code/tests/unit/index.ts | 5 +- editors/code/tests/unit/launch_config.test.ts | 2 +- editors/code/tests/unit/runnable_env.test.ts | 6 +- editors/code/tests/unit/settings.test.ts | 6 +- editors/code/tsconfig.json | 14 +- lib/README.md | 9 + lib/la-arena/Cargo.toml | 2 +- lib/line-index/Cargo.toml | 6 +- lib/line-index/src/lib.rs | 218 +- lib/line-index/src/tests.rs | 127 +- lib/line-index/tests/it.rs | 62 - lib/lsp-server/Cargo.toml | 6 +- lib/lsp-server/src/msg.rs | 2 +- xtask/Cargo.toml | 2 +- xtask/src/flags.rs | 29 +- xtask/src/install.rs | 10 +- xtask/src/metrics.rs | 76 +- xtask/src/publish.rs | 11 +- 311 files changed, 13700 insertions(+), 9110 deletions(-) create mode 100644 crates/hir-ty/src/mir/eval/shim/simd.rs create mode 100644 crates/ide-assists/src/handlers/generate_delegate_trait.rs rename crates/ide-assists/src/handlers/{generate_getter.rs => generate_getter_or_setter.rs} (61%) delete mode 100644 crates/ide-assists/src/handlers/generate_setter.rs create mode 100644 crates/ide-assists/src/handlers/generate_trait_from_impl.rs create mode 100644 crates/ide/src/view_memory_layout.rs create mode 100644 crates/load-cargo/Cargo.toml create mode 100644 crates/load-cargo/src/lib.rs create mode 100644 crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast create mode 100644 crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs create mode 100644 crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast create mode 100644 crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs delete mode 100644 crates/rust-analyzer/src/cli/load_cargo.rs create mode 100644 crates/rust-analyzer/src/cli/run_tests.rs delete mode 100644 editors/code/rustdoc.markdown.injection.tmGrammar.json create mode 100644 editors/code/src/nullable.ts create mode 100644 editors/code/src/undefinable.ts delete mode 100644 lib/line-index/tests/it.rs diff --git a/.editorconfig b/.editorconfig index 314f79d3f901..f00ade5fd826 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,13 +7,10 @@ trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true indent_style = space - -[*.{rs,toml}] indent_size = 4 -[*.ts] -indent_size = 4 -[*.js] -indent_size = 4 -[*.json] -indent_size = 4 +[*.md] +indent_size = 2 + +[*.{yml, yaml}] +indent_size = 2 diff --git a/.github/workflows/autopublish.yaml b/.github/workflows/autopublish.yaml index 15cedab12726..310a8a5be7ad 100644 --- a/.github/workflows/autopublish.yaml +++ b/.github/workflows/autopublish.yaml @@ -49,8 +49,8 @@ jobs: cargo workspaces rename --from project-model project_model cargo workspaces rename --from test-utils test_utils cargo workspaces rename --from text-edit text_edit - cargo workspaces rename ra_ap_%n # Remove library crates from the workspaces so we don't auto-publish them as well sed -i 's/ "lib\/\*",//' ./Cargo.toml + cargo workspaces rename ra_ap_%n find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} + cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$(($RUN_NUMBER + 133)) diff --git a/.github/workflows/metrics.yaml b/.github/workflows/metrics.yaml index 3fe2fc917a39..260e45ff517c 100644 --- a/.github/workflows/metrics.yaml +++ b/.github/workflows/metrics.yaml @@ -1,8 +1,8 @@ name: metrics on: push: - branches: - - master + branches: + - master env: CARGO_INCREMENTAL: 0 @@ -11,20 +11,135 @@ env: RUSTUP_MAX_RETRIES: 10 jobs: - metrics: + setup_cargo: if: github.repository == 'rust-lang/rust-analyzer' runs-on: ubuntu-latest + steps: + - name: Install Rust toolchain + run: | + rustup update --no-self-update stable + rustup component add rustfmt rust-src + rustup default stable + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} + + build_metrics: + runs-on: ubuntu-latest + needs: setup_cargo steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - - name: Install Rust toolchain - run: | - rustup update --no-self-update stable - rustup component add rustfmt rust-src + - name: Restore cargo cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} - - name: Collect metrics - run: cargo xtask metrics - env: - METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }} + - name: Collect build metrics + run: cargo xtask metrics build + + - name: Cache target + uses: actions/cache@v3 + with: + path: target/ + key: ${{ runner.os }}-target-${{ github.sha }} + + - name: Upload build metrics + uses: actions/upload-artifact@v3 + with: + name: build-${{ github.sha }} + path: target/build.json + if-no-files-found: error + + other_metrics: + strategy: + matrix: + names: [self, ripgrep, webrender, diesel] + runs-on: ubuntu-latest + needs: [setup_cargo, build_metrics] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Restore cargo cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} + + - name: Restore target cache + uses: actions/cache@v3 + with: + path: target/ + key: ${{ runner.os }}-target-${{ github.sha }} + + - name: Collect metrics + run: cargo xtask metrics ${{ matrix.names }} + + - name: Upload metrics + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.names }}-${{ github.sha }} + path: target/${{ matrix.names }}.json + if-no-files-found: error + + generate_final_metrics: + runs-on: ubuntu-latest + needs: [build_metrics, other_metrics] + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Download build metrics + uses: actions/download-artifact@v3 + with: + name: build-${{ github.sha }} + + - name: Download self metrics + uses: actions/download-artifact@v3 + with: + name: self-${{ github.sha }} + + - name: Download ripgrep metrics + uses: actions/download-artifact@v3 + with: + name: ripgrep-${{ github.sha }} + + - name: Download webrender metrics + uses: actions/download-artifact@v3 + with: + name: webrender-${{ github.sha }} + + - name: Download diesel metrics + uses: actions/download-artifact@v3 + with: + name: diesel-${{ github.sha }} + + - name: Combine json + run: | + git clone --depth 1 https://$METRICS_TOKEN@github.com/rust-analyzer/metrics.git + jq -s ".[0] * .[1] * .[2] * .[3] * .[4]" build.json self.json ripgrep.json webrender.json diesel.json -c >> metrics/metrics.json + cd metrics + git add . + git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈 + git push origin master + env: + METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index 13cb25f7b03b..f8806794979e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "anymap" @@ -46,9 +46,9 @@ checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "atty" @@ -77,8 +77,8 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", - "object", + "miniz_oxide 0.6.2", + "object 0.30.4", "rustc-demangle", ] @@ -87,7 +87,7 @@ name = "base-db" version = "0.0.0" dependencies = [ "cfg", - "la-arena", + "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "profile", "rustc-hash", "salsa", @@ -107,9 +107,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.1.0" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" [[package]] name = "byteorder" @@ -177,21 +177,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.91.0" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59178fded594fe78c47b841520e5a4399d00fe15fffee19b945958a878cd02d" +checksum = "ff5053a8a42dbff5279a82423946fc56dc1253b76cf211b2b3c14b3aad4e1281" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", "synstructure", ] [[package]] name = "chalk-ir" -version = "0.91.0" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824be92876823b828d551bb792f79eb1f69c69d1948abf69fccbf84e448e57b" +checksum = "8a56de2146a8ed0fcd54f4bd50db852f1de4eac9e1efe568494f106c21b77d2a" dependencies = [ "bitflags 1.3.2", "chalk-derive", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.91.0" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e110d1260809c238072d1c8ef84060e39983e8ea9d4c6f74b19b0ebbf8904dc" +checksum = "5cc09e6e9531f3544989ef89b189e80fbc7ad9e2f73f1c5e03ddc9ffb0527463" dependencies = [ "chalk-derive", "chalk-ir", @@ -213,14 +213,14 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.91.0" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12200b19abf4b0633095f7bd099f3ef609d314754b6adb358c68cc04d10589e5" +checksum = "b392e02b4c81ec76d3748da839fc70a5539b83d27c9030668463d34d5110b860" dependencies = [ "chalk-derive", "chalk-ir", "ena", - "indexmap", + "indexmap 1.9.3", "itertools", "petgraph", "rustc-hash", @@ -286,22 +286,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.9.0", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -313,7 +313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.12.3", "lock_api", "once_cell", "parking_lot_core 0.9.6", @@ -321,13 +321,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" +checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -363,6 +363,12 @@ dependencies = [ "log", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "expect-test" version = "1.4.1" @@ -382,7 +388,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -393,12 +399,12 @@ checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -419,9 +425,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -443,9 +449,9 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "hashbrown" @@ -453,6 +459,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.3.3" @@ -509,7 +521,7 @@ dependencies = [ "anymap", "arrayvec", "base-db", - "bitflags 2.1.0", + "bitflags 2.3.2", "cfg", "cov-mark", "dashmap", @@ -517,14 +529,14 @@ dependencies = [ "either", "expect-test", "fst", - "hashbrown", + "hashbrown 0.12.3", "hir-expand", "hkalbasi-rustc-ap-rustc_abi", "hkalbasi-rustc-ap-rustc_index", - "indexmap", + "indexmap 2.0.0", "intern", "itertools", - "la-arena", + "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", "once_cell", @@ -548,10 +560,10 @@ dependencies = [ "cov-mark", "either", "expect-test", - "hashbrown", + "hashbrown 0.12.3", "intern", "itertools", - "la-arena", + "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", "profile", @@ -570,7 +582,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.1.0", + "bitflags 2.3.2", "chalk-derive", "chalk-ir", "chalk-recursive", @@ -584,10 +596,11 @@ dependencies = [ "hkalbasi-rustc-ap-rustc_index", "intern", "itertools", - "la-arena", + "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "nohash-hasher", "once_cell", + "oorandom", "profile", "project-model", "rustc-hash", @@ -626,11 +639,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -715,10 +728,10 @@ dependencies = [ "expect-test", "fst", "hir", - "indexmap", + "indexmap 2.0.0", "itertools", "limit", - "line-index", + "line-index 0.1.0-pre.1", "memchr", "nohash-hasher", "once_cell", @@ -748,6 +761,7 @@ dependencies = [ "hir", "ide-db", "itertools", + "once_cell", "profile", "serde_json", "sourcegen", @@ -777,9 +791,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -792,7 +806,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] @@ -829,7 +853,7 @@ name = "intern" version = "0.0.0" dependencies = [ "dashmap", - "hashbrown", + "hashbrown 0.12.3", "once_cell", "rustc-hash", "triomphe", @@ -878,7 +902,13 @@ dependencies = [ [[package]] name = "la-arena" -version = "0.3.0" +version = "0.3.1" + +[[package]] +name = "la-arena" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3752f229dcc5a481d60f385fa479ff46818033d881d2d801aa27dffcfb5e8306" [[package]] name = "lazy_static" @@ -888,25 +918,25 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" dependencies = [ "cfg-if", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "libmimalloc-sys" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a558e3d911bc3c7bfc8c78bc580b404d6e51c1cefbf656e176a94b49b0df40" +checksum = "f4ac0e912c8ef1b735e92369695618dc5b1819f5a7bf3f167301a3ba1cea515e" dependencies = [ "cc", "libc", @@ -919,16 +949,43 @@ version = "0.0.0" [[package]] name = "line-index" version = "0.1.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cad96769710c1745e11d4f940a8ff36000ade4bbada4285b001cb8aa2f745ce" dependencies = [ "nohash-hasher", "text-size", ] +[[package]] +name = "line-index" +version = "0.1.0" +dependencies = [ + "nohash-hasher", + "text-size", +] + +[[package]] +name = "load-cargo" +version = "0.0.0" +dependencies = [ + "anyhow", + "crossbeam-channel", + "ide", + "ide-db", + "itertools", + "proc-macro-api", + "project-model", + "tracing", + "tt", + "vfs", + "vfs-notify", +] + [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -936,16 +993,25 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "lsp-server" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3711e4d6f491dc9edc0f1df80e204f38206775ac92c1241e89b79229a850bc00" dependencies = [ - "cfg-if", + "crossbeam-channel", + "log", + "serde", + "serde_json", ] [[package]] name = "lsp-server" -version = "0.7.0" +version = "0.7.2" dependencies = [ "crossbeam-channel", "log", @@ -967,15 +1033,6 @@ dependencies = [ "url", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - [[package]] name = "mbe" version = "0.0.0" @@ -1016,10 +1073,19 @@ dependencies = [ ] [[package]] -name = "mimalloc" -version = "0.1.36" +name = "memoffset" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d88dad3f985ec267a3fcb7a1726f5cb1a7e8cad8b646e70a84f967210df23da" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mimalloc" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2894987a3459f3ffb755608bd82188f8ed00d0ae077f1edea29c068d639d98" dependencies = [ "libmimalloc-sys", ] @@ -1033,6 +1099,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.5" @@ -1042,7 +1117,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1051,7 +1126,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123" dependencies = [ - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1087,7 +1162,7 @@ dependencies = [ "libc", "mio", "walkdir", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1112,18 +1187,27 @@ dependencies = [ [[package]] name = "object" -version = "0.30.3" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" @@ -1182,7 +1266,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1209,9 +1293,9 @@ version = "0.0.0" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "perf-event" @@ -1239,7 +1323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -1253,7 +1337,7 @@ name = "proc-macro-api" version = "0.0.0" dependencies = [ "memmap2", - "object", + "object 0.31.1", "paths", "profile", "serde", @@ -1273,7 +1357,7 @@ dependencies = [ "libloading", "mbe", "memmap2", - "object", + "object 0.31.1", "paths", "proc-macro-api", "proc-macro-test", @@ -1317,7 +1401,7 @@ version = "0.0.0" dependencies = [ "cfg-if", "countme", - "la-arena", + "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "once_cell", "perf-event", @@ -1335,7 +1419,7 @@ dependencies = [ "cfg", "expect-test", "itertools", - "la-arena", + "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "profile", "rustc-hash", @@ -1370,9 +1454,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" dependencies = [ "bitflags 1.3.2", "memchr", @@ -1390,9 +1474,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -1438,30 +1522,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "regex" -version = "1.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "rowan" version = "0.15.11" @@ -1469,8 +1529,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf" dependencies = [ "countme", - "hashbrown", - "memoffset", + "hashbrown 0.12.3", + "memoffset 0.8.0", "rustc-hash", "text-size", ] @@ -1494,7 +1554,8 @@ dependencies = [ "ide-db", "ide-ssr", "itertools", - "lsp-server", + "load-cargo", + "lsp-server 0.7.1", "lsp-types", "mbe", "mimalloc", @@ -1512,12 +1573,10 @@ dependencies = [ "scip", "serde", "serde_json", - "serde_repr", "sourcegen", "stdx", "syntax", "test-utils", - "thiserror", "tikv-jemallocator", "toolchain", "tracing", @@ -1525,7 +1584,6 @@ dependencies = [ "tracing-subscriber", "tracing-tree", "triomphe", - "tt", "vfs", "vfs-notify", "winapi", @@ -1535,9 +1593,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -1558,7 +1616,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b223dccb46c32753144d0b51290da7230bb4aedcd8379d6b4c9a474c18bf17a" dependencies = [ "crossbeam-utils", - "indexmap", + "indexmap 1.9.3", "lock_api", "log", "oorandom", @@ -1641,11 +1699,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" dependencies = [ - "indexmap", + "indexmap 1.9.3", "itoa", "ryu", "serde", @@ -1653,13 +1711,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -1731,9 +1789,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -1748,7 +1806,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", "unicode-xid", ] @@ -1759,7 +1817,7 @@ dependencies = [ "cov-mark", "either", "expect-test", - "indexmap", + "indexmap 2.0.0", "itertools", "once_cell", "parser", @@ -1806,22 +1864,22 @@ checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -1867,9 +1925,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" dependencies = [ "serde", "time-core", @@ -1877,9 +1935,9 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "tinyvec" @@ -1917,20 +1975,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -1949,25 +2007,21 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ - "matchers", - "once_cell", - "regex", "sharded-slab", "thread_local", - "tracing", "tracing-core", "tracing-log", ] [[package]] name = "tracing-tree" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758e983ab7c54fee18403994507e7f212b9005e957ce7984996fac8d11facedb" +checksum = "4f9742d8df709837409dbb22aa25dd7769c260406f20ff48a2320b80a4a6aed0" dependencies = [ "atty", "nu-ansi-term", @@ -2060,9 +2114,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -2087,9 +2141,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -2114,7 +2168,7 @@ name = "vfs" version = "0.0.0" dependencies = [ "fst", - "indexmap", + "indexmap 2.0.0", "nohash-hasher", "paths", "rustc-hash", @@ -2187,13 +2241,37 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -2202,42 +2280,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "write-json" version = "0.1.2" @@ -2289,9 +2409,9 @@ dependencies = [ [[package]] name = "zip" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", "crc32fast", diff --git a/Cargo.toml b/Cargo.toml index 3050cf764a4c..f6a50bfa6b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.66" +rust-version = "1.70" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] @@ -35,6 +35,10 @@ debug = 0 # chalk-ir = { path = "../chalk/chalk-ir" } # chalk-recursive = { path = "../chalk/chalk-recursive" } # chalk-derive = { path = "../chalk/chalk-derive" } +# line-index = { path = "lib/line-index" } +# la-arena = { path = "lib/la-arena" } +# lsp-server = { path = "lib/lsp-server" } + # ungrammar = { path = "../ungrammar" } @@ -57,13 +61,13 @@ ide-diagnostics = { path = "./crates/ide-diagnostics", version = "0.0.0" } ide-ssr = { path = "./crates/ide-ssr", version = "0.0.0" } intern = { path = "./crates/intern", version = "0.0.0" } limit = { path = "./crates/limit", version = "0.0.0" } +load-cargo = { path = "./crates/load-cargo", version = "0.0.0" } mbe = { path = "./crates/mbe", version = "0.0.0" } parser = { path = "./crates/parser", version = "0.0.0" } paths = { path = "./crates/paths", version = "0.0.0" } proc-macro-api = { path = "./crates/proc-macro-api", version = "0.0.0" } proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" } proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" } -proc-macro-test = { path = "./crates/proc-macro-test", version = "0.0.0" } profile = { path = "./crates/profile", version = "0.0.0" } project-model = { path = "./crates/project-model", version = "0.0.0" } sourcegen = { path = "./crates/sourcegen", version = "0.0.0" } @@ -75,7 +79,14 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -line-index = { version = "0.1.0-pre.1", path = "./lib/line-index" } + +# local crates that aren't published to crates.io. These should not have versions. +proc-macro-test = { path = "./crates/proc-macro-test" } + +# In-tree crates that are published separately and follow semver. See lib/README.md +line-index = { version = "0.1.0-pre.1" } +la-arena = { version = "0.3.1" } +lsp-server = { version = "0.7.1" } # non-local crates smallvec = { version = "1.10.0", features = [ @@ -86,9 +97,10 @@ smallvec = { version = "1.10.0", features = [ smol_str = "0.2.0" nohash-hasher = "0.2.0" text-size = "1.1.0" -# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved -serde = { version = "=1.0.156", features = ["derive"] } -serde_json = "1.0.94" +serde = { version = "1.0.156", features = ["derive"] } +serde_json = "1.0.96" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } +# can't upgrade due to dashmap depending on 0.12.3 currently +hashbrown = { version = "0.12.3", features = ["inline-more"], default-features = false } rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" } diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml index 6001772c86ee..171c113a950d 100644 --- a/crates/base-db/Cargo.toml +++ b/crates/base-db/Cargo.toml @@ -17,7 +17,7 @@ rustc-hash = "1.1.0" triomphe.workspace = true -la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +la-arena.workspace = true # local deps cfg.workspace = true diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index d3abc3870b70..323ee4260e4f 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -26,7 +26,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); fixture.change.apply(&mut db); - assert_eq!(fixture.files.len(), 1); + assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture"); (db, fixture.files[0]) } @@ -102,6 +102,8 @@ pub struct ChangeFixture { pub change: Change, } +const SOURCE_ROOT_PREFIX: &str = "/"; + impl ChangeFixture { pub fn parse(ra_fixture: &str) -> ChangeFixture { Self::parse_with_proc_macros(ra_fixture, Vec::new()) @@ -131,7 +133,6 @@ impl ChangeFixture { let mut file_set = FileSet::default(); let mut current_source_root_kind = SourceRootKind::Local; - let source_root_prefix = "/".to_string(); let mut file_id = FileId(0); let mut roots = Vec::new(); @@ -151,19 +152,23 @@ impl ChangeFixture { entry.text.clone() }; - let meta = FileMeta::from(entry); - assert!(meta.path.starts_with(&source_root_prefix)); + let meta = FileMeta::from_fixture(entry, current_source_root_kind); + assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX)); if !meta.deps.is_empty() { assert!(meta.krate.is_some(), "can't specify deps without naming the crate") } - if let Some(kind) = &meta.introduce_new_source_root { - let root = match current_source_root_kind { + if let Some(kind) = meta.introduce_new_source_root { + assert!( + meta.krate.is_some(), + "new_source_root meta doesn't make sense without crate meta" + ); + let prev_kind = mem::replace(&mut current_source_root_kind, kind); + let prev_root = match prev_kind { SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)), SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)), }; - roots.push(root); - current_source_root_kind = *kind; + roots.push(prev_root); } if let Some((krate, origin, version)) = meta.krate { @@ -185,7 +190,7 @@ impl ChangeFixture { Some(toolchain), ); let prev = crates.insert(crate_name.clone(), crate_id); - assert!(prev.is_none()); + assert!(prev.is_none(), "multiple crates with same name: {}", crate_name); for dep in meta.deps { let prelude = meta.extern_prelude.contains(&dep); let dep = CrateName::normalize_dashes(&dep); @@ -219,7 +224,7 @@ impl ChangeFixture { false, CrateOrigin::Local { repo: None, name: None }, default_target_data_layout - .map(|x| x.into()) + .map(|it| it.into()) .ok_or_else(|| "target_data_layout unset".into()), Some(toolchain), ); @@ -442,51 +447,74 @@ struct FileMeta { target_data_layout: Option, } -fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option) { - if let Some((a, b)) = crate_str.split_once('@') { - let (version, origin) = match b.split_once(':') { - Some(("CratesIo", data)) => match data.split_once(',') { - Some((version, url)) => { - (version, CrateOrigin::Local { repo: Some(url.to_owned()), name: None }) - } - _ => panic!("Bad crates.io parameter: {data}"), - }, - _ => panic!("Bad string for crate origin: {b}"), - }; - (a.to_owned(), origin, Some(version.to_string())) - } else { - let crate_origin = match LangCrateOrigin::from(&*crate_str) { - LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None }, - origin => CrateOrigin::Lang(origin), - }; - (crate_str, crate_origin, None) - } -} - -impl From for FileMeta { - fn from(f: Fixture) -> FileMeta { +impl FileMeta { + fn from_fixture(f: Fixture, current_source_root_kind: SourceRootKind) -> Self { let mut cfg = CfgOptions::default(); - f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); - f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); + for (k, v) in f.cfgs { + if let Some(v) = v { + cfg.insert_key_value(k.into(), v.into()); + } else { + cfg.insert_atom(k.into()); + } + } + + let introduce_new_source_root = f.introduce_new_source_root.map(|kind| match &*kind { + "local" => SourceRootKind::Local, + "library" => SourceRootKind::Library, + invalid => panic!("invalid source root kind '{invalid}'"), + }); + let current_source_root_kind = + introduce_new_source_root.unwrap_or(current_source_root_kind); + let deps = f.deps; - FileMeta { + Self { path: f.path, - krate: f.krate.map(parse_crate), + krate: f.krate.map(|it| parse_crate(it, current_source_root_kind, f.library)), extern_prelude: f.extern_prelude.unwrap_or_else(|| deps.clone()), deps, cfg, - edition: f.edition.as_ref().map_or(Edition::CURRENT, |v| Edition::from_str(v).unwrap()), + edition: f.edition.map_or(Edition::CURRENT, |v| Edition::from_str(&v).unwrap()), env: f.env.into_iter().collect(), - introduce_new_source_root: f.introduce_new_source_root.map(|kind| match &*kind { - "local" => SourceRootKind::Local, - "library" => SourceRootKind::Library, - invalid => panic!("invalid source root kind '{invalid}'"), - }), + introduce_new_source_root, target_data_layout: f.target_data_layout, } } } +fn parse_crate( + crate_str: String, + current_source_root_kind: SourceRootKind, + explicit_non_workspace_member: bool, +) -> (String, CrateOrigin, Option) { + // syntax: + // "my_awesome_crate" + // "my_awesome_crate@0.0.1,http://example.com" + let (name, repo, version) = if let Some((name, remain)) = crate_str.split_once('@') { + let (version, repo) = + remain.split_once(',').expect("crate meta: found '@' without version and url"); + (name.to_owned(), Some(repo.to_owned()), Some(version.to_owned())) + } else { + (crate_str, None, None) + }; + + let non_workspace_member = explicit_non_workspace_member + || matches!(current_source_root_kind, SourceRootKind::Library); + + let origin = match LangCrateOrigin::from(&*name) { + LangCrateOrigin::Other => { + let name = name.clone(); + if non_workspace_member { + CrateOrigin::Library { repo, name } + } else { + CrateOrigin::Local { repo, name: Some(name) } + } + } + origin => CrateOrigin::Lang(origin), + }; + + (name, origin, version) +} + // Identity mapping #[derive(Debug)] struct IdentityProcMacroExpander; diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index f2e523675bcf..c47799f13209 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -138,12 +138,12 @@ impl ops::Deref for CrateName { } } -/// Origin of the crates. It is used in emitting monikers. +/// Origin of the crates. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CrateOrigin { - /// Crates that are from the rustc workspace + /// Crates that are from the rustc workspace. Rustc { name: String }, - /// Crates that are workspace members, + /// Crates that are workspace members. Local { repo: Option, name: Option }, /// Crates that are non member libraries. Library { repo: Option, name: String }, diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml index 0880bc239d83..ed380897278b 100644 --- a/crates/cfg/Cargo.toml +++ b/crates/cfg/Cargo.toml @@ -18,13 +18,13 @@ rustc-hash = "1.1.0" tt.workspace = true [dev-dependencies] -expect-test = "1.4.0" +expect-test = "1.4.1" oorandom = "11.1.3" # We depend on both individually instead of using `features = ["derive"]` to microoptimize the # build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr` # supports `arbitrary`. This way, we avoid feature unification. -arbitrary = "1.2.2" -derive_arbitrary = "1.2.2" +arbitrary = "1.3.0" +derive_arbitrary = "1.3.1" # local deps mbe.workspace = true diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 495119d5519c..183b9b7d278c 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -69,7 +69,7 @@ impl CfgOptions { } pub fn get_cfg_keys(&self) -> impl Iterator { - self.enabled.iter().map(|x| match x { + self.enabled.iter().map(|it| match it { CfgAtom::Flag(key) => key, CfgAtom::KeyValue { key, .. } => key, }) @@ -79,7 +79,7 @@ impl CfgOptions { &'a self, cfg_key: &'a str, ) -> impl Iterator + 'a { - self.enabled.iter().filter_map(move |x| match x { + self.enabled.iter().filter_map(move |it| match it { CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value), _ => None, }) diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 3f6671b1c43d..e7f7adc78471 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml @@ -12,9 +12,9 @@ rust-version.workspace = true doctest = false [dependencies] -crossbeam-channel = "0.5.5" +crossbeam-channel = "0.5.8" tracing = "0.1.37" -cargo_metadata = "0.15.0" +cargo_metadata = "0.15.4" rustc-hash = "1.1.0" serde_json.workspace = true serde.workspace = true diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 83c7051646ed..30307deb79b8 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -21,14 +21,14 @@ dashmap = { version = "=5.4.0", features = ["raw-api"] } drop_bomb = "0.1.5" either = "1.7.0" fst = { version = "0.4.7", default-features = false } -hashbrown = { version = "0.12.1", default-features = false } -indexmap = "1.9.1" +indexmap = "2.0.0" itertools = "0.10.5" -la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +la-arena.workspace = true once_cell = "1.17.0" rustc-hash = "1.1.0" -smallvec.workspace = true tracing = "0.1.35" +smallvec.workspace = true +hashbrown.workspace = true triomphe.workspace = true rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index bab3bbc2329a..c29446d8235a 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -137,13 +137,16 @@ impl Attrs { let cfg_options = &crate_graph[krate].cfg_options; - let Some(variant) = enum_.variants.clone().filter(|variant| { - let attrs = item_tree.attrs(db, krate, (*variant).into()); - attrs.is_cfg_enabled(cfg_options) - }) - .zip(0u32..) - .find(|(_variant, idx)| it.local_id == Idx::from_raw(RawIdx::from(*idx))) - .map(|(variant, _idx)| variant) + let Some(variant) = enum_ + .variants + .clone() + .filter(|variant| { + let attrs = item_tree.attrs(db, krate, (*variant).into()); + attrs.is_cfg_enabled(cfg_options) + }) + .zip(0u32..) + .find(|(_variant, idx)| it.local_id == Idx::from_raw(RawIdx::from(*idx))) + .map(|(variant, _idx)| variant) else { return Arc::new(res); }; @@ -272,6 +275,25 @@ impl Attrs { self.by_key("proc_macro_derive").exists() } + pub fn is_test(&self) -> bool { + self.iter().any(|it| { + it.path() + .segments() + .iter() + .rev() + .zip(["core", "prelude", "v1", "test"].iter().rev()) + .all(|it| it.0.as_str() == Some(it.1)) + }) + } + + pub fn is_ignore(&self) -> bool { + self.by_key("ignore").exists() + } + + pub fn is_bench(&self) -> bool { + self.by_key("bench").exists() + } + pub fn is_unstable(&self) -> bool { self.by_key("unstable").exists() } @@ -282,7 +304,7 @@ use std::slice::Iter as SliceIter; pub enum DocAtom { /// eg. `#[doc(hidden)]` Flag(SmolStr), - /// eg. `#[doc(alias = "x")]` + /// eg. `#[doc(alias = "it")]` /// /// Note that a key can have multiple values that are all considered "active" at the same time. /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`. @@ -462,6 +484,7 @@ impl AttrsWithOwner { } }, AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it), + AttrDefId::ExternCrateId(it) => attrs_from_item_tree_loc(db, it), }; let attrs = raw_attrs.filter(db.upcast(), def.krate(db)); @@ -546,6 +569,7 @@ impl AttrsWithOwner { .map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())), }, AttrDefId::ExternBlockId(id) => any_has_attrs(db, id), + AttrDefId::ExternCrateId(id) => any_has_attrs(db, id), }; AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs)) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 94dc39b11755..f8d492d0e525 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -273,10 +273,10 @@ impl Body { pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool { match self.binding_owners.get(&binding) { - Some(x) => { + Some(it) => { // We assign expression ids in a way that outer closures will receive // a lower id - x.into_raw() < relative_to.into_raw() + it.into_raw() < relative_to.into_raw() } None => true, } diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index b375ec63a67b..152c02743f77 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -297,11 +297,11 @@ impl ExprCollector<'_> { let (result_expr_id, prev_binding_owner) = this.initialize_binding_owner(syntax_ptr); let inner_expr = this.collect_block(e); - let x = this.db.intern_anonymous_const(ConstBlockLoc { + let it = this.db.intern_anonymous_const(ConstBlockLoc { parent: this.owner, root: inner_expr, }); - this.body.exprs[result_expr_id] = Expr::Const(x); + this.body.exprs[result_expr_id] = Expr::Const(it); this.current_binding_owner = prev_binding_owner; result_expr_id }) @@ -324,10 +324,10 @@ impl ExprCollector<'_> { ast::Expr::CallExpr(e) => { let is_rustc_box = { let attrs = e.attrs(); - attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box") + attrs.filter_map(|it| it.as_simple_atom()).any(|it| it == "rustc_box") }; if is_rustc_box { - let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next())); + let expr = self.collect_expr_opt(e.arg_list().and_then(|it| it.args().next())); self.alloc_expr(Expr::Box { expr }, syntax_ptr) } else { let callee = self.collect_expr_opt(e.expr()); @@ -781,7 +781,7 @@ impl ExprCollector<'_> { pat: self.alloc_pat_desugared(some_pat), guard: None, expr: self.with_opt_labeled_rib(label, |this| { - this.collect_expr_opt(e.loop_body().map(|x| x.into())) + this.collect_expr_opt(e.loop_body().map(|it| it.into())) }), }; let iter_name = Name::generate_new_name(); @@ -874,10 +874,10 @@ impl ExprCollector<'_> { }), guard: None, expr: { - let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone()); + let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone()); let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone()); let result = self.alloc_expr( - Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false }, + Expr::Call { callee, args: Box::new([it]), is_assignee_expr: false }, syntax_ptr.clone(), ); self.alloc_expr( @@ -1240,12 +1240,12 @@ impl ExprCollector<'_> { pats.push(self.collect_pat(first, binding_list)); binding_list.reject_new = true; for rest in it { - for (_, x) in binding_list.is_used.iter_mut() { - *x = false; + for (_, it) in binding_list.is_used.iter_mut() { + *it = false; } pats.push(self.collect_pat(rest, binding_list)); - for (&id, &x) in binding_list.is_used.iter() { - if !x { + for (&id, &is_used) in binding_list.is_used.iter() { + if !is_used { self.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); } @@ -1352,9 +1352,9 @@ impl ExprCollector<'_> { // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference. ast::Pat::RangePat(p) => { let mut range_part_lower = |p: Option| { - p.and_then(|x| match &x { - ast::Pat::LiteralPat(x) => { - Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0))) + p.and_then(|it| match &it { + ast::Pat::LiteralPat(it) => { + Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(it)?.0))) } ast::Pat::IdentPat(p) => { let name = @@ -1451,9 +1451,7 @@ impl ExprCollector<'_> { &self, lifetime: Option, ) -> Result, BodyDiagnostic> { - let Some(lifetime) = lifetime else { - return Ok(None) - }; + let Some(lifetime) = lifetime else { return Ok(None) }; let name = Name::new_lifetime(&lifetime); for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index cd6df0e6325c..0c6cf0b49a26 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -105,7 +105,7 @@ struct Printer<'a> { needs_indent: bool, } -impl<'a> Write for Printer<'a> { +impl Write for Printer<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { for line in s.split_inclusive('\n') { if self.needs_indent { @@ -125,7 +125,7 @@ impl<'a> Write for Printer<'a> { } } -impl<'a> Printer<'a> { +impl Printer<'_> { fn indented(&mut self, f: impl FnOnce(&mut Self)) { self.indent_level += 1; wln!(self); diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs index edee2c7ff96b..d5582011645d 100644 --- a/crates/hir-def/src/body/tests.rs +++ b/crates/hir-def/src/body/tests.rs @@ -3,12 +3,12 @@ mod block; use base_db::{fixture::WithFixture, SourceDatabase}; use expect_test::Expect; -use crate::ModuleDefId; +use crate::{test_db::TestDB, ModuleDefId}; use super::*; fn lower(ra_fixture: &str) -> Arc { - let db = crate::test_db::TestDB::with_files(ra_fixture); + let db = TestDB::with_files(ra_fixture); let krate = db.crate_graph().iter().next().unwrap(); let def_map = db.crate_def_map(krate); @@ -25,15 +25,15 @@ fn lower(ra_fixture: &str) -> Arc { db.body(fn_def.unwrap().into()) } -fn block_def_map_at(ra_fixture: &str) -> String { - let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); +fn def_map_at(ra_fixture: &str) -> String { + let (db, position) = TestDB::with_position(ra_fixture); let module = db.module_at_position(position); module.def_map(&db).dump(&db) } fn check_block_scopes_at(ra_fixture: &str, expect: Expect) { - let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); + let (db, position) = TestDB::with_position(ra_fixture); let module = db.module_at_position(position); let actual = module.def_map(&db).dump_block_scopes(&db); @@ -41,7 +41,7 @@ fn check_block_scopes_at(ra_fixture: &str, expect: Expect) { } fn check_at(ra_fixture: &str, expect: Expect) { - let actual = block_def_map_at(ra_fixture); + let actual = def_map_at(ra_fixture); expect.assert_eq(&actual); } diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/body/tests/block.rs index 6e77744f2153..4e015a7fbbb0 100644 --- a/crates/hir-def/src/body/tests/block.rs +++ b/crates/hir-def/src/body/tests/block.rs @@ -133,6 +133,47 @@ struct Struct {} ); } +#[test] +fn super_imports_2() { + check_at( + r#" +fn outer() { + mod m { + struct ResolveMe {} + fn middle() { + mod m2 { + fn inner() { + use super::ResolveMe; + $0 + } + } + } + } +} +"#, + expect![[r#" + block scope + ResolveMe: t + + block scope + m2: t + + block scope::m2 + inner: v + + block scope + m: t + + block scope::m + ResolveMe: t + middle: v + + crate + outer: v + "#]], + ); +} + #[test] fn nested_module_scoping() { check_block_scopes_at( @@ -155,6 +196,42 @@ fn f() { ); } +#[test] +fn self_imports() { + check_at( + r#" +fn f() { + mod m { + struct ResolveMe {} + fn g() { + fn h() { + use self::ResolveMe; + $0 + } + } + } +} +"#, + expect![[r#" + block scope + ResolveMe: t + + block scope + h: v + + block scope + m: t + + block scope::m + ResolveMe: t + g: v + + crate + f: v + "#]], + ); +} + #[test] fn legacy_macro_items() { // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 40e6a4308784..54fe9a2e8440 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -24,11 +24,12 @@ use crate::{ proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind}, DefMap, MacroSubNs, }, + path::ImportAlias, type_ref::{TraitRef, TypeBound, TypeRef}, visibility::RawVisibility, - AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, - Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId, ProcMacroId, - StaticId, TraitAliasId, TraitId, TypeAliasId, TypeAliasLoc, + AssocItemId, AstIdWithPath, ConstId, ConstLoc, ExternCrateId, FunctionId, FunctionLoc, + HasModule, ImplId, Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId, + ProcMacroId, StaticId, TraitAliasId, TraitId, TypeAliasId, TypeAliasLoc, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -424,6 +425,7 @@ impl MacroRulesData { Arc::new(MacroRulesData { name: makro.name.clone(), macro_export }) } } + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProcMacroData { pub name: Name, @@ -460,6 +462,30 @@ impl ProcMacroData { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExternCrateDeclData { + pub name: Name, + pub alias: Option, + pub visibility: RawVisibility, +} + +impl ExternCrateDeclData { + pub(crate) fn extern_crate_decl_data_query( + db: &dyn DefDatabase, + extern_crate: ExternCrateId, + ) -> Arc { + let loc = extern_crate.lookup(db); + let item_tree = loc.id.item_tree(db); + let extern_crate = &item_tree[loc.id.value]; + + Arc::new(Self { + name: extern_crate.name.clone(), + visibility: item_tree[extern_crate.visibility].clone(), + alias: extern_crate.alias.clone(), + }) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConstData { /// `None` for `const _: () = ();` @@ -573,7 +599,7 @@ impl<'a> AssocItemCollector<'a> { if !attrs.is_cfg_enabled(self.expander.cfg_options()) { self.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id.local_id, - InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()), + InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).erase()), attrs.cfg().unwrap(), self.expander.cfg_options().clone(), )); diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 6db5abccc921..c8df3f3f96a5 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -18,7 +18,6 @@ use triomphe::Arc; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, - expander::CfgExpander, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, lang_item::LangItem, lower::LowerCtx, @@ -29,8 +28,8 @@ use crate::{ tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, type_ref::TypeRef, visibility::RawVisibility, - EnumId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, UnionId, - VariantId, + EnumId, EnumLoc, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, + UnionId, VariantId, }; /// Note that we use `StructData` for unions as well! @@ -76,6 +75,7 @@ pub struct EnumData { pub struct EnumVariantData { pub name: Name, pub variant_data: Arc, + pub tree_id: la_arena::Idx, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -147,6 +147,7 @@ fn parse_repr_tt(tt: &Subtree) -> Option { } "C" => ReprFlags::IS_C, "transparent" => ReprFlags::IS_TRANSPARENT, + "simd" => ReprFlags::IS_SIMD, repr => { if let Some(builtin) = BuiltinInt::from_suffix(repr) .map(Either::Left) @@ -325,11 +326,12 @@ impl EnumData { variants.alloc(EnumVariantData { name: var.name.clone(), variant_data: Arc::new(var_data), + tree_id, }); } else { diagnostics.push(DefDiagnostic::unconfigured_code( loc.container.local_id, - InFile::new(loc.id.file_id(), var.ast_id.upcast()), + InFile::new(loc.id.file_id(), var.ast_id.erase()), attrs.cfg().unwrap(), cfg_options.clone(), )) @@ -367,9 +369,10 @@ impl HasChildSource for EnumId { &self, db: &dyn DefDatabase, ) -> InFile> { - let src = self.lookup(db).source(db); + let loc = &self.lookup(db); + let src = loc.source(db); let mut trace = Trace::new_for_map(); - lower_enum(db, &mut trace, &src, self.lookup(db).container); + lower_enum(db, &mut trace, &src, loc); src.with_value(trace.into_map()) } } @@ -378,31 +381,58 @@ fn lower_enum( db: &dyn DefDatabase, trace: &mut Trace, ast: &InFile, - module_id: ModuleId, + loc: &EnumLoc, ) { - let expander = CfgExpander::new(db, ast.file_id, module_id.krate); + let item_tree = loc.id.item_tree(db); + let krate = loc.container.krate; + + let item_tree_variants = item_tree[loc.id.value].variants.clone(); + + let cfg_options = &db.crate_graph()[krate].cfg_options; let variants = ast .value .variant_list() .into_iter() .flat_map(|it| it.variants()) - .filter(|var| expander.is_cfg_enabled(db, var)); - for var in variants { + .zip(item_tree_variants) + .filter(|&(_, item_tree_id)| { + item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) + }); + for (var, item_tree_id) in variants { trace.alloc( || var.clone(), || EnumVariantData { name: var.name().map_or_else(Name::missing, |it| it.as_name()), - variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)), + variant_data: Arc::new(VariantData::new( + db, + ast.with_value(var.kind()), + loc.container, + &item_tree, + item_tree_id, + )), + tree_id: item_tree_id, }, ); } } impl VariantData { - fn new(db: &dyn DefDatabase, flavor: InFile, module_id: ModuleId) -> Self { - let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate); + fn new( + db: &dyn DefDatabase, + flavor: InFile, + module_id: ModuleId, + item_tree: &ItemTree, + variant: la_arena::Idx, + ) -> Self { let mut trace = Trace::new_for_arena(); - match lower_struct(db, &mut expander, &mut trace, &flavor) { + match lower_struct( + db, + &mut trace, + &flavor, + module_id.krate, + item_tree, + &item_tree[variant].fields, + ) { StructKind::Tuple => VariantData::Tuple(trace.into_arena()), StructKind::Record => VariantData::Record(trace.into_arena()), StructKind::Unit => VariantData::Unit, @@ -434,28 +464,43 @@ impl HasChildSource for VariantId { type Value = Either; fn child_source(&self, db: &dyn DefDatabase) -> InFile> { - let (src, module_id) = match self { + let item_tree; + let (src, fields, container) = match *self { VariantId::EnumVariantId(it) => { // I don't really like the fact that we call into parent source // here, this might add to more queries then necessary. + let lookup = it.parent.lookup(db); + item_tree = lookup.id.item_tree(db); let src = it.parent.child_source(db); - (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container) + let tree_id = db.enum_data(it.parent).variants[it.local_id].tree_id; + let fields = &item_tree[tree_id].fields; + (src.map(|map| map[it.local_id].kind()), fields, lookup.container) } VariantId::StructId(it) => { - (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container) + let lookup = it.lookup(db); + item_tree = lookup.id.item_tree(db); + ( + lookup.source(db).map(|it| it.kind()), + &item_tree[lookup.id.value].fields, + lookup.container, + ) + } + VariantId::UnionId(it) => { + let lookup = it.lookup(db); + item_tree = lookup.id.item_tree(db); + ( + lookup.source(db).map(|it| { + it.record_field_list() + .map(ast::StructKind::Record) + .unwrap_or(ast::StructKind::Unit) + }), + &item_tree[lookup.id.value].fields, + lookup.container, + ) } - VariantId::UnionId(it) => ( - it.lookup(db).source(db).map(|it| { - it.record_field_list() - .map(ast::StructKind::Record) - .unwrap_or(ast::StructKind::Unit) - }), - it.lookup(db).container, - ), }; - let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); let mut trace = Trace::new_for_map(); - lower_struct(db, &mut expander, &mut trace, &src); + lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields); src.with_value(trace.into_map()) } } @@ -469,16 +514,19 @@ pub enum StructKind { fn lower_struct( db: &dyn DefDatabase, - expander: &mut CfgExpander, trace: &mut Trace>, ast: &InFile, + krate: CrateId, + item_tree: &ItemTree, + fields: &Fields, ) -> StructKind { - let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id); + let ctx = LowerCtx::with_file_id(db, ast.file_id); - match &ast.value { - ast::StructKind::Tuple(fl) => { - for (i, fd) in fl.fields().enumerate() { - if !expander.is_cfg_enabled(db, &fd) { + match (&ast.value, fields) { + (ast::StructKind::Tuple(fl), Fields::Tuple(fields)) => { + let cfg_options = &db.crate_graph()[krate].cfg_options; + for ((i, fd), item_tree_id) in fl.fields().enumerate().zip(fields.clone()) { + if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) { continue; } @@ -493,9 +541,10 @@ fn lower_struct( } StructKind::Tuple } - ast::StructKind::Record(fl) => { - for fd in fl.fields() { - if !expander.is_cfg_enabled(db, &fd) { + (ast::StructKind::Record(fl), Fields::Record(fields)) => { + let cfg_options = &db.crate_graph()[krate].cfg_options; + for (fd, item_tree_id) in fl.fields().zip(fields.clone()) { + if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) { continue; } @@ -510,7 +559,7 @@ fn lower_struct( } StructKind::Record } - ast::StructKind::Unit => StructKind::Unit, + _ => StructKind::Unit, } } @@ -539,8 +588,8 @@ fn lower_fields( InFile::new( current_file_id, match field.ast_id { - FieldAstId::Record(it) => it.upcast(), - FieldAstId::Tuple(it) => it.upcast(), + FieldAstId::Record(it) => it.erase(), + FieldAstId::Tuple(it) => it.erase(), }, ), attrs.cfg().unwrap(), @@ -563,8 +612,8 @@ fn lower_fields( InFile::new( current_file_id, match field.ast_id { - FieldAstId::Record(it) => it.upcast(), - FieldAstId::Tuple(it) => it.upcast(), + FieldAstId::Record(it) => it.erase(), + FieldAstId::Tuple(it) => it.erase(), }, ), attrs.cfg().unwrap(), diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 04ec47f84caa..82e6dfb30c81 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -12,27 +12,31 @@ use crate::{ body::{scope::ExprScopes, Body, BodySourceMap}, data::{ adt::{EnumData, StructData}, - ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData, - TraitAliasData, TraitData, TypeAliasData, + ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData, + ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData, }, generics::GenericParams, import_map::ImportMap, item_tree::{AttrOwner, ItemTree}, - lang_item::{LangItem, LangItemTarget, LangItems}, + lang_item::{self, LangItem, LangItemTarget, LangItems}, nameres::{diagnostics::DefDiagnostic, DefMap}, visibility::{self, Visibility}, AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, - EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, - ImplLoc, InTypeConstId, InTypeConstLoc, LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, - MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, - StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, - UnionLoc, VariantId, + EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, + FunctionLoc, GenericDefId, ImplId, ImplLoc, ImportId, ImportLoc, InTypeConstId, InTypeConstLoc, + LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, + ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, + TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, }; #[salsa::query_group(InternDatabaseStorage)] pub trait InternDatabase: SourceDatabase { // region: items #[salsa::interned] + fn intern_import(&self, loc: ImportLoc) -> ImportId; + #[salsa::interned] + fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId; + #[salsa::interned] fn intern_function(&self, loc: FunctionLoc) -> FunctionId; #[salsa::interned] fn intern_struct(&self, loc: StructLoc) -> StructId; @@ -160,6 +164,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; + #[salsa::invoke(ExternCrateDeclData::extern_crate_decl_data_query)] + fn extern_crate_decl_data(&self, extern_crate: ExternCrateId) -> Arc; + // endregion:data #[salsa::invoke(Body::body_with_source_map_query)] @@ -197,6 +204,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Attrs; + #[salsa::invoke(lang_item::lang_attr_query)] + fn lang_attr(&self, def: AttrDefId) -> Option; + #[salsa::transparent] #[salsa::invoke(AttrsWithOwner::attrs_with_owner)] fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner; diff --git a/crates/hir-def/src/dyn_map/keys.rs b/crates/hir-def/src/dyn_map/keys.rs index f30be6b64e3d..4197d0106082 100644 --- a/crates/hir-def/src/dyn_map/keys.rs +++ b/crates/hir-def/src/dyn_map/keys.rs @@ -8,8 +8,8 @@ use syntax::{ast, AstNode, AstPtr}; use crate::{ dyn_map::{DynMap, Policy}, - ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id, - MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, + ConstId, EnumId, EnumVariantId, ExternCrateId, FieldId, FunctionId, ImplId, LifetimeParamId, + Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, }; @@ -25,6 +25,7 @@ pub const TRAIT_ALIAS: Key = Key::new(); pub const STRUCT: Key = Key::new(); pub const UNION: Key = Key::new(); pub const ENUM: Key = Key::new(); +pub const EXTERN_CRATE: Key = Key::new(); pub const VARIANT: Key = Key::new(); pub const TUPLE_FIELD: Key = Key::new(); diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs index a588827c8d39..cc85bd893ac4 100644 --- a/crates/hir-def/src/expander.rs +++ b/crates/hir-def/src/expander.rs @@ -15,18 +15,11 @@ use crate::{ MacroId, ModuleId, }; -/// A subset of Expander that only deals with cfg attributes. We only need it to -/// avoid cyclic queries in crate def map during enum processing. #[derive(Debug)] -pub(crate) struct CfgExpander { +pub struct Expander { cfg_options: CfgOptions, hygiene: Hygiene, krate: CrateId, -} - -#[derive(Debug)] -pub struct Expander { - cfg_expander: CfgExpander, pub(crate) current_file_id: HirFileId, pub(crate) module: ModuleId, /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. @@ -34,41 +27,23 @@ pub struct Expander { recursion_limit: Limit, } -impl CfgExpander { - pub(crate) fn new( - db: &dyn DefDatabase, - current_file_id: HirFileId, - krate: CrateId, - ) -> CfgExpander { - let hygiene = Hygiene::new(db.upcast(), current_file_id); - let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - CfgExpander { cfg_options, hygiene, krate } - } - - pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) - } - - pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool { - let attrs = self.parse_attrs(db, owner); - attrs.is_cfg_enabled(&self.cfg_options) - } - - pub(crate) fn hygiene(&self) -> &Hygiene { - &self.hygiene - } -} - impl Expander { pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { - let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); let recursion_limit = db.recursion_limit(module.krate); #[cfg(not(test))] let recursion_limit = Limit::new(recursion_limit as usize); // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug #[cfg(test)] let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize)); - Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit } + Expander { + current_file_id, + module, + recursion_depth: 0, + recursion_limit, + cfg_options: db.crate_graph()[module.krate].cfg_options.clone(), + hygiene: Hygiene::new(db.upcast(), current_file_id), + krate: module.krate, + } } pub fn enter_expand( @@ -120,7 +95,7 @@ impl Expander { } pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); + self.hygiene = Hygiene::new(db.upcast(), mark.file_id); self.current_file_id = mark.file_id; if self.recursion_depth == u32::MAX { // Recursion limit has been reached somewhere in the macro expansion tree. Reset the @@ -135,7 +110,7 @@ impl Expander { } pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> { - LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id) + LowerCtx::new(db, &self.hygiene, self.current_file_id) } pub(crate) fn to_source(&self, value: T) -> InFile { @@ -143,11 +118,11 @@ impl Expander { } pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - self.cfg_expander.parse_attrs(db, owner) + Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) } pub(crate) fn cfg_options(&self) -> &CfgOptions { - &self.cfg_expander.cfg_options + &self.cfg_options } pub fn current_file_id(&self) -> HirFileId { @@ -155,7 +130,7 @@ impl Expander { } pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option { - let ctx = LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id); + let ctx = LowerCtx::new(db, &self.hygiene, self.current_file_id); Path::from_src(path, &ctx) } @@ -194,7 +169,7 @@ impl Expander { let parse = value.cast::()?; self.recursion_depth += 1; - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); + self.hygiene = Hygiene::new(db.upcast(), file_id); let old_file_id = std::mem::replace(&mut self.current_file_id, file_id); let mark = Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") }; diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 8c49ae1c4afe..df2af4c89b08 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -360,7 +360,7 @@ fn calculate_best_path( prefer_no_std, )?; cov_mark::hit!(partially_imported); - path.push_segment(info.path.segments.last()?.clone()); + path.push_segment(info.name.clone()); Some(path) }) }); diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index f19c3f028f42..d7d44e413880 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -67,21 +67,21 @@ pub enum TypeOrConstParamData { impl TypeOrConstParamData { pub fn name(&self) -> Option<&Name> { match self { - TypeOrConstParamData::TypeParamData(x) => x.name.as_ref(), - TypeOrConstParamData::ConstParamData(x) => Some(&x.name), + TypeOrConstParamData::TypeParamData(it) => it.name.as_ref(), + TypeOrConstParamData::ConstParamData(it) => Some(&it.name), } } pub fn has_default(&self) -> bool { match self { - TypeOrConstParamData::TypeParamData(x) => x.default.is_some(), - TypeOrConstParamData::ConstParamData(x) => x.has_default, + TypeOrConstParamData::TypeParamData(it) => it.default.is_some(), + TypeOrConstParamData::ConstParamData(it) => it.has_default, } } pub fn type_param(&self) -> Option<&TypeParamData> { match self { - TypeOrConstParamData::TypeParamData(x) => Some(x), + TypeOrConstParamData::TypeParamData(it) => Some(it), TypeOrConstParamData::ConstParamData(_) => None, } } @@ -89,14 +89,14 @@ impl TypeOrConstParamData { pub fn const_param(&self) -> Option<&ConstParamData> { match self { TypeOrConstParamData::TypeParamData(_) => None, - TypeOrConstParamData::ConstParamData(x) => Some(x), + TypeOrConstParamData::ConstParamData(it) => Some(it), } } pub fn is_trait_self(&self) -> bool { match self { - TypeOrConstParamData::TypeParamData(x) => { - x.provenance == TypeParamProvenance::TraitSelf + TypeOrConstParamData::TypeParamData(it) => { + it.provenance == TypeParamProvenance::TraitSelf } TypeOrConstParamData::ConstParamData(_) => false, } diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index fa1f4933a266..57f023ef35d4 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -425,8 +425,8 @@ impl ConstRef { } match expr { ast::Expr::PathExpr(p) if is_path_ident(&p) => { - match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) { - Some(x) => Self::Path(x.as_name()), + match p.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) { + Some(it) => Self::Path(it.as_name()), None => Self::Scalar(LiteralConstRef::Unknown), } } diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 48532655e04b..4b2e5041a126 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -1,13 +1,14 @@ //! A map of all publicly exported items in a crate. +use std::collections::hash_map::Entry; use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; use fst::{self, Streamer}; use hir_expand::name::Name; -use indexmap::{map::Entry, IndexMap}; +use indexmap::IndexMap; use itertools::Itertools; -use rustc_hash::{FxHashSet, FxHasher}; +use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use triomphe::Arc; use crate::{ @@ -17,52 +18,23 @@ use crate::{ type FxIndexMap = IndexMap>; +// FIXME: Support aliases: an item may be exported under multiple names, so `ImportInfo` should +// have `Vec<(Name, ModuleId)>` instead of `(Name, ModuleId)`. /// Item import details stored in the `ImportMap`. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ImportInfo { - /// A path that can be used to import the item, relative to the crate's root. - pub path: ImportPath, + /// A name that can be used to import the item, relative to the crate's root. + pub name: Name, /// The module containing this item. pub container: ModuleId, /// Whether the import is a trait associated item or not. pub is_trait_assoc_item: bool, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ImportPath { - pub segments: Vec, -} - -impl ImportPath { - pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a { - struct Display<'a> { - db: &'a dyn DefDatabase, - path: &'a ImportPath, - } - impl fmt::Display for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt( - &self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"), - f, - ) - } - } - Display { db, path: self } - } - - fn len(&self) -> usize { - self.segments.len() - } -} - -/// A map from publicly exported items to the path needed to import/name them from a downstream -/// crate. +/// A map from publicly exported items to its name. /// /// Reexports of items are taken into account, ie. if something is exported under multiple /// names, the one with the shortest import path will be used. -/// -/// Note that all paths are relative to the containing crate's root, so the crate name still needs -/// to be prepended to the `ModPath` before the path is valid. #[derive(Default)] pub struct ImportMap { map: FxIndexMap, @@ -70,122 +42,58 @@ pub struct ImportMap { /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. /// - /// Since a path can refer to multiple items due to namespacing, we store all items with the - /// same path right after each other. This allows us to find all items after the FST gives us + /// Since a name can refer to multiple items due to namespacing, we store all items with the + /// same name right after each other. This allows us to find all items after the FST gives us /// the index of the first one. importables: Vec, fst: fst::Map>, } impl ImportMap { - pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { + pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = profile::span("import_map_query"); - let mut import_map = collect_import_map(db, krate); + let map = collect_import_map(db, krate); - let mut importables = import_map - .map + let mut importables: Vec<_> = map .iter() - .map(|(item, info)| (item, fst_path(db, &info.path))) - .collect::>(); - importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2)); + // We've only collected items, whose name cannot be tuple field. + .map(|(&item, info)| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + .collect(); + importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name)); // Build the FST, taking care not to insert duplicate values. - let mut builder = fst::MapBuilder::memory(); - let mut last_batch_start = 0; - - for idx in 0..importables.len() { - let key = &importables[last_batch_start].1; - if let Some((_, fst_path)) = importables.get(idx + 1) { - if key == fst_path { - continue; - } - } - - let _ = builder.insert(key, last_batch_start as u64); - - last_batch_start = idx + 1; + let iter = importables.iter().enumerate().dedup_by(|lhs, rhs| lhs.1 .1 == rhs.1 .1); + for (start_idx, (_, name)) in iter { + let _ = builder.insert(name, start_idx as u64); } - import_map.fst = builder.into_map(); - import_map.importables = importables.iter().map(|&(&item, _)| item).collect(); - - Arc::new(import_map) - } - - /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. - pub fn path_of(&self, item: ItemInNs) -> Option<&ImportPath> { - self.import_info_for(item).map(|it| &it.path) + Arc::new(ImportMap { + map, + fst: builder.into_map(), + importables: importables.into_iter().map(|(item, _)| item).collect(), + }) } pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { self.map.get(&item) } - - #[cfg(test)] - fn fmt_for_test(&self, db: &dyn DefDatabase) -> String { - let mut importable_paths: Vec<_> = self - .map - .iter() - .map(|(item, info)| { - let ns = match item { - ItemInNs::Types(_) => "t", - ItemInNs::Values(_) => "v", - ItemInNs::Macros(_) => "m", - }; - format!("- {} ({ns})", info.path.display(db)) - }) - .collect(); - - importable_paths.sort(); - importable_paths.join("\n") - } - - fn collect_trait_assoc_items( - &mut self, - db: &dyn DefDatabase, - tr: TraitId, - is_type_in_ns: bool, - original_import_info: &ImportInfo, - ) { - let _p = profile::span("collect_trait_assoc_items"); - for (assoc_item_name, item) in &db.trait_data(tr).items { - let module_def_id = match item { - AssocItemId::FunctionId(f) => ModuleDefId::from(*f), - AssocItemId::ConstId(c) => ModuleDefId::from(*c), - // cannot use associated type aliases directly: need a `::TypeAlias` - // qualifier, ergo no need to store it for imports in import_map - AssocItemId::TypeAliasId(_) => { - cov_mark::hit!(type_aliases_ignored); - continue; - } - }; - let assoc_item = if is_type_in_ns { - ItemInNs::Types(module_def_id) - } else { - ItemInNs::Values(module_def_id) - }; - - let mut assoc_item_info = original_import_info.clone(); - assoc_item_info.path.segments.push(assoc_item_name.to_owned()); - assoc_item_info.is_trait_assoc_item = true; - self.map.insert(assoc_item, assoc_item_info); - } - } } -fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { +fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap { let _p = profile::span("collect_import_map"); let def_map = db.crate_def_map(krate); - let mut import_map = ImportMap::default(); + let mut map = FxIndexMap::default(); // We look only into modules that are public(ly reexported), starting with the crate root. - let empty = ImportPath { segments: vec![] }; let root = def_map.module_id(DefMap::ROOT); - let mut worklist = vec![(root, empty)]; - while let Some((module, mod_path)) = worklist.pop() { + let mut worklist = vec![(root, 0)]; + // Records items' minimum module depth. + let mut depth_map = FxHashMap::default(); + + while let Some((module, depth)) = worklist.pop() { let ext_def_map; let mod_data = if module.krate == krate { &def_map[module.local_id] @@ -201,52 +109,83 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { }); for (name, per_ns) in visible_items { - let mk_path = || { - let mut path = mod_path.clone(); - path.segments.push(name.clone()); - path - }; - for item in per_ns.iter_items() { - let path = mk_path(); - let path_len = path.len(); - let import_info = - ImportInfo { path, container: module, is_trait_assoc_item: false }; + let import_info = ImportInfo { + name: name.clone(), + container: module, + is_trait_assoc_item: false, + }; - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { - import_map.collect_trait_assoc_items( - db, - tr, - matches!(item, ItemInNs::Types(_)), - &import_info, - ); - } - - match import_map.map.entry(item) { + match depth_map.entry(item) { Entry::Vacant(entry) => { - entry.insert(import_info); + entry.insert(depth); } Entry::Occupied(mut entry) => { - // If the new path is shorter, prefer that one. - if path_len < entry.get().path.len() { - *entry.get_mut() = import_info; + if depth < *entry.get() { + entry.insert(depth); } else { continue; } } } - // If we've just added a path to a module, descend into it. We might traverse - // modules multiple times, but only if the new path to it is shorter than the - // first (else we `continue` above). + if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { + collect_trait_assoc_items( + db, + &mut map, + tr, + matches!(item, ItemInNs::Types(_)), + &import_info, + ); + } + + map.insert(item, import_info); + + // If we've just added a module, descend into it. We might traverse modules + // multiple times, but only if the module depth is smaller (else we `continue` + // above). if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { - worklist.push((mod_id, mk_path())); + worklist.push((mod_id, depth + 1)); } } } } - import_map + map +} + +fn collect_trait_assoc_items( + db: &dyn DefDatabase, + map: &mut FxIndexMap, + tr: TraitId, + is_type_in_ns: bool, + trait_import_info: &ImportInfo, +) { + let _p = profile::span("collect_trait_assoc_items"); + for (assoc_item_name, item) in &db.trait_data(tr).items { + let module_def_id = match item { + AssocItemId::FunctionId(f) => ModuleDefId::from(*f), + AssocItemId::ConstId(c) => ModuleDefId::from(*c), + // cannot use associated type aliases directly: need a `::TypeAlias` + // qualifier, ergo no need to store it for imports in import_map + AssocItemId::TypeAliasId(_) => { + cov_mark::hit!(type_aliases_ignored); + continue; + } + }; + let assoc_item = if is_type_in_ns { + ItemInNs::Types(module_def_id) + } else { + ItemInNs::Values(module_def_id) + }; + + let assoc_item_info = ImportInfo { + container: trait_import_info.container, + name: assoc_item_name.clone(), + is_trait_assoc_item: true, + }; + map.insert(assoc_item, assoc_item_info); + } } impl PartialEq for ImportMap { @@ -260,7 +199,7 @@ impl Eq for ImportMap {} impl fmt::Debug for ImportMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut importable_paths: Vec<_> = self + let mut importable_names: Vec<_> = self .map .iter() .map(|(item, _)| match item { @@ -270,56 +209,40 @@ impl fmt::Debug for ImportMap { }) .collect(); - importable_paths.sort(); - f.write_str(&importable_paths.join("\n")) + importable_names.sort(); + f.write_str(&importable_names.join("\n")) } } -fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String { - let _p = profile::span("fst_path"); - let mut s = path.display(db).to_string(); - s.make_ascii_lowercase(); - s -} - -#[derive(Debug, Eq, PartialEq, Hash)] -pub enum ImportKind { - Module, - Function, - Adt, - EnumVariant, - Const, - Static, - Trait, - TraitAlias, - TypeAlias, - BuiltinType, - AssociatedItem, - Macro, -} - /// A way to match import map contents against the search query. #[derive(Debug)] -pub enum SearchMode { +enum SearchMode { /// Import map entry should strictly match the query string. - Equals, - /// Import map entry should contain the query string. - Contains, + Exact, /// Import map entry should contain all letters from the query string, /// in the same order, but not necessary adjacent. Fuzzy, } +/// Three possible ways to search for the name in associated and/or other items. +#[derive(Debug, Clone, Copy)] +pub enum AssocSearchMode { + /// Search for the name in both associated and other items. + Include, + /// Search for the name in other items only. + Exclude, + /// Search for the name in the associated items only. + AssocItemsOnly, +} + #[derive(Debug)] pub struct Query { query: String, lowercased: String, - name_only: bool, - assoc_items_only: bool, search_mode: SearchMode, + assoc_mode: AssocSearchMode, case_sensitive: bool, limit: usize, - exclude_import_kinds: FxHashSet, } impl Query { @@ -328,30 +251,21 @@ impl Query { Self { query, lowercased, - name_only: false, - assoc_items_only: false, - search_mode: SearchMode::Contains, + search_mode: SearchMode::Exact, + assoc_mode: AssocSearchMode::Include, case_sensitive: false, - limit: usize::max_value(), - exclude_import_kinds: FxHashSet::default(), + limit: usize::MAX, } } - /// Matches entries' names only, ignoring the rest of - /// the qualifier. - /// Example: for `std::marker::PhantomData`, the name is `PhantomData`. - pub fn name_only(self) -> Self { - Self { name_only: true, ..self } + /// Fuzzy finds items instead of exact matching. + pub fn fuzzy(self) -> Self { + Self { search_mode: SearchMode::Fuzzy, ..self } } - /// Matches only the entries that are associated items, ignoring the rest. - pub fn assoc_items_only(self) -> Self { - Self { assoc_items_only: true, ..self } - } - - /// Specifies the way to search for the entries using the query. - pub fn search_mode(self, search_mode: SearchMode) -> Self { - Self { search_mode, ..self } + /// Specifies whether we want to include associated items in the result. + pub fn assoc_search_mode(self, assoc_mode: AssocSearchMode) -> Self { + Self { assoc_mode, ..self } } /// Limits the returned number of items to `limit`. @@ -364,12 +278,6 @@ impl Query { Self { case_sensitive: true, ..self } } - /// Do not include imports of the specified kind in the search results. - pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self { - self.exclude_import_kinds.insert(import_kind); - self - } - fn import_matches( &self, db: &dyn DefDatabase, @@ -377,49 +285,36 @@ impl Query { enforce_lowercase: bool, ) -> bool { let _p = profile::span("import_map::Query::import_matches"); - if import.is_trait_assoc_item { - if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { - return false; - } - } else if self.assoc_items_only { - return false; + match (import.is_trait_assoc_item, self.assoc_mode) { + (true, AssocSearchMode::Exclude) => return false, + (false, AssocSearchMode::AssocItemsOnly) => return false, + _ => {} } - let mut input = if import.is_trait_assoc_item || self.name_only { - import.path.segments.last().unwrap().display(db.upcast()).to_string() - } else { - import.path.display(db).to_string() - }; - if enforce_lowercase || !self.case_sensitive { + let mut input = import.name.display(db.upcast()).to_string(); + let case_insensitive = enforce_lowercase || !self.case_sensitive; + if case_insensitive { input.make_ascii_lowercase(); } - let query_string = - if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased }; + let query_string = if case_insensitive { &self.lowercased } else { &self.query }; match self.search_mode { - SearchMode::Equals => &input == query_string, - SearchMode::Contains => input.contains(query_string), + SearchMode::Exact => &input == query_string, SearchMode::Fuzzy => { - let mut unchecked_query_chars = query_string.chars(); - let mut mismatching_query_char = unchecked_query_chars.next(); - - for input_char in input.chars() { - match mismatching_query_char { - None => return true, - Some(matching_query_char) if matching_query_char == input_char => { - mismatching_query_char = unchecked_query_chars.next(); - } - _ => (), + let mut input_chars = input.chars(); + for query_char in query_string.chars() { + if input_chars.find(|&it| it == query_char).is_none() { + return false; } } - mismatching_query_char.is_none() + true } } } } -/// Searches dependencies of `krate` for an importable path matching `query`. +/// Searches dependencies of `krate` for an importable name matching `query`. /// /// This returns a list of items that could be imported from dependencies of `krate`. pub fn search_dependencies( @@ -442,65 +337,44 @@ pub fn search_dependencies( let mut stream = op.union(); - let mut all_indexed_values = FxHashSet::default(); - while let Some((_, indexed_values)) = stream.next() { - all_indexed_values.extend(indexed_values.iter().copied()); - } - let mut res = FxHashSet::default(); - for indexed_value in all_indexed_values { - let import_map = &import_maps[indexed_value.index]; - let importables = &import_map.importables[indexed_value.value as usize..]; + while let Some((_, indexed_values)) = stream.next() { + for indexed_value in indexed_values { + let import_map = &import_maps[indexed_value.index]; + let importables = &import_map.importables[indexed_value.value as usize..]; - let common_importable_data = &import_map.map[&importables[0]]; - if !query.import_matches(db, common_importable_data, true) { - continue; - } + let common_importable_data = &import_map.map[&importables[0]]; + if !query.import_matches(db, common_importable_data, true) { + continue; + } - // Path shared by the importable items in this group. - let common_importables_path_fst = fst_path(db, &common_importable_data.path); - // Add the items from this `ModPath` group. Those are all subsequent items in - // `importables` whose paths match `path`. - let iter = importables - .iter() - .copied() - .take_while(|item| { - common_importables_path_fst == fst_path(db, &import_map.map[item].path) - }) - .filter(|&item| match item_import_kind(item) { - Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), - None => true, - }) - .filter(|item| { - !query.case_sensitive // we've already checked the common importables path case-insensitively + // Name shared by the importable items in this group. + let common_importable_name = + common_importable_data.name.to_smol_str().to_ascii_lowercase(); + // Add the items from this name group. Those are all subsequent items in + // `importables` whose name match `common_importable_name`. + let iter = importables + .iter() + .copied() + .take_while(|item| { + common_importable_name + == import_map.map[item].name.to_smol_str().to_ascii_lowercase() + }) + .filter(|item| { + !query.case_sensitive // we've already checked the common importables name case-insensitively || query.import_matches(db, &import_map.map[item], false) - }); - res.extend(iter); + }); + res.extend(iter); - if res.len() >= query.limit { - return res; + if res.len() >= query.limit { + return res; + } } } res } -fn item_import_kind(item: ItemInNs) -> Option { - Some(match item.as_module_def_id()? { - ModuleDefId::ModuleId(_) => ImportKind::Module, - ModuleDefId::FunctionId(_) => ImportKind::Function, - ModuleDefId::AdtId(_) => ImportKind::Adt, - ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant, - ModuleDefId::ConstId(_) => ImportKind::Const, - ModuleDefId::StaticId(_) => ImportKind::Static, - ModuleDefId::TraitId(_) => ImportKind::Trait, - ModuleDefId::TraitAliasId(_) => ImportKind::TraitAlias, - ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias, - ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType, - ModuleDefId::MacroId(_) => ImportKind::Macro, - }) -} - #[cfg(test)] mod tests { use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; @@ -510,16 +384,39 @@ mod tests { use super::*; + impl ImportMap { + fn fmt_for_test(&self, db: &dyn DefDatabase) -> String { + let mut importable_paths: Vec<_> = self + .map + .iter() + .map(|(item, info)| { + let path = render_path(db, info); + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {path} ({ns})") + }) + .collect(); + + importable_paths.sort(); + importable_paths.join("\n") + } + } + fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) { let db = TestDB::with_files(ra_fixture); let crate_graph = db.crate_graph(); let krate = crate_graph .iter() - .find(|krate| { - crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) - == Some(crate_name.to_string()) + .find(|&krate| { + crate_graph[krate] + .display_name + .as_ref() + .is_some_and(|it| &**it.crate_name() == crate_name) }) - .unwrap(); + .expect("could not find crate"); let actual = search_dependencies(db.upcast(), krate, query) .into_iter() @@ -530,7 +427,7 @@ mod tests { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { Some(assoc_item_path) => (assoc_item_path, "a"), None => ( - dependency_imports.path_of(dependency)?.display(&db).to_string(), + render_path(&db, dependency_imports.import_info_for(dependency)?), match dependency { ItemInNs::Types(ModuleDefId::FunctionId(_)) | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", @@ -560,57 +457,25 @@ mod tests { dependency_imports: &ImportMap, dependency: ItemInNs, ) -> Option { - let dependency_assoc_item_id = match dependency { - ItemInNs::Types(ModuleDefId::FunctionId(id)) - | ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id), - ItemInNs::Types(ModuleDefId::ConstId(id)) - | ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id), - ItemInNs::Types(ModuleDefId::TypeAliasId(id)) - | ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id), + let (dependency_assoc_item_id, container) = match dependency.as_module_def_id()? { + ModuleDefId::FunctionId(id) => (AssocItemId::from(id), id.lookup(db).container), + ModuleDefId::ConstId(id) => (AssocItemId::from(id), id.lookup(db).container), + ModuleDefId::TypeAliasId(id) => (AssocItemId::from(id), id.lookup(db).container), _ => return None, }; - let trait_ = assoc_to_trait(db, dependency)?; - if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? { - let trait_data = db.trait_data(tr); - let assoc_item_name = - trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| { - if &dependency_assoc_item_id == assoc_item_id { - Some(assoc_item_name) - } else { - None - } - })?; - return Some(format!( - "{}::{}", - dependency_imports.path_of(trait_)?.display(db), - assoc_item_name.display(db.upcast()) - )); - } - None - } - - fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option { - let assoc: AssocItemId = match item { - ItemInNs::Types(it) | ItemInNs::Values(it) => match it { - ModuleDefId::TypeAliasId(it) => it.into(), - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - _ => return None, - }, - _ => return None, + let ItemContainerId::TraitId(trait_id) = container else { + return None; }; - let container = match assoc { - AssocItemId::FunctionId(it) => it.lookup(db).container, - AssocItemId::ConstId(it) => it.lookup(db).container, - AssocItemId::TypeAliasId(it) => it.lookup(db).container, - }; + let trait_info = dependency_imports.import_info_for(ItemInNs::Types(trait_id.into()))?; - match container { - ItemContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())), - _ => None, - } + let trait_data = db.trait_data(trait_id); + let (assoc_item_name, _) = trait_data + .items + .iter() + .find(|(_, assoc_item_id)| &dependency_assoc_item_id == assoc_item_id)?; + Some(format!("{}::{}", render_path(db, trait_info), assoc_item_name.display(db.upcast()))) } fn check(ra_fixture: &str, expect: Expect) { @@ -633,6 +498,24 @@ mod tests { expect.assert_eq(&actual) } + fn render_path(db: &dyn DefDatabase, info: &ImportInfo) -> String { + let mut module = info.container; + let mut segments = vec![&info.name]; + + let def_map = module.def_map(db); + assert!(def_map.block_id().is_none(), "block local items should not be in `ImportMap`"); + + while let Some(parent) = module.containing_module(db) { + let parent_data = &def_map[parent.local_id]; + let (name, _) = + parent_data.children.iter().find(|(_, id)| **id == module.local_id).unwrap(); + segments.push(name); + module = parent; + } + + segments.iter().rev().map(|it| it.display(db.upcast())).join("::") + } + #[test] fn smoke() { check( @@ -749,6 +632,7 @@ mod tests { #[test] fn module_reexport() { // Reexporting modules from a dependency adds all contents to the import map. + // XXX: The rendered paths are relative to the defining crate. check( r" //- /main.rs crate:main deps:lib @@ -764,9 +648,9 @@ mod tests { - module::S (t) - module::S (v) main: + - module::S (t) + - module::S (v) - reexported_module (t) - - reexported_module::S (t) - - reexported_module::S (v) "#]], ); } @@ -868,10 +752,9 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), + Query::new("fmt".to_string()).fuzzy(), expect![[r#" dep::fmt (t) - dep::fmt::Display (t) dep::fmt::Display::FMT_CONST (a) dep::fmt::Display::format_function (a) dep::fmt::Display::format_method (a) @@ -898,7 +781,9 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), + Query::new("fmt".to_string()) + .fuzzy() + .assoc_search_mode(AssocSearchMode::AssocItemsOnly), expect![[r#" dep::fmt::Display::FMT_CONST (a) dep::fmt::Display::format_function (a) @@ -909,23 +794,10 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()) - .search_mode(SearchMode::Fuzzy) - .exclude_import_kind(ImportKind::AssociatedItem), + Query::new("fmt".to_string()).fuzzy().assoc_search_mode(AssocSearchMode::Exclude), expect![[r#" - dep::fmt (t) - dep::fmt::Display (t) - "#]], - ); - - check_search( - ra_fixture, - "main", - Query::new("fmt".to_string()) - .search_mode(SearchMode::Fuzzy) - .assoc_items_only() - .exclude_import_kind(ImportKind::AssociatedItem), - expect![[r#""#]], + dep::fmt (t) + "#]], ); } @@ -958,13 +830,12 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), + Query::new("fmt".to_string()).fuzzy(), expect![[r#" dep::Fmt (m) dep::Fmt (t) dep::Fmt (v) dep::fmt (t) - dep::fmt::Display (t) dep::fmt::Display::fmt (a) dep::format (f) "#]], @@ -973,7 +844,7 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Equals), + Query::new("fmt".to_string()), expect![[r#" dep::Fmt (m) dep::Fmt (t) @@ -982,20 +853,6 @@ mod tests { dep::fmt::Display::fmt (a) "#]], ); - - check_search( - ra_fixture, - "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Contains), - expect![[r#" - dep::Fmt (m) - dep::Fmt (t) - dep::Fmt (v) - dep::fmt (t) - dep::fmt::Display (t) - dep::fmt::Display::fmt (a) - "#]], - ); } #[test] @@ -1033,7 +890,6 @@ mod tests { dep::Fmt (t) dep::Fmt (v) dep::fmt (t) - dep::fmt::Display (t) dep::fmt::Display::fmt (a) "#]], ); @@ -1041,7 +897,7 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).name_only(), + Query::new("fmt".to_string()), expect![[r#" dep::Fmt (m) dep::Fmt (t) @@ -1106,43 +962,10 @@ mod tests { pub fn no() {} "#, "main", - Query::new("".to_string()).limit(2), + Query::new("".to_string()).fuzzy().limit(1), expect![[r#" - dep::Fmt (m) - dep::Fmt (t) - dep::Fmt (v) - dep::fmt (t) + dep::fmt::Display (t) "#]], ); } - - #[test] - fn search_exclusions() { - let ra_fixture = r#" - //- /main.rs crate:main deps:dep - //- /dep.rs crate:dep - - pub struct fmt; - pub struct FMT; - "#; - - check_search( - ra_fixture, - "main", - Query::new("FMT".to_string()), - expect![[r#" - dep::FMT (t) - dep::FMT (v) - dep::fmt (t) - dep::fmt (v) - "#]], - ); - - check_search( - ra_fixture, - "main", - Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt), - expect![[r#""#]], - ); - } } diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 2001fb29a9e3..2ac1bcdc079f 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -14,8 +14,8 @@ use stdx::format_to; use syntax::ast; use crate::{ - db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, HasModule, - ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId, + db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, + ExternCrateId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId, }; #[derive(Copy, Clone, Debug)] @@ -50,6 +50,7 @@ pub struct ItemScope { unnamed_consts: Vec, /// Traits imported via `use Trait as _;`. unnamed_trait_imports: FxHashMap, + extern_crate_decls: Vec, /// Macros visible in current module in legacy textual scope /// /// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first. @@ -188,7 +189,11 @@ impl ItemScope { } pub(crate) fn define_impl(&mut self, imp: ImplId) { - self.impls.push(imp) + self.impls.push(imp); + } + + pub(crate) fn define_extern_crate_decl(&mut self, extern_crate: ExternCrateId) { + self.extern_crate_decls.push(extern_crate); } pub(crate) fn define_unnamed_const(&mut self, konst: ConstId) { @@ -397,6 +402,7 @@ impl ItemScope { legacy_macros, attr_macros, derive_macros, + extern_crate_decls, } = self; types.shrink_to_fit(); values.shrink_to_fit(); @@ -409,6 +415,7 @@ impl ItemScope { legacy_macros.shrink_to_fit(); attr_macros.shrink_to_fit(); derive_macros.shrink_to_fit(); + extern_crate_decls.shrink_to_fit(); } } diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index e74b71888c2b..6f80bb6e07ce 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -46,7 +46,7 @@ use ast::{AstNode, HasName, StructKind}; use base_db::CrateId; use either::Either; use hir_expand::{ - ast_id_map::FileAstId, + ast_id_map::{AstIdNode, FileAstId}, attrs::RawAttrs, hygiene::Hygiene, name::{name, AsName, Name}, @@ -314,7 +314,7 @@ from_attrs!(ModItem(ModItem), Variant(Idx), Field(Idx), Param(Id /// Trait implemented by all item nodes in the item tree. pub trait ItemTreeNode: Clone { - type Source: AstNode + Into; + type Source: AstIdNode + Into; fn ast_id(&self) -> FileAstId; diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index e873316a578c..ddf668d20b05 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -52,7 +52,7 @@ struct Printer<'a> { needs_indent: bool, } -impl<'a> Printer<'a> { +impl Printer<'_> { fn indented(&mut self, f: impl FnOnce(&mut Self)) { self.indent_level += 1; wln!(self); @@ -572,7 +572,7 @@ impl<'a> Printer<'a> { } } -impl<'a> Write for Printer<'a> { +impl Write for Printer<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { for line in s.split_inclusive('\n') { if self.needs_indent { diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 0e9ac58fbaaa..627479bb7c1b 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -180,15 +180,15 @@ impl LangItems { T: Into + Copy, { let _p = profile::span("collect_lang_item"); - if let Some(lang_item) = lang_attr(db, item) { + if let Some(lang_item) = db.lang_attr(item.into()) { self.items.entry(lang_item).or_insert_with(|| constructor(item)); } } } -pub fn lang_attr(db: &dyn DefDatabase, item: impl Into + Copy) -> Option { - let attrs = db.attrs(item.into()); - attrs.by_key("lang").string_value().cloned().and_then(|it| LangItem::from_str(&it)) +pub(crate) fn lang_attr_query(db: &dyn DefDatabase, item: AttrDefId) -> Option { + let attrs = db.attrs(item); + attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it)) } pub enum GenericRequirement { diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 9d8b57a0da9e..1e74e2dfcb4c 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -64,7 +64,7 @@ use std::{ use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind}; use hir_expand::{ - ast_id_map::FileAstId, + ast_id_map::{AstIdNode, FileAstId}, attrs::{Attr, AttrId, AttrInput}, builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, @@ -88,8 +88,8 @@ use crate::{ builtin_type::BuiltinType, data::adt::VariantData, item_tree::{ - Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, Static, - Struct, Trait, TraitAlias, TypeAlias, Union, + Const, Enum, ExternCrate, Function, Impl, Import, ItemTreeId, ItemTreeNode, MacroDef, + MacroRules, Static, Struct, Trait, TraitAlias, TypeAlias, Union, }, }; @@ -145,24 +145,28 @@ pub struct ModuleId { } impl ModuleId { - pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc { + pub fn def_map(self, db: &dyn db::DefDatabase) -> Arc { match self.block { Some(block) => db.block_def_map(block), None => db.crate_def_map(self.krate), } } - pub fn krate(&self) -> CrateId { + pub fn krate(self) -> CrateId { self.krate } - pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option { + pub fn containing_module(self, db: &dyn db::DefDatabase) -> Option { self.def_map(db).containing_module(self.local_id) } - pub fn containing_block(&self) -> Option { + pub fn containing_block(self) -> Option { self.block } + + pub fn is_block_module(self) -> bool { + self.block.is_some() && self.local_id == DefMap::ROOT + } } /// An ID of a module, **local** to a `DefMap`. @@ -313,6 +317,16 @@ pub struct ImplId(salsa::InternId); type ImplLoc = ItemLoc; impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct ImportId(salsa::InternId); +type ImportLoc = ItemLoc; +impl_intern!(ImportId, ImportLoc, intern_import, lookup_intern_import); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct ExternCrateId(salsa::InternId); +type ExternCrateLoc = ItemLoc; +impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate); + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ExternBlockId(salsa::InternId); type ExternBlockLoc = ItemLoc; @@ -392,14 +406,14 @@ impl TypeParamId { impl TypeParamId { /// Caller should check if this toc id really belongs to a type - pub fn from_unchecked(x: TypeOrConstParamId) -> Self { - Self(x) + pub fn from_unchecked(it: TypeOrConstParamId) -> Self { + Self(it) } } impl From for TypeOrConstParamId { - fn from(x: TypeParamId) -> Self { - x.0 + fn from(it: TypeParamId) -> Self { + it.0 } } @@ -418,14 +432,14 @@ impl ConstParamId { impl ConstParamId { /// Caller should check if this toc id really belongs to a const - pub fn from_unchecked(x: TypeOrConstParamId) -> Self { - Self(x) + pub fn from_unchecked(it: TypeOrConstParamId) -> Self { + Self(it) } } impl From for TypeOrConstParamId { - fn from(x: ConstParamId) -> Self { - x.0 + fn from(it: ConstParamId) -> Self { + it.0 } } @@ -548,14 +562,14 @@ pub enum TypeOwnerId { impl TypeOwnerId { fn as_generic_def_id(self) -> Option { Some(match self { - TypeOwnerId::FunctionId(x) => GenericDefId::FunctionId(x), - TypeOwnerId::ConstId(x) => GenericDefId::ConstId(x), - TypeOwnerId::AdtId(x) => GenericDefId::AdtId(x), - TypeOwnerId::TraitId(x) => GenericDefId::TraitId(x), - TypeOwnerId::TraitAliasId(x) => GenericDefId::TraitAliasId(x), - TypeOwnerId::TypeAliasId(x) => GenericDefId::TypeAliasId(x), - TypeOwnerId::ImplId(x) => GenericDefId::ImplId(x), - TypeOwnerId::EnumVariantId(x) => GenericDefId::EnumVariantId(x), + TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it), + TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it), + TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it), + TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it), + TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it), + TypeOwnerId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), + TypeOwnerId::ImplId(it) => GenericDefId::ImplId(it), + TypeOwnerId::EnumVariantId(it) => GenericDefId::EnumVariantId(it), TypeOwnerId::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => { return None } @@ -578,15 +592,15 @@ impl_from!( for TypeOwnerId ); -// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let x: Type = _; }`) +// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let it: Type = _; }`) impl From for TypeOwnerId { fn from(value: DefWithBodyId) -> Self { match value { - DefWithBodyId::FunctionId(x) => x.into(), - DefWithBodyId::StaticId(x) => x.into(), - DefWithBodyId::ConstId(x) => x.into(), - DefWithBodyId::InTypeConstId(x) => x.into(), - DefWithBodyId::VariantId(x) => x.into(), + DefWithBodyId::FunctionId(it) => it.into(), + DefWithBodyId::StaticId(it) => it.into(), + DefWithBodyId::ConstId(it) => it.into(), + DefWithBodyId::InTypeConstId(it) => it.into(), + DefWithBodyId::VariantId(it) => it.into(), } } } @@ -594,14 +608,14 @@ impl From for TypeOwnerId { impl From for TypeOwnerId { fn from(value: GenericDefId) -> Self { match value { - GenericDefId::FunctionId(x) => x.into(), - GenericDefId::AdtId(x) => x.into(), - GenericDefId::TraitId(x) => x.into(), - GenericDefId::TraitAliasId(x) => x.into(), - GenericDefId::TypeAliasId(x) => x.into(), - GenericDefId::ImplId(x) => x.into(), - GenericDefId::EnumVariantId(x) => x.into(), - GenericDefId::ConstId(x) => x.into(), + GenericDefId::FunctionId(it) => it.into(), + GenericDefId::AdtId(it) => it.into(), + GenericDefId::TraitId(it) => it.into(), + GenericDefId::TraitAliasId(it) => it.into(), + GenericDefId::TypeAliasId(it) => it.into(), + GenericDefId::ImplId(it) => it.into(), + GenericDefId::EnumVariantId(it) => it.into(), + GenericDefId::ConstId(it) => it.into(), } } } @@ -716,7 +730,7 @@ impl GeneralConstId { .const_data(const_id) .name .as_ref() - .and_then(|x| x.as_str()) + .and_then(|it| it.as_str()) .unwrap_or("_") .to_owned(), GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"), @@ -821,6 +835,7 @@ pub enum AttrDefId { ImplId(ImplId), GenericParamId(GenericParamId), ExternBlockId(ExternBlockId), + ExternCrateId(ExternCrateId), } impl_from!( @@ -835,7 +850,8 @@ impl_from!( TypeAliasId, MacroId(Macro2Id, MacroRulesId, ProcMacroId), ImplId, - GenericParamId + GenericParamId, + ExternCrateId for AttrDefId ); @@ -927,6 +943,12 @@ impl HasModule for AdtId { } } +impl HasModule for ExternCrateId { + fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { + self.lookup(db).container + } +} + impl HasModule for VariantId { fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { match self { @@ -950,17 +972,17 @@ impl HasModule for MacroId { impl HasModule for TypeOwnerId { fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { match self { - TypeOwnerId::FunctionId(x) => x.lookup(db).module(db), - TypeOwnerId::StaticId(x) => x.lookup(db).module(db), - TypeOwnerId::ConstId(x) => x.lookup(db).module(db), - TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.module(db), - TypeOwnerId::AdtId(x) => x.module(db), - TypeOwnerId::TraitId(x) => x.lookup(db).container, - TypeOwnerId::TraitAliasId(x) => x.lookup(db).container, - TypeOwnerId::TypeAliasId(x) => x.lookup(db).module(db), - TypeOwnerId::ImplId(x) => x.lookup(db).container, - TypeOwnerId::EnumVariantId(x) => x.parent.lookup(db).container, - TypeOwnerId::ModuleId(x) => *x, + TypeOwnerId::FunctionId(it) => it.lookup(db).module(db), + TypeOwnerId::StaticId(it) => it.lookup(db).module(db), + TypeOwnerId::ConstId(it) => it.lookup(db).module(db), + TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db), + TypeOwnerId::AdtId(it) => it.module(db), + TypeOwnerId::TraitId(it) => it.lookup(db).container, + TypeOwnerId::TraitAliasId(it) => it.lookup(db).container, + TypeOwnerId::TypeAliasId(it) => it.lookup(db).module(db), + TypeOwnerId::ImplId(it) => it.lookup(db).container, + TypeOwnerId::EnumVariantId(it) => it.parent.lookup(db).container, + TypeOwnerId::ModuleId(it) => *it, } } } @@ -1050,6 +1072,7 @@ impl AttrDefId { .krate } AttrDefId::MacroId(it) => it.module(db).krate, + AttrDefId::ExternCrateId(it) => it.lookup(db).container.krate, } } } @@ -1101,12 +1124,12 @@ impl AsMacroCall for InFile<&ast::MacroCall> { /// Helper wrapper for `AstId` with `ModPath` #[derive(Clone, Debug, Eq, PartialEq)] -struct AstIdWithPath { +struct AstIdWithPath { ast_id: AstId, path: path::ModPath, } -impl AstIdWithPath { +impl AstIdWithPath { fn new(file_id: HirFileId, ast_id: FileAstId, path: path::ModPath) -> AstIdWithPath { AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path } } diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs index af623fd0e5d4..e523c229179b 100644 --- a/crates/hir-def/src/lower.rs +++ b/crates/hir-def/src/lower.rs @@ -1,5 +1,9 @@ //! Context for lowering paths. -use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile}; +use hir_expand::{ + ast_id_map::{AstIdMap, AstIdNode}, + hygiene::Hygiene, + AstId, HirFileId, InFile, +}; use once_cell::unsync::OnceCell; use syntax::ast; use triomphe::Arc; @@ -37,7 +41,7 @@ impl<'a> LowerCtx<'a> { Path::from_src(ast, self) } - pub(crate) fn ast_id(&self, item: &N) -> Option> { + pub(crate) fn ast_id(&self, item: &N) -> Option> { let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?; let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id)); Some(InFile::new(file_id, ast_id_map.ast_id(item))) diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index f41f97190435..abd84c6a46de 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -278,6 +278,44 @@ impl < > core::cmp::Eq for Command< > where {}"#]], ); } +#[test] +fn test_partial_eq_expand_with_derive_const() { + // FIXME: actually expand with const + check( + r#" +//- minicore: derive, eq +#[derive_const(PartialEq, Eq)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +#[derive_const(PartialEq, Eq)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::cmp::PartialEq for Command< > where { + fn eq(&self , other: &Self ) -> bool { + match (self , other) { + (Command::Move { + x: x_self, y: y_self, + } + , Command::Move { + x: x_other, y: y_other, + } + )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false + } + } +} +impl < > core::cmp::Eq for Command< > where {}"#]], + ); +} + #[test] fn test_partial_ord_expand() { check( @@ -378,6 +416,44 @@ fn test_hash_expand() { //- minicore: derive, hash use core::hash::Hash; +#[derive(Hash)] +struct Foo { + x: i32, + y: u64, + z: (i32, u64), +} +"#, + expect![[r#" +use core::hash::Hash; + +#[derive(Hash)] +struct Foo { + x: i32, + y: u64, + z: (i32, u64), +} + +impl < > core::hash::Hash for Foo< > where { + fn hash(&self , ra_expand_state: &mut H) { + match self { + Foo { + x: x, y: y, z: z, + } + => { + x.hash(ra_expand_state); + y.hash(ra_expand_state); + z.hash(ra_expand_state); + } + , + } + } +}"#]], + ); + check( + r#" +//- minicore: derive, hash +use core::hash::Hash; + #[derive(Hash)] enum Command { Move { x: i32, y: i32 }, diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 07d9baa58974..b232651db96e 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -201,7 +201,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(arg2), ::core::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -235,11 +235,11 @@ macro_rules! format_args { fn main() { /* error: no rule matches input tokens */; - /* error: no rule matches input tokens */; - /* error: no rule matches input tokens */; - /* error: no rule matches input tokens */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(), ::core::fmt::Display::fmt), ]); - /* error: no rule matches input tokens */; - ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(5), ::core::fmt::Display::fmt), ]); + /* error: expected expression */; + /* error: expected expression, expected COMMA */; + /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]); + /* error: expected expression, expected R_PAREN */; + ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]); } "##]], ); @@ -267,7 +267,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a::()), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -300,7 +300,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::Argument::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]); + ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]); } "##]], ); @@ -334,7 +334,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::Argument::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -364,8 +364,8 @@ macro_rules! format_args { fn main() { let _ = - /* error: no rule matches input tokens *//* parse error: expected field name or number */ -::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]); + /* error: expected field name or number *//* parse error: expected field name or number */ +::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]); } "##]], ); diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 553ffe3d0b88..b26f9867580b 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -98,6 +98,42 @@ fn#19 main#20(#21)#21 {#22 "##]], ); } + +#[test] +fn token_mapping_eager() { + check( + r#" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +macro_rules! identity { + ($expr:expr) => { $expr }; +} + +fn main(foo: ()) { + format_args/*+tokenids*/!("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar") +} + +"#, + expect![[r##" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +macro_rules! identity { + ($expr:expr) => { $expr }; +} + +fn main(foo: ()) { + // format_args/*+tokenids*/!("{} {} {}"#1,#3 format_args!("{}", 0#10),#12 foo#13,#14 identity!(10#18),#21 "bar"#22) +::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 " "#4294967295,#4294967295 " "#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#42949672950#10)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#4294967295foo#13)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#429496729510#18)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295 +} + +"##]], + ); +} + #[test] fn float_field_access_macro_input() { check( diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 4a62696df081..7a87e61c6939 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -20,8 +20,8 @@ use ::mbe::TokenMap; use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase}; use expect_test::Expect; use hir_expand::{ - db::{ExpandDatabase, TokenExpander}, - AstId, InFile, MacroDefId, MacroDefKind, MacroFile, + db::{DeclarativeMacroExpander, ExpandDatabase}, + AstId, InFile, MacroFile, }; use stdx::format_to; use syntax::{ @@ -100,32 +100,29 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let call_offset = macro_.syntax().text_range().start().into(); let file_ast_id = db.ast_id_map(source.file_id).ast_id(¯o_); let ast_id = AstId::new(source.file_id, file_ast_id.upcast()); - let kind = MacroDefKind::Declarative(ast_id); - let macro_def = db - .macro_def(MacroDefId { krate, kind, local_inner: false, allow_internal_unsafe: false }) - .unwrap(); - if let TokenExpander::DeclarativeMacro { mac, def_site_token_map } = &*macro_def { - let tt = match ¯o_ { - ast::Macro::MacroRules(mac) => mac.token_tree().unwrap(), - ast::Macro::MacroDef(_) => unimplemented!(""), - }; + let DeclarativeMacroExpander { mac, def_site_token_map } = + &*db.decl_macro_expander(krate, ast_id); + assert_eq!(mac.err(), None); + let tt = match ¯o_ { + ast::Macro::MacroRules(mac) => mac.token_tree().unwrap(), + ast::Macro::MacroDef(_) => unimplemented!(""), + }; - let tt_start = tt.syntax().text_range().start(); - tt.syntax().descendants_with_tokens().filter_map(SyntaxElement::into_token).for_each( - |token| { - let range = token.text_range().checked_sub(tt_start).unwrap(); - if let Some(id) = def_site_token_map.token_by_range(range) { - let offset = (range.end() + tt_start).into(); - text_edits.push((offset..offset, format!("#{}", id.0))); - } - }, - ); - text_edits.push(( - call_offset..call_offset, - format!("// call ids will be shifted by {:?}\n", mac.shift()), - )); - } + let tt_start = tt.syntax().text_range().start(); + tt.syntax().descendants_with_tokens().filter_map(SyntaxElement::into_token).for_each( + |token| { + let range = token.text_range().checked_sub(tt_start).unwrap(); + if let Some(id) = def_site_token_map.token_by_range(range) { + let offset = (range.end() + tt_start).into(); + text_edits.push((offset..offset, format!("#{}", id.0))); + } + }, + ); + text_edits.push(( + call_offset..call_offset, + format!("// call ids will be shifted by {:?}\n", mac.shift()), + )); } for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) { @@ -190,7 +187,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let range: Range = range.into(); if show_token_ids { - if let Some((tree, map, _)) = arg.as_deref() { + if let Some((tree, map, _)) = arg.value.as_deref() { let tt_range = call.token_tree().unwrap().syntax().text_range(); let mut ranges = Vec::new(); extract_id_ranges(&mut ranges, map, tree); @@ -239,7 +236,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream for impl_id in def_map[local_id].scope.impls() { let src = impl_id.lookup(&db).source(&db); - if src.file_id.is_builtin_derive(&db).is_some() { + if src.file_id.is_builtin_derive(&db) { let pp = pretty_print_macro_expansion(src.value.syntax().clone(), None); format_to!(expanded_text, "\n{}", pp) } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 0ab1bd8490c5..86818ce26dd6 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -60,7 +60,7 @@ mod tests; use std::{cmp::Ord, ops::Deref}; use base_db::{CrateId, Edition, FileId, ProcMacroKind}; -use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId}; +use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId}; use itertools::Itertools; use la_arena::Arena; use profile::Count; @@ -196,6 +196,10 @@ impl BlockRelativeModuleId { fn into_module(self, krate: CrateId) -> ModuleId { ModuleId { krate, block: self.block, local_id: self.local_id } } + + fn is_block_module(self) -> bool { + self.block.is_some() && self.local_id == DefMap::ROOT + } } impl std::ops::Index for DefMap { @@ -278,7 +282,9 @@ pub struct ModuleData { pub origin: ModuleOrigin, /// Declared visibility of this module. pub visibility: Visibility, - /// Always [`None`] for block modules + /// Parent module in the same `DefMap`. + /// + /// [`None`] for block modules because they are always its `DefMap`'s root. pub parent: Option, pub children: FxHashMap, pub scope: ItemScope, @@ -626,6 +632,17 @@ impl ModuleData { self.origin.definition_source(db) } + /// Same as [`definition_source`] but only returns the file id to prevent parsing the ASt. + pub fn definition_source_file_id(&self) -> HirFileId { + match self.origin { + ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { + definition.into() + } + ModuleOrigin::Inline { definition, .. } => definition.file_id, + ModuleOrigin::BlockExpr { block } => block.file_id, + } + } + /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. /// `None` for the crate root or block. pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option> { diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 62fb3c7882cd..c048716d7403 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -52,10 +52,10 @@ use crate::{ tt, visibility::{RawVisibility, Visibility}, AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId, - ExternBlockLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, - Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId, - ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, - TypeAliasLoc, UnionLoc, UnresolvedMacro, + ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, ImportLoc, Intern, + ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, + MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, + TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, }; static GLOB_RECURSION_LIMIT: Limit = Limit::new(100); @@ -156,10 +156,9 @@ struct Import { alias: Option, visibility: RawVisibility, kind: ImportKind, - is_prelude: bool, - is_extern_crate: bool, - is_macro_use: bool, source: ImportSource, + is_prelude: bool, + is_macro_use: bool, } impl Import { @@ -168,26 +167,23 @@ impl Import { krate: CrateId, tree: &ItemTree, id: ItemTreeId, - ) -> Vec { + mut cb: impl FnMut(Self), + ) { let it = &tree[id.value]; let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into()); let visibility = &tree[it.visibility]; let is_prelude = attrs.by_key("prelude_import").exists(); - - let mut res = Vec::new(); it.use_tree.expand(|idx, path, kind, alias| { - res.push(Self { + cb(Self { path, alias, visibility: visibility.clone(), kind, is_prelude, - is_extern_crate: false, is_macro_use: false, source: ImportSource::Import { id, use_tree: idx }, }); }); - res } fn from_extern_crate( @@ -205,7 +201,6 @@ impl Import { visibility: visibility.clone(), kind: ImportKind::Plain, is_prelude: false, - is_extern_crate: true, is_macro_use: attrs.by_key("macro_use").exists(), source: ImportSource::ExternCrate(id), } @@ -776,7 +771,7 @@ impl DefCollector<'_> { let _p = profile::span("resolve_import") .detail(|| format!("{}", import.path.display(self.db.upcast()))); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); - if import.is_extern_crate { + if matches!(import.source, ImportSource::ExternCrate { .. }) { let name = import .path .as_ident() @@ -813,11 +808,8 @@ impl DefCollector<'_> { } } - // Check whether all namespace is resolved - if def.take_types().is_some() - && def.take_values().is_some() - && def.take_macros().is_some() - { + // Check whether all namespaces are resolved. + if def.is_full() { PartialResolvedImport::Resolved(def) } else { PartialResolvedImport::Indeterminate(def) @@ -826,7 +818,7 @@ impl DefCollector<'_> { } fn resolve_extern_crate(&self, name: &Name) -> Option { - if *name == name!(self) { + if *name == name![self] { cov_mark::hit!(extern_crate_self_as); Some(self.def_map.crate_root()) } else { @@ -867,7 +859,7 @@ impl DefCollector<'_> { tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 - if import.is_extern_crate + if matches!(import.source, ImportSource::ExternCrate { .. }) && self.def_map.block.is_none() && module_id == DefMap::ROOT { @@ -1585,21 +1577,34 @@ impl ModCollector<'_, '_> { match item { ModItem::Mod(m) => self.collect_module(m, &attrs), ModItem::Import(import_id) => { - let imports = Import::from_use( + let _import_id = ImportLoc { + container: module, + id: ItemTreeId::new(self.tree_id, import_id), + } + .intern(db); + Import::from_use( db, krate, self.item_tree, ItemTreeId::new(self.tree_id, import_id), - ); - self.def_collector.unresolved_imports.extend(imports.into_iter().map( - |import| ImportDirective { - module_id: self.module_id, - import, - status: PartialResolvedImport::Unresolved, + |import| { + self.def_collector.unresolved_imports.push(ImportDirective { + module_id: self.module_id, + import, + status: PartialResolvedImport::Unresolved, + }); }, - )); + ) } ModItem::ExternCrate(import_id) => { + let extern_crate_id = ExternCrateLoc { + container: module, + id: ItemTreeId::new(self.tree_id, import_id), + } + .intern(db); + self.def_collector.def_map.modules[self.module_id] + .scope + .define_extern_crate_decl(extern_crate_id); self.def_collector.unresolved_imports.push(ImportDirective { module_id: self.module_id, import: Import::from_extern_crate( @@ -2230,8 +2235,12 @@ impl ModCollector<'_, '_> { } fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) { - let Some((source, target)) = Self::borrow_modules(self.def_collector.def_map.modules.as_mut(), module_id, self.module_id) else { - return + let Some((source, target)) = Self::borrow_modules( + self.def_collector.def_map.modules.as_mut(), + module_id, + self.module_id, + ) else { + return; }; for (name, macs) in source.scope.legacy_macros() { @@ -2271,7 +2280,7 @@ impl ModCollector<'_, '_> { fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) { let ast_id = item.ast_id(self.item_tree); - let ast_id = InFile::new(self.file_id(), ast_id.upcast()); + let ast_id = InFile::new(self.file_id(), ast_id.erase()); self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id, ast_id, diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs index 18b424255cd6..e82e97b628e9 100644 --- a/crates/hir-def/src/nameres/diagnostics.rs +++ b/crates/hir-def/src/nameres/diagnostics.rs @@ -2,12 +2,9 @@ use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use hir_expand::{attrs::AttrId, MacroCallKind}; +use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind}; use la_arena::Idx; -use syntax::{ - ast::{self, AnyHasAttrs}, - SyntaxError, -}; +use syntax::{ast, SyntaxError}; use crate::{ item_tree::{self, ItemTreeId}, @@ -24,7 +21,7 @@ pub enum DefDiagnosticKind { UnresolvedImport { id: ItemTreeId, index: Idx }, - UnconfiguredCode { ast: AstId, cfg: CfgExpr, opts: CfgOptions }, + UnconfiguredCode { ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions }, UnresolvedProcMacro { ast: MacroCallKind, krate: CrateId }, @@ -81,7 +78,7 @@ impl DefDiagnostic { pub fn unconfigured_code( container: LocalModuleId, - ast: AstId, + ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions, ) -> Self { diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 5f6163175a72..de22ea101460 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -12,11 +12,12 @@ use base_db::Edition; use hir_expand::name::Name; +use triomphe::Arc; use crate::{ db::DefDatabase, item_scope::BUILTIN_SCOPE, - nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs}, + nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs}, path::{ModPath, PathKind}, per_ns::PerNs, visibility::{RawVisibility, Visibility}, @@ -159,13 +160,15 @@ impl DefMap { (None, new) => new, }; - match ¤t_map.block { - Some(block) => { + match current_map.block { + Some(block) if original_module == Self::ROOT => { + // Block modules "inherit" names from its parent module. original_module = block.parent.local_id; arc = block.parent.def_map(db, current_map.krate); - current_map = &*arc; + current_map = &arc; } - None => return result, + // Proper (non-block) modules, including those in block `DefMap`s, don't. + _ => return result, } } } @@ -189,7 +192,7 @@ impl DefMap { )); let mut segments = path.segments().iter().enumerate(); - let mut curr_per_ns: PerNs = match path.kind { + let mut curr_per_ns = match path.kind { PathKind::DollarCrate(krate) => { if krate == self.krate { cov_mark::hit!(macro_dollar_crate_self); @@ -241,51 +244,54 @@ impl DefMap { ) } PathKind::Super(lvl) => { - let mut module = original_module; - for i in 0..lvl { - match self.modules[module].parent { - Some(it) => module = it, - None => match &self.block { - Some(block) => { - // Look up remaining path in parent `DefMap` - let new_path = ModPath::from_segments( - PathKind::Super(lvl - i), - path.segments().to_vec(), - ); - tracing::debug!( - "`super` path: {} -> {} in parent map", - path.display(db.upcast()), - new_path.display(db.upcast()) - ); - return block - .parent - .def_map(db, self.krate) - .resolve_path_fp_with_macro( - db, - mode, - block.parent.local_id, - &new_path, - shadow, - expected_macro_subns, - ); - } - None => { - tracing::debug!("super path in root module"); - return ResolvePathResult::empty(ReachedFixedPoint::Yes); - } - }, + let mut local_id = original_module; + let mut ext; + let mut def_map = self; + + // Adjust `local_id` to `self`, i.e. the nearest non-block module. + if def_map.module_id(local_id).is_block_module() { + (ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id); + def_map = &ext; + } + + // Go up the module tree but skip block modules as `super` always refers to the + // nearest non-block module. + for _ in 0..lvl { + // Loop invariant: at the beginning of each loop, `local_id` must refer to a + // non-block module. + if let Some(parent) = def_map.modules[local_id].parent { + local_id = parent; + if def_map.module_id(local_id).is_block_module() { + (ext, local_id) = + adjust_to_nearest_non_block_module(db, def_map, local_id); + def_map = &ext; + } + } else { + stdx::always!(def_map.block.is_none()); + tracing::debug!("super path in root module"); + return ResolvePathResult::empty(ReachedFixedPoint::Yes); } } - // Resolve `self` to the containing crate-rooted module if we're a block - self.with_ancestor_maps(db, module, &mut |def_map, module| { - if def_map.block.is_some() { - None // keep ascending - } else { - Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public)) - } - }) - .expect("block DefMap not rooted in crate DefMap") + let module = def_map.module_id(local_id); + stdx::never!(module.is_block_module()); + + if self.block != def_map.block { + // If we have a different `DefMap` from `self` (the orignal `DefMap` we started + // with), resolve the remaining path segments in that `DefMap`. + let path = + ModPath::from_segments(PathKind::Super(0), path.segments().iter().cloned()); + return def_map.resolve_path_fp_with_macro( + db, + mode, + local_id, + &path, + shadow, + expected_macro_subns, + ); + } + + PerNs::types(module.into(), Visibility::Public) } PathKind::Abs => { // 2018-style absolute path -- only extern prelude @@ -508,3 +514,27 @@ impl DefMap { } } } + +/// Given a block module, returns its nearest non-block module and the `DefMap` it blongs to. +fn adjust_to_nearest_non_block_module( + db: &dyn DefDatabase, + def_map: &DefMap, + mut local_id: LocalModuleId, +) -> (Arc, LocalModuleId) { + // INVARIANT: `local_id` in `def_map` must be a block module. + stdx::always!(def_map.module_id(local_id).is_block_module()); + + let mut ext; + // This needs to be a local variable due to our mighty lifetime. + let mut def_map = def_map; + loop { + let BlockInfo { parent, .. } = def_map.block.expect("block module without parent module"); + + ext = parent.def_map(db, def_map.krate); + def_map = &ext; + local_id = parent.local_id; + if !parent.is_block_module() { + return (ext, local_id); + } + } +} diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs index ff4ae69546cf..06530cc7ebd7 100644 --- a/crates/hir-def/src/path.rs +++ b/crates/hir-def/src/path.rs @@ -45,7 +45,7 @@ pub enum Path { /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. generic_args: Option>]>>, }, - /// A link to a lang item. It is used in desugaring of things like `x?`. We can show these + /// A link to a lang item. It is used in desugaring of things like `it?`. We can show these /// links via a normal path since they might be private and not accessible in the usage place. LangItem(LangItemTarget), } @@ -135,10 +135,7 @@ impl Path { pub fn segments(&self) -> PathSegments<'_> { let Path::Normal { mod_path, generic_args, .. } = self else { - return PathSegments { - segments: &[], - generic_args: None, - }; + return PathSegments { segments: &[], generic_args: None }; }; let s = PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() }; diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs index 1cb17ff0d261..abd817893cc4 100644 --- a/crates/hir-def/src/path/lower.rs +++ b/crates/hir-def/src/path/lower.rs @@ -74,8 +74,8 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option>::Foo desugars to Trait::Foo Some(trait_ref) => { let Path::Normal { mod_path, generic_args: path_generic_args, .. } = - Path::from_src(trait_ref.path()?, ctx)? else - { + Path::from_src(trait_ref.path()?, ctx)? + else { return None; }; let num_segments = mod_path.segments().len(); diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs index 0aead6f37f73..11d58a6ba09c 100644 --- a/crates/hir-def/src/pretty.rs +++ b/crates/hir-def/src/pretty.rs @@ -12,8 +12,8 @@ use crate::{ }; pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result { - if let Path::LangItem(x) = path { - return write!(buf, "$lang_item::{x:?}"); + if let Path::LangItem(it) = path { + return write!(buf, "$lang_item::{it:?}"); } match path.type_anchor() { Some(anchor) => { diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 0d6f55411c1d..10f5702845e2 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -22,10 +22,10 @@ use crate::{ per_ns::PerNs, visibility::{RawVisibility, Visibility}, AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, - EnumVariantId, ExternBlockId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, - ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, - ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, - TypeOrConstParamId, TypeOwnerId, TypeParamId, VariantId, + EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, + HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, + MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, + TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, VariantId, }; #[derive(Debug, Clone)] @@ -186,12 +186,12 @@ impl Resolver { Path::LangItem(l) => { return Some(( match *l { - LangItemTarget::Union(x) => TypeNs::AdtId(x.into()), - LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x), - LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()), - LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x), - LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()), - LangItemTarget::Trait(x) => TypeNs::TraitId(x), + LangItemTarget::Union(it) => TypeNs::AdtId(it.into()), + LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it), + LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()), + LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it), + LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()), + LangItemTarget::Trait(it) => TypeNs::TraitId(it), LangItemTarget::Function(_) | LangItemTarget::ImplDef(_) | LangItemTarget::Static(_) => return None, @@ -273,10 +273,10 @@ impl Resolver { Path::Normal { mod_path, .. } => mod_path, Path::LangItem(l) => { return Some(ResolveValueResult::ValueNs(match *l { - LangItemTarget::Function(x) => ValueNs::FunctionId(x), - LangItemTarget::Static(x) => ValueNs::StaticId(x), - LangItemTarget::Struct(x) => ValueNs::StructId(x), - LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x), + LangItemTarget::Function(it) => ValueNs::FunctionId(it), + LangItemTarget::Static(it) => ValueNs::StaticId(it), + LangItemTarget::Struct(it) => ValueNs::StructId(it), + LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), LangItemTarget::Union(_) | LangItemTarget::ImplDef(_) | LangItemTarget::TypeAlias(_) @@ -425,14 +425,14 @@ impl Resolver { /// The shadowing is accounted for: in /// /// ``` - /// let x = 92; + /// let it = 92; /// { - /// let x = 92; + /// let it = 92; /// $0 /// } /// ``` /// - /// there will be only one entry for `x` in the result. + /// there will be only one entry for `it` in the result. /// /// The result is ordered *roughly* from the innermost scope to the /// outermost: when the name is introduced in two namespaces in two scopes, @@ -1018,20 +1018,26 @@ impl HasResolver for ExternBlockId { } } +impl HasResolver for ExternCrateId { + fn resolver(self, db: &dyn DefDatabase) -> Resolver { + self.lookup(db).container.resolver(db) + } +} + impl HasResolver for TypeOwnerId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { - TypeOwnerId::FunctionId(x) => x.resolver(db), - TypeOwnerId::StaticId(x) => x.resolver(db), - TypeOwnerId::ConstId(x) => x.resolver(db), - TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.resolver(db), - TypeOwnerId::AdtId(x) => x.resolver(db), - TypeOwnerId::TraitId(x) => x.resolver(db), - TypeOwnerId::TraitAliasId(x) => x.resolver(db), - TypeOwnerId::TypeAliasId(x) => x.resolver(db), - TypeOwnerId::ImplId(x) => x.resolver(db), - TypeOwnerId::EnumVariantId(x) => x.resolver(db), - TypeOwnerId::ModuleId(x) => x.resolver(db), + TypeOwnerId::FunctionId(it) => it.resolver(db), + TypeOwnerId::StaticId(it) => it.resolver(db), + TypeOwnerId::ConstId(it) => it.resolver(db), + TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.resolver(db), + TypeOwnerId::AdtId(it) => it.resolver(db), + TypeOwnerId::TraitId(it) => it.resolver(db), + TypeOwnerId::TraitAliasId(it) => it.resolver(db), + TypeOwnerId::TypeAliasId(it) => it.resolver(db), + TypeOwnerId::ImplId(it) => it.resolver(db), + TypeOwnerId::EnumVariantId(it) => it.resolver(db), + TypeOwnerId::ModuleId(it) => it.resolver(db), } } } diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml index 40d8659f25ba..1f27204c1917 100644 --- a/crates/hir-expand/Cargo.toml +++ b/crates/hir-expand/Cargo.toml @@ -16,11 +16,9 @@ cov-mark = "2.0.0-pre.1" tracing = "0.1.35" either = "1.7.0" rustc-hash = "1.1.0" -la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +la-arena.workspace = true itertools = "0.10.5" -hashbrown = { version = "0.12.1", features = [ - "inline-more", -], default-features = false } +hashbrown.workspace = true smallvec.workspace = true triomphe.workspace = true diff --git a/crates/hir-expand/src/ast_id_map.rs b/crates/hir-expand/src/ast_id_map.rs index c2b0d5985e3c..1906ed15baec 100644 --- a/crates/hir-expand/src/ast_id_map.rs +++ b/crates/hir-expand/src/ast_id_map.rs @@ -18,47 +18,89 @@ use rustc_hash::FxHasher; use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; /// `AstId` points to an AST node in a specific file. -pub struct FileAstId { +pub struct FileAstId { raw: ErasedFileAstId, covariant: PhantomData N>, } -impl Clone for FileAstId { +impl Clone for FileAstId { fn clone(&self) -> FileAstId { *self } } -impl Copy for FileAstId {} +impl Copy for FileAstId {} -impl PartialEq for FileAstId { +impl PartialEq for FileAstId { fn eq(&self, other: &Self) -> bool { self.raw == other.raw } } -impl Eq for FileAstId {} -impl Hash for FileAstId { +impl Eq for FileAstId {} +impl Hash for FileAstId { fn hash(&self, hasher: &mut H) { self.raw.hash(hasher); } } -impl fmt::Debug for FileAstId { +impl fmt::Debug for FileAstId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "FileAstId::<{}>({})", type_name::(), self.raw.into_raw()) } } -impl FileAstId { +impl FileAstId { // Can't make this a From implementation because of coherence - pub fn upcast(self) -> FileAstId + pub fn upcast(self) -> FileAstId where N: Into, { FileAstId { raw: self.raw, covariant: PhantomData } } + + pub fn erase(self) -> ErasedFileAstId { + self.raw + } } -type ErasedFileAstId = Idx; +pub type ErasedFileAstId = Idx; + +pub trait AstIdNode: AstNode {} +macro_rules! register_ast_id_node { + (impl AstIdNode for $($ident:ident),+ ) => { + $( + impl AstIdNode for ast::$ident {} + )+ + fn should_alloc_id(kind: syntax::SyntaxKind) -> bool { + $( + ast::$ident::can_cast(kind) + )||+ + } + }; +} +register_ast_id_node! { + impl AstIdNode for + Item, + Adt, + Enum, + Struct, + Union, + Const, + ExternBlock, + ExternCrate, + Fn, + Impl, + Macro, + MacroDef, + MacroRules, + MacroCall, + Module, + Static, + Trait, + TraitAlias, + TypeAlias, + Use, + AssocItem, BlockExpr, Variant, RecordField, TupleField, ConstArg +} /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. #[derive(Default)] @@ -92,14 +134,7 @@ impl AstIdMap { // change parent's id. This means that, say, adding a new function to a // trait does not change ids of top-level items, which helps caching. bdfs(node, |it| { - let kind = it.kind(); - if ast::Item::can_cast(kind) - || ast::BlockExpr::can_cast(kind) - || ast::Variant::can_cast(kind) - || ast::RecordField::can_cast(kind) - || ast::TupleField::can_cast(kind) - || ast::ConstArg::can_cast(kind) - { + if should_alloc_id(it.kind()) { res.alloc(&it); true } else { @@ -120,15 +155,19 @@ impl AstIdMap { res } - pub fn ast_id(&self, item: &N) -> FileAstId { + pub fn ast_id(&self, item: &N) -> FileAstId { let raw = self.erased_ast_id(item.syntax()); FileAstId { raw, covariant: PhantomData } } - pub fn get(&self, id: FileAstId) -> AstPtr { + pub fn get(&self, id: FileAstId) -> AstPtr { AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap() } + pub(crate) fn get_raw(&self, id: ErasedFileAstId) -> SyntaxNodePtr { + self.arena[id].clone() + } + fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { let ptr = SyntaxNodePtr::new(item); let hash = hash_ptr(&ptr); diff --git a/crates/hir-expand/src/builtin_attr_macro.rs b/crates/hir-expand/src/builtin_attr_macro.rs index 80695bc06562..4ee12e2f2129 100644 --- a/crates/hir-expand/src/builtin_attr_macro.rs +++ b/crates/hir-expand/src/builtin_attr_macro.rs @@ -35,7 +35,7 @@ macro_rules! register_builtin { impl BuiltinAttrExpander { pub fn is_derive(self) -> bool { - matches!(self, BuiltinAttrExpander::Derive) + matches!(self, BuiltinAttrExpander::Derive | BuiltinAttrExpander::DeriveConst) } pub fn is_test(self) -> bool { matches!(self, BuiltinAttrExpander::Test) @@ -50,6 +50,8 @@ register_builtin! { (cfg_accessible, CfgAccessible) => dummy_attr_expand, (cfg_eval, CfgEval) => dummy_attr_expand, (derive, Derive) => derive_attr_expand, + // derive const is equivalent to derive for our proposes. + (derive_const, DeriveConst) => derive_attr_expand, (global_allocator, GlobalAllocator) => dummy_attr_expand, (test, Test) => dummy_attr_expand, (test_case, TestCase) => dummy_attr_expand diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 3d1e272b9006..ecc8b407a9c8 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -12,9 +12,7 @@ use crate::{ name::{AsName, Name}, tt::{self, TokenId}, }; -use syntax::ast::{ - self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, -}; +use syntax::ast::{self, AstNode, FieldList, HasAttrs, HasGenericParams, HasName, HasTypeBounds}; use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId}; @@ -30,12 +28,13 @@ macro_rules! register_builtin { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + token_map: &TokenMap, ) -> ExpandResult { let expander = match *self { $( BuiltinDeriveExpander::$trait => $expand, )* }; - expander(db, id, tt) + expander(db, id, tt, token_map) } fn find_by_name(name: &name::Name) -> Option { @@ -72,12 +71,12 @@ enum VariantShape { } fn tuple_field_iterator(n: usize) -> impl Iterator { - (0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified())) + (0..n).map(|it| Ident::new(format!("f{it}"), tt::TokenId::unspecified())) } impl VariantShape { fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree { - self.as_pattern_map(path, |x| quote!(#x)) + self.as_pattern_map(path, |it| quote!(#it)) } fn field_names(&self) -> Vec { @@ -95,17 +94,17 @@ impl VariantShape { ) -> tt::Subtree { match self { VariantShape::Struct(fields) => { - let fields = fields.iter().map(|x| { - let mapped = field_map(x); - quote! { #x : #mapped , } + let fields = fields.iter().map(|it| { + let mapped = field_map(it); + quote! { #it : #mapped , } }); quote! { #path { ##fields } } } &VariantShape::Tuple(n) => { - let fields = tuple_field_iterator(n).map(|x| { - let mapped = field_map(&x); + let fields = tuple_field_iterator(n).map(|it| { + let mapped = field_map(&it); quote! { #mapped , } @@ -118,16 +117,16 @@ impl VariantShape { } } - fn from(value: Option, token_map: &TokenMap) -> Result { + fn from(tm: &TokenMap, value: Option) -> Result { let r = match value { None => VariantShape::Unit, - Some(FieldList::RecordFieldList(x)) => VariantShape::Struct( - x.fields() - .map(|x| x.name()) - .map(|x| name_to_token(token_map, x)) + Some(FieldList::RecordFieldList(it)) => VariantShape::Struct( + it.fields() + .map(|it| it.name()) + .map(|it| name_to_token(tm, it)) .collect::>()?, ), - Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()), + Some(FieldList::TupleFieldList(it)) => VariantShape::Tuple(it.fields().count()), }; Ok(r) } @@ -141,7 +140,7 @@ enum AdtShape { impl AdtShape { fn as_pattern(&self, name: &tt::Ident) -> Vec { - self.as_pattern_map(name, |x| quote!(#x)) + self.as_pattern_map(name, |it| quote!(#it)) } fn field_names(&self) -> Vec> { @@ -190,32 +189,19 @@ struct BasicAdtInfo { associated_types: Vec, } -fn parse_adt(tt: &tt::Subtree) -> Result { - let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems); - let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| { - debug!("derive node didn't parse"); - ExpandError::other("invalid item definition") - })?; - let item = macro_items.items().next().ok_or_else(|| { - debug!("no module item parsed"); - ExpandError::other("no item found") - })?; - let adt = ast::Adt::cast(item.syntax().clone()).ok_or_else(|| { - debug!("expected adt, found: {:?}", item); - ExpandError::other("expected struct, enum or union") - })?; +fn parse_adt(tm: &TokenMap, adt: &ast::Adt) -> Result { let (name, generic_param_list, shape) = match &adt { ast::Adt::Struct(it) => ( it.name(), it.generic_param_list(), - AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?), + AdtShape::Struct(VariantShape::from(tm, it.field_list())?), ), ast::Adt::Enum(it) => { let default_variant = it .variant_list() .into_iter() - .flat_map(|x| x.variants()) - .position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into()))); + .flat_map(|it| it.variants()) + .position(|it| it.attrs().any(|it| it.simple_name() == Some("default".into()))); ( it.name(), it.generic_param_list(), @@ -224,11 +210,11 @@ fn parse_adt(tt: &tt::Subtree) -> Result { variants: it .variant_list() .into_iter() - .flat_map(|x| x.variants()) - .map(|x| { + .flat_map(|it| it.variants()) + .map(|it| { Ok(( - name_to_token(&token_map, x.name())?, - VariantShape::from(x.field_list(), &token_map)?, + name_to_token(tm, it.name())?, + VariantShape::from(tm, it.field_list())?, )) }) .collect::>()?, @@ -246,16 +232,16 @@ fn parse_adt(tt: &tt::Subtree) -> Result { let name = { let this = param.name(); match this { - Some(x) => { - param_type_set.insert(x.as_name()); - mbe::syntax_node_to_token_tree(x.syntax()).0 + Some(it) => { + param_type_set.insert(it.as_name()); + mbe::syntax_node_to_token_tree(it.syntax()).0 } None => tt::Subtree::empty(), } }; let bounds = match ¶m { - ast::TypeOrConstParam::Type(x) => { - x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + ast::TypeOrConstParam::Type(it) => { + it.type_bound_list().map(|it| mbe::syntax_node_to_token_tree(it.syntax()).0) } ast::TypeOrConstParam::Const(_) => None, }; @@ -296,9 +282,9 @@ fn parse_adt(tt: &tt::Subtree) -> Result { let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name(); param_type_set.contains(&name).then_some(p) }) - .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + .map(|it| mbe::syntax_node_to_token_tree(it.syntax()).0) .collect(); - let name_token = name_to_token(&token_map, name)?; + let name_token = name_to_token(&tm, name)?; Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types }) } @@ -345,11 +331,12 @@ fn name_to_token(token_map: &TokenMap, name: Option) -> Result tt::Subtree, ) -> ExpandResult { - let info = match parse_adt(tt) { + let info = match parse_adt(tm, tt) { Ok(info) => info, Err(e) => return ExpandResult::new(tt::Subtree::empty(), e), }; @@ -373,10 +360,10 @@ fn expand_simple_derive( }) .unzip(); - where_block.extend(info.associated_types.iter().map(|x| { - let x = x.clone(); + where_block.extend(info.associated_types.iter().map(|it| { + let it = it.clone(); let bound = trait_path.clone(); - quote! { #x : #bound , } + quote! { #it : #bound , } })); let name = info.name; @@ -405,19 +392,21 @@ fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId) -> tt::TokenTree fn copy_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {}) + expand_simple_derive(tt, tm, quote! { #krate::marker::Copy }, |_| quote! {}) } fn clone_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| { + expand_simple_derive(tt, tm, quote! { #krate::clone::Clone }, |adt| { if matches!(adt.shape, AdtShape::Union) { let star = tt::Punct { char: '*', @@ -444,7 +433,7 @@ fn clone_expand( } let name = &adt.name; let patterns = adt.shape.as_pattern(name); - let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() }); + let exprs = adt.shape.as_pattern_map(name, |it| quote! { #it .clone() }); let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| { let fat_arrow = fat_arrow(); quote! { @@ -479,10 +468,11 @@ fn and_and() -> ::tt::Subtree { fn default_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = &find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| { + expand_simple_derive(tt, tm, quote! { #krate::default::Default }, |adt| { let body = match &adt.shape { AdtShape::Struct(fields) => { let name = &adt.name; @@ -518,16 +508,17 @@ fn default_expand( fn debug_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = &find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| { + expand_simple_derive(tt, tm, quote! { #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { - let for_fields = fields.iter().map(|x| { - let x_string = x.to_string(); + let for_fields = fields.iter().map(|it| { + let x_string = it.to_string(); quote! { - .field(#x_string, & #x) + .field(#x_string, & #it) } }); quote! { @@ -535,9 +526,9 @@ fn debug_expand( } } VariantShape::Tuple(n) => { - let for_fields = tuple_field_iterator(*n).map(|x| { + let for_fields = tuple_field_iterator(*n).map(|it| { quote! { - .field( & #x) + .field( & #it) } }); quote! { @@ -598,10 +589,11 @@ fn debug_expand( fn hash_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = &find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| { + expand_simple_derive(tt, tm, quote! { #krate::hash::Hash }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here return quote! {}; @@ -621,7 +613,7 @@ fn hash_expand( let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map( |(pat, names)| { let expr = { - let it = names.iter().map(|x| quote! { #x . hash(ra_expand_state); }); + let it = names.iter().map(|it| quote! { #it . hash(ra_expand_state); }); quote! { { ##it } } @@ -632,9 +624,14 @@ fn hash_expand( } }, ); + let check_discriminant = if matches!(&adt.shape, AdtShape::Enum { .. }) { + quote! { #krate::mem::discriminant(self).hash(ra_expand_state); } + } else { + quote! {} + }; quote! { fn hash(&self, ra_expand_state: &mut H) { - #krate::mem::discriminant(self).hash(ra_expand_state); + #check_discriminant match self { ##arms } @@ -646,19 +643,21 @@ fn hash_expand( fn eq_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {}) + expand_simple_derive(tt, tm, quote! { #krate::cmp::Eq }, |_| quote! {}) } fn partial_eq_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| { + expand_simple_derive(tt, tm, quote! { #krate::cmp::PartialEq }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here return quote! {}; @@ -674,9 +673,9 @@ fn partial_eq_expand( quote!(true) } [first, rest @ ..] => { - let rest = rest.iter().map(|x| { - let t1 = Ident::new(format!("{}_self", x.text), x.span); - let t2 = Ident::new(format!("{}_other", x.text), x.span); + let rest = rest.iter().map(|it| { + let t1 = Ident::new(format!("{}_self", it.text), it.span); + let t2 = Ident::new(format!("{}_other", it.text), it.span); let and_and = and_and(); quote!(#and_and #t1 .eq( #t2 )) }); @@ -708,12 +707,12 @@ fn self_and_other_patterns( adt: &BasicAdtInfo, name: &tt::Ident, ) -> (Vec, Vec) { - let self_patterns = adt.shape.as_pattern_map(name, |x| { - let t = Ident::new(format!("{}_self", x.text), x.span); + let self_patterns = adt.shape.as_pattern_map(name, |it| { + let t = Ident::new(format!("{}_self", it.text), it.span); quote!(#t) }); - let other_patterns = adt.shape.as_pattern_map(name, |x| { - let t = Ident::new(format!("{}_other", x.text), x.span); + let other_patterns = adt.shape.as_pattern_map(name, |it| { + let t = Ident::new(format!("{}_other", it.text), it.span); quote!(#t) }); (self_patterns, other_patterns) @@ -722,10 +721,11 @@ fn self_and_other_patterns( fn ord_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = &find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| { + expand_simple_derive(tt, tm, quote! { #krate::cmp::Ord }, |adt| { fn compare( krate: &tt::TokenTree, left: tt::Subtree, @@ -747,9 +747,6 @@ fn ord_expand( // FIXME: Return expand error here return quote!(); } - let left = quote!(#krate::intrinsics::discriminant_value(self)); - let right = quote!(#krate::intrinsics::discriminant_value(other)); - let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name); let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map( |(pat1, pat2, fields)| { @@ -764,17 +761,17 @@ fn ord_expand( }, ); let fat_arrow = fat_arrow(); - let body = compare( - krate, - left, - right, - quote! { - match (self, other) { - ##arms - _unused #fat_arrow #krate::cmp::Ordering::Equal - } - }, - ); + let mut body = quote! { + match (self, other) { + ##arms + _unused #fat_arrow #krate::cmp::Ordering::Equal + } + }; + if matches!(&adt.shape, AdtShape::Enum { .. }) { + let left = quote!(#krate::intrinsics::discriminant_value(self)); + let right = quote!(#krate::intrinsics::discriminant_value(other)); + body = compare(krate, left, right, body); + } quote! { fn cmp(&self, other: &Self) -> #krate::cmp::Ordering { #body @@ -786,10 +783,11 @@ fn ord_expand( fn partial_ord_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &ast::Adt, + tm: &TokenMap, ) -> ExpandResult { let krate = &find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| { + expand_simple_derive(tt, tm, quote! { #krate::cmp::PartialOrd }, |adt| { fn compare( krate: &tt::TokenTree, left: tt::Subtree, diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index a9f0c154b02b..95c6baf42da7 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -339,7 +339,7 @@ fn format_args_expand_general( parts.push(mem::take(&mut last_part)); let arg_tree = if argument.is_empty() { match args.next() { - Some(x) => x, + Some(it) => it, None => { err = Some(mbe::ExpandError::NoMatchingRule.into()); tt::Subtree::empty() @@ -361,7 +361,7 @@ fn format_args_expand_general( quote!(::core::fmt::Display::fmt) } }; - arg_tts.push(quote! { ::core::fmt::Argument::new(&(#arg_tree), #formatter), }); + arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }); } '}' => { if format_iter.peek() == Some(&'}') { @@ -378,11 +378,11 @@ fn format_args_expand_general( if !last_part.is_empty() { parts.push(last_part); } - let part_tts = parts.into_iter().map(|x| { + let part_tts = parts.into_iter().map(|it| { let text = if let Some(raw) = &raw_sharps { - format!("r{raw}\"{}\"{raw}", x).into() + format!("r{raw}\"{}\"{raw}", it).into() } else { - format!("\"{}\"", x).into() + format!("\"{}\"", it).into() }; let l = tt::Literal { span: tt::TokenId::unspecified(), text }; quote!(#l ,) @@ -574,7 +574,7 @@ fn concat_bytes_expand( syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()), syntax::SyntaxKind::BYTE_STRING => { let components = unquote_byte_string(lit).unwrap_or_default(); - components.into_iter().for_each(|x| bytes.push(x.to_string())); + components.into_iter().for_each(|it| bytes.push(it.to_string())); } _ => { err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); @@ -692,7 +692,7 @@ pub(crate) fn include_arg_to_tt( arg_id: MacroCallId, ) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> { let loc = db.lookup_intern_macro_call(arg_id); - let Some(EagerCallInfo {arg, arg_id: Some(arg_id), .. }) = loc.eager.as_deref() else { + let Some(EagerCallInfo { arg,arg_id, .. }) = loc.eager.as_deref() else { panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager); }; let path = parse_string(&arg.0)?; diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 78b2db7306b9..309c0930d1af 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -1,9 +1,9 @@ //! Defines database & queries for macro expansion. -use base_db::{salsa, Edition, SourceDatabase}; +use base_db::{salsa, CrateId, Edition, SourceDatabase}; use either::Either; use limit::Limit; -use mbe::syntax_node_to_token_tree; +use mbe::{syntax_node_to_token_tree, ValueResult}; use rustc_hash::FxHashSet; use syntax::{ ast::{self, HasAttrs, HasDocComments}, @@ -13,7 +13,7 @@ use triomphe::Arc; use crate::{ ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, - builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander, + builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, @@ -27,62 +27,68 @@ use crate::{ /// Actual max for `analysis-stats .` at some point: 30672. static TOKEN_LIMIT: Limit = Limit::new(1_048_576); +#[derive(Debug, Clone, Eq, PartialEq)] +/// Old-style `macro_rules` or the new macros 2.0 +pub struct DeclarativeMacroExpander { + pub mac: mbe::DeclarativeMacro, + pub def_site_token_map: mbe::TokenMap, +} + +impl DeclarativeMacroExpander { + pub fn expand(&self, tt: tt::Subtree) -> ExpandResult { + match self.mac.err() { + Some(e) => ExpandResult::new( + tt::Subtree::empty(), + ExpandError::other(format!("invalid macro definition: {e}")), + ), + None => self.mac.expand(tt).map_err(Into::into), + } + } + + pub fn map_id_down(&self, token_id: tt::TokenId) -> tt::TokenId { + self.mac.map_id_down(token_id) + } + + pub fn map_id_up(&self, token_id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { + self.mac.map_id_up(token_id) + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum TokenExpander { - /// Old-style `macro_rules` or the new macros 2.0 - DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap }, + DeclarativeMacro(Arc), /// Stuff like `line!` and `file!`. - Builtin(BuiltinFnLikeExpander), + BuiltIn(BuiltinFnLikeExpander), /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.) - BuiltinEager(EagerExpander), + BuiltInEager(EagerExpander), /// `global_allocator` and such. - BuiltinAttr(BuiltinAttrExpander), + BuiltInAttr(BuiltinAttrExpander), /// `derive(Copy)` and such. - BuiltinDerive(BuiltinDeriveExpander), + BuiltInDerive(BuiltinDeriveExpander), /// The thing we love the most here in rust-analyzer -- procedural macros. ProcMacro(ProcMacroExpander), } +// FIXME: Get rid of these methods impl TokenExpander { - fn expand( - &self, - db: &dyn ExpandDatabase, - id: MacroCallId, - tt: &tt::Subtree, - ) -> ExpandResult { - match self { - TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into), - TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into), - TokenExpander::BuiltinEager(it) => it.expand(db, id, tt).map_err(Into::into), - TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt), - TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt), - TokenExpander::ProcMacro(_) => { - // We store the result in salsa db to prevent non-deterministic behavior in - // some proc-macro implementation - // See #4315 for details - db.expand_proc_macro(id) - } - } - } - pub(crate) fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { match self { - TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id), - TokenExpander::Builtin(..) - | TokenExpander::BuiltinEager(..) - | TokenExpander::BuiltinAttr(..) - | TokenExpander::BuiltinDerive(..) + TokenExpander::DeclarativeMacro(expander) => expander.map_id_down(id), + TokenExpander::BuiltIn(..) + | TokenExpander::BuiltInEager(..) + | TokenExpander::BuiltInAttr(..) + | TokenExpander::BuiltInDerive(..) | TokenExpander::ProcMacro(..) => id, } } pub(crate) fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { match self { - TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id), - TokenExpander::Builtin(..) - | TokenExpander::BuiltinEager(..) - | TokenExpander::BuiltinAttr(..) - | TokenExpander::BuiltinDerive(..) + TokenExpander::DeclarativeMacro(expander) => expander.map_id_up(id), + TokenExpander::BuiltIn(..) + | TokenExpander::BuiltInEager(..) + | TokenExpander::BuiltInAttr(..) + | TokenExpander::BuiltInDerive(..) | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), } } @@ -118,14 +124,26 @@ pub trait ExpandDatabase: SourceDatabase { fn macro_arg( &self, id: MacroCallId, - ) -> Option>; + ) -> ValueResult< + Option>, + Arc>, + >; /// Extracts syntax node, corresponding to a macro call. That's a firewall /// query, only typing in the macro call itself changes the returned /// subtree. - fn macro_arg_text(&self, id: MacroCallId) -> Option; - /// Gets the expander for this macro. This compiles declarative macros, and - /// just fetches procedural ones. - fn macro_def(&self, id: MacroDefId) -> Result, mbe::ParseError>; + fn macro_arg_node( + &self, + id: MacroCallId, + ) -> ValueResult, Arc>>; + /// Fetches the expander for this macro. + #[salsa::transparent] + fn macro_expander(&self, id: MacroDefId) -> TokenExpander; + /// Fetches (and compiles) the expander of this decl macro. + fn decl_macro_expander( + &self, + def_crate: CrateId, + id: AstId, + ) -> Arc; /// Expand macro call to a token tree. // This query is LRU cached @@ -141,8 +159,8 @@ pub trait ExpandDatabase: SourceDatabase { /// Special case of the previous query for procedural macros. We can't LRU /// proc macros, since they are not deterministic in general, and /// non-determinism breaks salsa in a very, very, very bad way. - /// @edwin0cheng heroically debugged this once! - fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult; + /// @edwin0cheng heroically debugged this once! See #4315 for details + fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult>; /// Firewall query that returns the errors from the `parse_macro_expansion` query. fn parse_macro_expansion_error( &self, @@ -163,7 +181,6 @@ pub fn expand_speculative( token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, SyntaxToken)> { let loc = db.lookup_intern_macro_call(actual_macro_call); - let macro_def = db.macro_def(loc.def).ok()?; let token_range = token_to_map.text_range(); // Build the subtree and token mapping for the speculative args @@ -221,7 +238,12 @@ pub fn expand_speculative( None => { let range = token_range.checked_sub(speculative_args.text_range().start())?; let token_id = spec_args_tmap.token_by_range(range)?; - macro_def.map_id_down(token_id) + match loc.def.kind { + MacroDefKind::Declarative(it) => { + db.decl_macro_expander(loc.krate, it).map_id_down(token_id) + } + _ => token_id, + } } }; @@ -235,7 +257,17 @@ pub fn expand_speculative( MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?) } - _ => macro_def.expand(db, actual_macro_call, &tt), + MacroDefKind::BuiltInDerive(expander, ..) => { + // this cast is a bit sus, can we avoid losing the typedness here? + let adt = ast::Adt::cast(speculative_args.clone()).unwrap(); + expander.expand(db, actual_macro_call, &adt, &spec_args_tmap) + } + MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand(tt), + MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into), + MacroDefKind::BuiltInEager(it, _) => { + it.expand(db, actual_macro_call, &tt).map_err(Into::into) + } + MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt), }; let expand_to = macro_expand_to(db, actual_macro_call); @@ -297,17 +329,31 @@ fn parse_macro_expansion( ExpandResult { value: (parse, Arc::new(rev_token_map)), err } } +fn parse_macro_expansion_error( + db: &dyn ExpandDatabase, + macro_call_id: MacroCallId, +) -> ExpandResult> { + db.parse_macro_expansion(MacroFile { macro_call_id }) + .map(|it| it.0.errors().to_vec().into_boxed_slice()) +} + fn macro_arg( db: &dyn ExpandDatabase, id: MacroCallId, -) -> Option> { +) -> ValueResult< + Option>, + Arc>, +> { let loc = db.lookup_intern_macro_call(id); - if let Some(EagerCallInfo { arg, arg_id: Some(_), error: _ }) = loc.eager.as_deref() { - return Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default()))); + if let Some(EagerCallInfo { arg, arg_id: _, error: _ }) = loc.eager.as_deref() { + return ValueResult::ok(Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default())))); } - let arg = db.macro_arg_text(id)?; + let ValueResult { value, err } = db.macro_arg_node(id); + let Some(arg) = value else { + return ValueResult { value: None, err }; + }; let node = SyntaxNode::new_root(arg); let censor = censor_for_macro_input(&loc, &node); @@ -325,9 +371,16 @@ fn macro_arg( // proc macros expect their inputs without parentheses, MBEs expect it with them included tt.delimiter = tt::Delimiter::unspecified(); } - Some(Arc::new((tt, tmap, fixups.undo_info))) + let val = Some(Arc::new((tt, tmap, fixups.undo_info))); + match err { + Some(err) => ValueResult::new(val, err), + None => ValueResult::ok(val), + } } +/// Certain macro calls expect some nodes in the input to be preprocessed away, namely: +/// - derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped +/// - attributes expect the invoking attribute to be stripped fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet { // FIXME: handle `cfg_attr` (|| { @@ -364,9 +417,44 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet Option { +fn macro_arg_node( + db: &dyn ExpandDatabase, + id: MacroCallId, +) -> ValueResult, Arc>> { + let err = || -> Arc> { + Arc::new(Box::new([SyntaxError::new_at_offset( + "invalid macro call".to_owned(), + syntax::TextSize::from(0), + )])) + }; let loc = db.lookup_intern_macro_call(id); - let arg = loc.kind.arg(db)?; + let arg = if let MacroDefKind::BuiltInEager(..) = loc.def.kind { + let res = if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() { + Some(mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr).0) + } else { + loc.kind + .arg(db) + .and_then(|arg| ast::TokenTree::cast(arg.value)) + .map(|tt| tt.reparse_as_expr().to_syntax()) + }; + + match res { + Some(res) if res.errors().is_empty() => res.syntax_node(), + Some(res) => { + return ValueResult::new( + Some(res.syntax_node().green().into()), + // Box::<[_]>::from(res.errors()), not stable yet + Arc::new(res.errors().to_vec().into_boxed_slice()), + ); + } + None => return ValueResult::only_err(err()), + } + } else { + match loc.kind.arg(db) { + Some(res) => res.value, + None => return ValueResult::only_err(err()), + } + }; if matches!(loc.kind, MacroCallKind::FnLike { .. }) { let first = arg.first_child_or_token().map_or(T![.], |it| it.kind()); let last = arg.last_child_or_token().map_or(T![.], |it| it.kind()); @@ -381,101 +469,146 @@ fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option // Some day, we'll have explicit recursion counters for all // recursive things, at which point this code might be removed. cov_mark::hit!(issue9358_bad_macro_stack_overflow); - return None; + return ValueResult::only_err(Arc::new(Box::new([SyntaxError::new( + "unbalanced token tree".to_owned(), + arg.text_range(), + )]))); } } - if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() { - Some( - mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr) - .0 - .syntax_node() - .green() - .into(), - ) - } else { - Some(arg.green().into()) - } + ValueResult::ok(Some(arg.green().into())) } -fn macro_def( +fn decl_macro_expander( db: &dyn ExpandDatabase, - id: MacroDefId, -) -> Result, mbe::ParseError> { + def_crate: CrateId, + id: AstId, +) -> Arc { + let is_2021 = db.crate_graph()[def_crate].edition >= Edition::Edition2021; + let (mac, def_site_token_map) = match id.to_node(db) { + ast::Macro::MacroRules(macro_rules) => match macro_rules.token_tree() { + Some(arg) => { + let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); + let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021); + (mac, def_site_token_map) + } + None => ( + mbe::DeclarativeMacro::from_err( + mbe::ParseError::Expected("expected a token tree".into()), + is_2021, + ), + Default::default(), + ), + }, + ast::Macro::MacroDef(macro_def) => match macro_def.body() { + Some(arg) => { + let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); + let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021); + (mac, def_site_token_map) + } + None => ( + mbe::DeclarativeMacro::from_err( + mbe::ParseError::Expected("expected a token tree".into()), + is_2021, + ), + Default::default(), + ), + }, + }; + Arc::new(DeclarativeMacroExpander { mac, def_site_token_map }) +} + +fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander { match id.kind { MacroDefKind::Declarative(ast_id) => { - let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021; - let (mac, def_site_token_map) = match ast_id.to_node(db) { - ast::Macro::MacroRules(macro_rules) => { - let arg = macro_rules - .token_tree() - .ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; - let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); - let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?; - (mac, def_site_token_map) - } - ast::Macro::MacroDef(macro_def) => { - let arg = macro_def - .body() - .ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; - let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); - let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?; - (mac, def_site_token_map) - } - }; - Ok(Arc::new(TokenExpander::DeclarativeMacro { mac, def_site_token_map })) + TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id)) } - MacroDefKind::BuiltIn(expander, _) => Ok(Arc::new(TokenExpander::Builtin(expander))), - MacroDefKind::BuiltInAttr(expander, _) => { - Ok(Arc::new(TokenExpander::BuiltinAttr(expander))) - } - MacroDefKind::BuiltInDerive(expander, _) => { - Ok(Arc::new(TokenExpander::BuiltinDerive(expander))) - } - MacroDefKind::BuiltInEager(expander, ..) => { - Ok(Arc::new(TokenExpander::BuiltinEager(expander))) - } - MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))), + MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander), + MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander), + MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander), + MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander), + MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander), } } fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { let _p = profile::span("macro_expand"); let loc = db.lookup_intern_macro_call(id); - if let Some(EagerCallInfo { arg, arg_id: None, error }) = loc.eager.as_deref() { - // This is an input expansion for an eager macro. These are already pre-expanded - return ExpandResult { value: Arc::new(arg.0.clone()), err: error.clone() }; - } - let expander = match db.macro_def(loc.def) { - Ok(it) => it, - // FIXME: We should make sure to enforce a variant that invalid macro - // definitions do not get expanders that could reach this call path! - Err(err) => { - return ExpandResult { - value: Arc::new(tt::Subtree { - delimiter: tt::Delimiter::UNSPECIFIED, - token_trees: vec![], - }), - err: Some(ExpandError::other(format!("invalid macro definition: {err}"))), - } + + let ExpandResult { value: tt, mut err } = match loc.def.kind { + MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(id), + MacroDefKind::BuiltInDerive(expander, ..) => { + let arg = db.macro_arg_node(id).value.unwrap(); + + let node = SyntaxNode::new_root(arg); + let censor = censor_for_macro_input(&loc, &node); + let mut fixups = fixup::fixup_syntax(&node); + fixups.replace.extend(censor.into_iter().map(|node| (node.into(), Vec::new()))); + let (tmap, _) = mbe::syntax_node_to_token_map_with_modifications( + &node, + fixups.token_map, + fixups.next_id, + fixups.replace, + fixups.append, + ); + + // this cast is a bit sus, can we avoid losing the typedness here? + let adt = ast::Adt::cast(node).unwrap(); + let mut res = expander.expand(db, id, &adt, &tmap); + fixup::reverse_fixups(&mut res.value, &tmap, &fixups.undo_info); + res + } + _ => { + let ValueResult { value, err } = db.macro_arg(id); + let Some(macro_arg) = value else { + return ExpandResult { + value: Arc::new(tt::Subtree { + delimiter: tt::Delimiter::UNSPECIFIED, + token_trees: Vec::new(), + }), + // FIXME: We should make sure to enforce an invariant that invalid macro + // calls do not reach this call path! + err: Some(ExpandError::other("invalid token tree")), + }; + }; + + let (arg, arg_tm, undo_info) = &*macro_arg; + let mut res = match loc.def.kind { + MacroDefKind::Declarative(id) => { + db.decl_macro_expander(loc.def.krate, id).expand(arg.clone()) + } + MacroDefKind::BuiltIn(it, _) => it.expand(db, id, &arg).map_err(Into::into), + // This might look a bit odd, but we do not expand the inputs to eager macros here. + // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. + // That kind of expansion uses the ast id map of an eager macros input though which goes through + // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query + // will end up going through here again, whereas we want to just want to inspect the raw input. + // As such we just return the input subtree here. + MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => { + let mut arg = arg.clone(); + fixup::reverse_fixups(&mut arg, arg_tm, undo_info); + + return ExpandResult { + value: Arc::new(arg), + err: err.map(|err| { + let mut buf = String::new(); + for err in &**err { + use std::fmt::Write; + _ = write!(buf, "{}, ", err); + } + buf.pop(); + buf.pop(); + ExpandError::other(buf) + }), + }; + } + MacroDefKind::BuiltInEager(it, _) => it.expand(db, id, &arg).map_err(Into::into), + MacroDefKind::BuiltInAttr(it, _) => it.expand(db, id, &arg), + _ => unreachable!(), + }; + fixup::reverse_fixups(&mut res.value, arg_tm, undo_info); + res } }; - let Some(macro_arg) = db.macro_arg(id) else { - return ExpandResult { - value: Arc::new( - tt::Subtree { - delimiter: tt::Delimiter::UNSPECIFIED, - token_trees: Vec::new(), - }, - ), - // FIXME: We should make sure to enforce a variant that invalid macro - // calls do not reach this call path! - err: Some(ExpandError::other( - "invalid token tree" - )), - }; - }; - let (arg_tt, arg_tm, undo_info) = &*macro_arg; - let ExpandResult { value: mut tt, mut err } = expander.expand(db, id, arg_tt); if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() { // FIXME: We should report both errors! @@ -483,48 +616,29 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult ExpandResult> { - db.parse_macro_expansion(MacroFile { macro_call_id }) - .map(|it| it.0.errors().to_vec().into_boxed_slice()) -} - -fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { +fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { let loc = db.lookup_intern_macro_call(id); - let Some(macro_arg) = db.macro_arg(id) else { + let Some(macro_arg) = db.macro_arg(id).value else { return ExpandResult { - value: tt::Subtree { + value: Arc::new(tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new(), - }, - err: Some(ExpandError::other( - "invalid token tree" - )), + }), + // FIXME: We should make sure to enforce an invariant that invalid macro + // calls do not reach this call path! + err: Some(ExpandError::other("invalid token tree")), }; }; + let (arg_tt, arg_tm, undo_info) = &*macro_arg; + let expander = match loc.def.kind { MacroDefKind::ProcMacro(expander, ..) => expander, _ => unreachable!(), @@ -533,13 +647,23 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { let mut attr_args = attr_args.0.clone(); - mbe::Shift::new(¯o_arg.0).shift_all(&mut attr_args); + mbe::Shift::new(arg_tt).shift_all(&mut attr_args); Some(attr_args) } _ => None, }; - expander.expand(db, loc.def.krate, loc.krate, ¯o_arg.0, attr_arg.as_ref()) + let ExpandResult { value: mut tt, err } = + expander.expand(db, loc.def.krate, loc.krate, arg_tt, attr_arg.as_ref()); + + // Set a hard limit for the expanded tt + if let Err(value) = check_tt_count(&tt) { + return value; + } + + fixup::reverse_fixups(&mut tt, arg_tm, undo_info); + + ExpandResult { value: Arc::new(tt), err } } fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc { @@ -563,3 +687,22 @@ fn token_tree_to_syntax_node( }; mbe::token_tree_to_syntax_node(tt, entry_point) } + +fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult>> { + let count = tt.count(); + if TOKEN_LIMIT.check(count).is_err() { + Err(ExpandResult { + value: Arc::new(tt::Subtree { + delimiter: tt::Delimiter::UNSPECIFIED, + token_trees: vec![], + }), + err: Some(ExpandError::other(format!( + "macro invocation exceeds token limit: produced {} tokens, limit is {}", + count, + TOKEN_LIMIT.inner(), + ))), + }) + } else { + Ok(()) + } +} diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index 7ee3fd375f6d..876813eab5d5 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs @@ -19,7 +19,8 @@ //! //! See the full discussion : use base_db::CrateId; -use syntax::{ted, Parse, SyntaxNode}; +use rustc_hash::FxHashMap; +use syntax::{ted, Parse, SyntaxNode, TextRange, TextSize, WalkEvent}; use triomphe::Arc; use crate::{ @@ -38,19 +39,8 @@ pub fn expand_eager_macro_input( def: MacroDefId, resolver: &dyn Fn(ModPath) -> Option, ) -> Result>, UnresolvedMacro> { - assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..))); - let token_tree = macro_call.value.token_tree(); - - let Some(token_tree) = token_tree else { - return Ok(ExpandResult { value: None, err: - Some(ExpandError::other( - "invalid token tree" - )), - }); - }; - let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax()); - let ast_map = db.ast_id_map(macro_call.file_id); + // the expansion which the ast id map is built upon has no whitespace, so the offsets are wrong as macro_call is from the token tree that has whitespace! let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value)); let expand_to = ExpandTo::from_call_site(¯o_call.value); @@ -61,41 +51,69 @@ pub fn expand_eager_macro_input( let arg_id = db.intern_macro_call(MacroCallLoc { def, krate, - eager: Some(Box::new(EagerCallInfo { - arg: Arc::new((parsed_args, arg_token_map)), - arg_id: None, - error: None, - })), + eager: None, kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, }); - let arg_as_expr = match db.macro_arg_text(arg_id) { - Some(it) => it, - None => { - return Ok(ExpandResult { - value: None, - err: Some(ExpandError::other("invalid token tree")), - }) + let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = + db.parse_macro_expansion(arg_id.as_macro_file()); + // we need this map here as the expansion of the eager input fake file loses whitespace ... + let mut ws_mapping = FxHashMap::default(); + if let Some((_, tm, _)) = db.macro_arg(arg_id).value.as_deref() { + ws_mapping.extend(tm.entries().filter_map(|(id, range)| { + Some((arg_exp_map.first_range_by_token(id, syntax::SyntaxKind::TOMBSTONE)?, range)) + })); + } + + let ExpandResult { value: expanded_eager_input, err } = { + eager_macro_recur( + db, + &Hygiene::new(db, macro_call.file_id), + InFile::new(arg_id.as_file(), arg_exp.syntax_node()), + krate, + resolver, + )? + }; + let err = parse_err.or(err); + + let Some((expanded_eager_input, mapping)) = expanded_eager_input else { + return Ok(ExpandResult { value: None, err }); + }; + + let og_tmap = mbe::syntax_node_to_token_map( + macro_call.value.token_tree().expect("macro_arg_text succeeded").syntax(), + ); + + let (mut subtree, expanded_eager_input_token_map) = + mbe::syntax_node_to_token_tree(&expanded_eager_input); + + // The tokenmap and ids of subtree point into the expanded syntax node, but that is inaccessible from the outside + // so we need to remap them to the original input of the eager macro. + subtree.visit_ids(&|id| { + // Note: we discard all token ids of braces and the like here, but that's not too bad and only a temporary fix + + if let Some(range) = + expanded_eager_input_token_map.first_range_by_token(id, syntax::SyntaxKind::TOMBSTONE) + { + // remap from expanded eager input to eager input expansion + if let Some(og_range) = mapping.get(&range) { + // remap from eager input expansion to original eager input + if let Some(&og_range) = ws_mapping.get(og_range) { + if let Some(og_token) = og_tmap.token_by_range(og_range) { + return og_token; + } + } + } } - }; - let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur( - db, - &Hygiene::new(db, macro_call.file_id), - InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)), - krate, - resolver, - )?; - let Some(expanded_eager_input) = expanded_eager_input else { - return Ok(ExpandResult { value: None, err }) - }; - let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input); + tt::TokenId::UNSPECIFIED + }); subtree.delimiter = crate::tt::Delimiter::unspecified(); let loc = MacroCallLoc { def, krate, eager: Some(Box::new(EagerCallInfo { - arg: Arc::new((subtree, token_map)), - arg_id: Some(arg_id), + arg: Arc::new((subtree, og_tmap)), + arg_id, error: err.clone(), })), kind: MacroCallKind::FnLike { ast_id: call_id, expand_to }, @@ -109,19 +127,16 @@ fn lazy_expand( def: &MacroDefId, macro_call: InFile, krate: CrateId, -) -> ExpandResult>> { +) -> ExpandResult<(InFile>, Arc)> { let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); let expand_to = ExpandTo::from_call_site(¯o_call.value); - let id = def.as_lazy_macro( - db, - krate, - MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to }, - ); - + let ast_id = macro_call.with_value(ast_id); + let id = def.as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id, expand_to }); let macro_file = id.as_macro_file(); - db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0)) + db.parse_macro_expansion(macro_file) + .map(|parse| (InFile::new(macro_file.into(), parse.0), parse.1)) } fn eager_macro_recur( @@ -130,18 +145,43 @@ fn eager_macro_recur( curr: InFile, krate: CrateId, macro_resolver: &dyn Fn(ModPath) -> Option, -) -> Result>, UnresolvedMacro> { +) -> Result)>>, UnresolvedMacro> { let original = curr.value.clone_for_update(); + let mut mapping = FxHashMap::default(); - let children = original.descendants().filter_map(ast::MacroCall::cast); let mut replacements = Vec::new(); // Note: We only report a single error inside of eager expansions let mut error = None; + let mut offset = 0i32; + let apply_offset = |it: TextSize, offset: i32| { + TextSize::from(u32::try_from(offset + u32::from(it) as i32).unwrap_or_default()) + }; + let mut children = original.preorder_with_tokens(); // Collect replacement - for child in children { - let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) { + while let Some(child) = children.next() { + let WalkEvent::Enter(child) = child else { continue }; + let call = match child { + syntax::NodeOrToken::Node(node) => match ast::MacroCall::cast(node) { + Some(it) => { + children.skip_subtree(); + it + } + None => continue, + }, + syntax::NodeOrToken::Token(t) => { + mapping.insert( + TextRange::new( + apply_offset(t.text_range().start(), offset), + apply_offset(t.text_range().end(), offset), + ), + t.text_range(), + ); + continue; + } + }; + let def = match call.path().and_then(|path| ModPath::from_src(db, path, hygiene)) { Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?, None => { error = Some(ExpandError::other("malformed macro invocation")); @@ -153,7 +193,7 @@ fn eager_macro_recur( let ExpandResult { value, err } = match expand_eager_macro_input( db, krate, - curr.with_value(child.clone()), + curr.with_value(call.clone()), def, macro_resolver, ) { @@ -161,9 +201,22 @@ fn eager_macro_recur( Err(err) => return Err(err), }; match value { - Some(call) => { + Some(call_id) => { let ExpandResult { value, err: err2 } = - db.parse_macro_expansion(call.as_macro_file()); + db.parse_macro_expansion(call_id.as_macro_file()); + + let call_tt_start = + call.token_tree().unwrap().syntax().text_range().start(); + let call_start = apply_offset(call.syntax().text_range().start(), offset); + if let Some((_, arg_map, _)) = db.macro_arg(call_id).value.as_deref() { + mapping.extend(arg_map.entries().filter_map(|(tid, range)| { + value + .1 + .first_range_by_token(tid, syntax::SyntaxKind::TOMBSTONE) + .map(|r| (r + call_start, range + call_tt_start)) + })); + }; + ExpandResult { value: Some(value.0.syntax_node().clone_for_update()), err: err.or(err2), @@ -177,36 +230,61 @@ fn eager_macro_recur( | MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInDerive(..) | MacroDefKind::ProcMacro(..) => { - let ExpandResult { value, err } = - lazy_expand(db, &def, curr.with_value(child.clone()), krate); + let ExpandResult { value: (parse, tm), err } = + lazy_expand(db, &def, curr.with_value(call.clone()), krate); + let decl_mac = if let MacroDefKind::Declarative(ast_id) = def.kind { + Some(db.decl_macro_expander(def.krate, ast_id)) + } else { + None + }; // replace macro inside - let hygiene = Hygiene::new(db, value.file_id); + let hygiene = Hygiene::new(db, parse.file_id); let ExpandResult { value, err: error } = eager_macro_recur( db, &hygiene, // FIXME: We discard parse errors here - value.map(|it| it.syntax_node()), + parse.as_ref().map(|it| it.syntax_node()), krate, macro_resolver, )?; let err = err.or(error); - ExpandResult { value, err } + + let call_tt_start = call.token_tree().unwrap().syntax().text_range().start(); + let call_start = apply_offset(call.syntax().text_range().start(), offset); + if let Some((_tt, arg_map, _)) = parse + .file_id + .macro_file() + .and_then(|id| db.macro_arg(id.macro_call_id).value) + .as_deref() + { + mapping.extend(arg_map.entries().filter_map(|(tid, range)| { + tm.first_range_by_token( + decl_mac.as_ref().map(|it| it.map_id_down(tid)).unwrap_or(tid), + syntax::SyntaxKind::TOMBSTONE, + ) + .map(|r| (r + call_start, range + call_tt_start)) + })); + }; + // FIXME: Do we need to re-use _m here? + ExpandResult { value: value.map(|(n, _m)| n), err } } }; if err.is_some() { error = err; } // check if the whole original syntax is replaced - if child.syntax() == &original { - return Ok(ExpandResult { value, err: error }); + if call.syntax() == &original { + return Ok(ExpandResult { value: value.zip(Some(mapping)), err: error }); } if let Some(insert) = value { - replacements.push((child, insert)); + offset += u32::from(insert.text_range().len()) as i32 + - u32::from(call.syntax().text_range().len()) as i32; + replacements.push((call, insert)); } } replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); - Ok(ExpandResult { value: Some(original), err: error }) + Ok(ExpandResult { value: Some((original, mapping)), err: error }) } diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 00796e7c0dbb..e6e8d8c02992 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -26,7 +26,7 @@ pub(crate) struct SyntaxFixups { /// This is the information needed to reverse the fixups. #[derive(Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { - original: Vec, + original: Box<[Subtree]>, } const EMPTY_ID: SyntheticTokenId = SyntheticTokenId(!0); @@ -272,7 +272,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { replace, token_map, next_id, - undo_info: SyntaxFixupUndoInfo { original }, + undo_info: SyntaxFixupUndoInfo { original: original.into_boxed_slice() }, } } @@ -472,13 +472,13 @@ fn foo () {match __ra_fixup {}} check( r#" fn foo() { - match x { + match it { } } "#, expect![[r#" -fn foo () {match x {}} +fn foo () {match it {}} "#]], ) } @@ -547,11 +547,11 @@ fn foo () {a . __ra_fixup ; bar () ;} check( r#" fn foo() { - let x = a + let it = a } "#, expect![[r#" -fn foo () {let x = a ;} +fn foo () {let it = a ;} "#]], ) } @@ -561,11 +561,11 @@ fn foo () {let x = a ;} check( r#" fn foo() { - let x = a. + let it = a. } "#, expect![[r#" -fn foo () {let x = a . __ra_fixup ;} +fn foo () {let it = a . __ra_fixup ;} "#]], ) } diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index 10f8fe9cec42..54e74d50c87e 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -126,7 +126,7 @@ struct HygieneInfo { /// The start offset of the `macro_rules!` arguments or attribute input. attr_input_or_mac_def_start: Option>, - macro_def: Arc, + macro_def: TokenExpander, macro_arg: Arc<(crate::tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>, macro_arg_shift: mbe::Shift, exp_map: Arc, @@ -149,19 +149,15 @@ impl HygieneInfo { token_id = unshifted; (&attr_args.1, self.attr_input_or_mac_def_start?) } - None => ( - &self.macro_arg.1, - InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()), - ), + None => (&self.macro_arg.1, loc.kind.arg(db)?.map(|it| it.text_range().start())), }, _ => match origin { - mbe::Origin::Call => ( - &self.macro_arg.1, - InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()), - ), - mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def_start) { - (TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => { - (def_site_token_map, *tt) + mbe::Origin::Call => { + (&self.macro_arg.1, loc.kind.arg(db)?.map(|it| it.text_range().start())) + } + mbe::Origin::Def => match (&self.macro_def, &self.attr_input_or_mac_def_start) { + (TokenExpander::DeclarativeMacro(expander), Some(tt)) => { + (&expander.def_site_token_map, *tt) } _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), }, @@ -198,9 +194,9 @@ fn make_hygiene_info( _ => None, }); - let macro_def = db.macro_def(loc.def).ok()?; + let macro_def = db.macro_expander(loc.def); let (_, exp_map) = db.parse_macro_expansion(macro_file).value; - let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| { + let macro_arg = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| { Arc::new(( tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() }, Default::default(), diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index e0c199328ef3..9ed6c31ddde5 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -37,11 +37,11 @@ use either::Either; use syntax::{ algo::{self, skip_trivia_token}, ast::{self, AstNode, HasDocComments}, - Direction, SyntaxNode, SyntaxToken, + AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, }; use crate::{ - ast_id_map::FileAstId, + ast_id_map::{AstIdNode, ErasedFileAstId, FileAstId}, attrs::AttrId, builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, @@ -127,7 +127,8 @@ impl_intern_key!(MacroCallId); pub struct MacroCallLoc { pub def: MacroDefId, pub(crate) krate: CrateId, - /// Some if `def` is a builtin eager macro. + /// Some if this is a macro call for an eager macro. Note that this is `None` + /// for the eager input macro file. eager: Option>, pub kind: MacroCallKind, } @@ -152,11 +153,10 @@ pub enum MacroDefKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct EagerCallInfo { - /// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro! + /// The expanded argument of the eager macro. arg: Arc<(tt::Subtree, TokenMap)>, - /// call id of the eager macro's input file. If this is none, macro call containing this call info - /// is an eager macro's input, otherwise it is its output. - arg_id: Option, + /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). + arg_id: MacroCallId, error: Option, } @@ -221,11 +221,7 @@ impl HirFileId { HirFileIdRepr::FileId(id) => break id, HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id); - let is_include_expansion = loc.def.is_include() - && matches!( - loc.eager.as_deref(), - Some(EagerCallInfo { arg_id: Some(_), .. }) - ); + let is_include_expansion = loc.def.is_include() && loc.eager.is_some(); file_id = match is_include_expansion.then(|| db.include_expand(macro_call_id)) { Some(Ok((_, file))) => file.into(), _ => loc.kind.file_id(), @@ -270,57 +266,13 @@ impl HirFileId { /// Return expansion information if it is a macro-expansion file pub fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option { let macro_file = self.macro_file()?; - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - - let arg_tt = loc.kind.arg(db)?; - - let macro_def = db.macro_def(loc.def).ok()?; - let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; - let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| { - Arc::new(( - tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() }, - Default::default(), - Default::default(), - )) - }); - - let def = loc.def.ast_id().left().and_then(|id| { - let def_tt = match id.to_node(db) { - ast::Macro::MacroRules(mac) => mac.token_tree()?, - ast::Macro::MacroDef(_) if matches!(*macro_def, TokenExpander::BuiltinAttr(_)) => { - return None - } - ast::Macro::MacroDef(mac) => mac.body()?, - }; - Some(InFile::new(id.file_id, def_tt)) - }); - let attr_input_or_mac_def = def.or_else(|| match loc.kind { - MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { - // FIXME: handle `cfg_attr` - let tt = ast_id - .to_node(db) - .doc_comments_and_attrs() - .nth(invoc_attr_index.ast_index()) - .and_then(Either::left)? - .token_tree()?; - Some(InFile::new(ast_id.file_id, tt)) - } - _ => None, - }); - - Some(ExpansionInfo { - expanded: InFile::new(self, parse.syntax_node()), - arg: InFile::new(loc.kind.file_id(), arg_tt), - attr_input_or_mac_def, - macro_arg_shift: mbe::Shift::new(¯o_arg.0), - macro_arg, - macro_def, - exp_map, - }) + ExpansionInfo::new(db, macro_file) } - /// Indicate it is macro file generated for builtin derive - pub fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> Option> { + pub fn as_builtin_derive_attr_node( + &self, + db: &dyn db::ExpandDatabase, + ) -> Option> { let macro_file = self.macro_file()?; let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let attr = match loc.def.kind { @@ -333,8 +285,22 @@ impl HirFileId { pub fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool { match self.macro_file() { Some(macro_file) => { - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - matches!(loc.def.kind, MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _)) + matches!( + db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind, + MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) + ) + } + None => false, + } + } + + pub fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool { + match self.macro_file() { + Some(macro_file) => { + matches!( + db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind, + MacroDefKind::BuiltInDerive(..) + ) } None => false, } @@ -344,8 +310,7 @@ impl HirFileId { pub fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool { match self.macro_file() { Some(macro_file) => { - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - loc.def.is_include() + db.lookup_intern_macro_call(macro_file.macro_call_id).def.is_include() } _ => false, } @@ -355,7 +320,7 @@ impl HirFileId { match self.macro_file() { Some(macro_file) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - matches!(loc.eager.as_deref(), Some(EagerCallInfo { .. })) + matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) } _ => false, } @@ -536,9 +501,9 @@ impl MacroCallKind { }; let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), - MacroCallKind::Derive { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), - MacroCallKind::Attr { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), + MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), + MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(), + MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(), }; FileRange { range, file_id } @@ -588,13 +553,18 @@ impl MacroCallKind { FileRange { range, file_id } } - fn arg(&self, db: &dyn db::ExpandDatabase) -> Option { + fn arg(&self, db: &dyn db::ExpandDatabase) -> Option> { match self { - MacroCallKind::FnLike { ast_id, .. } => { - Some(ast_id.to_node(db).token_tree()?.syntax().clone()) + MacroCallKind::FnLike { ast_id, .. } => ast_id + .to_in_file_node(db) + .map(|it| Some(it.token_tree()?.syntax().clone())) + .transpose(), + MacroCallKind::Derive { ast_id, .. } => { + Some(ast_id.to_in_file_node(db).syntax().cloned()) + } + MacroCallKind::Attr { ast_id, .. } => { + Some(ast_id.to_in_file_node(db).syntax().cloned()) } - MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), - MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), } } } @@ -612,13 +582,13 @@ impl MacroCallId { /// ExpansionInfo mainly describes how to map text range between src and expanded macro #[derive(Debug, Clone, PartialEq, Eq)] pub struct ExpansionInfo { - expanded: InFile, + expanded: InMacroFile, /// The argument TokenTree or item for attributes arg: InFile, /// The `macro_rules!` or attribute input. attr_input_or_mac_def: Option>, - macro_def: Arc, + macro_def: TokenExpander, macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>, /// A shift built from `macro_arg`'s subtree, relevant for attributes as the item is the macro arg /// and as such we need to shift tokens if they are part of an attributes input instead of their item. @@ -628,7 +598,7 @@ pub struct ExpansionInfo { impl ExpansionInfo { pub fn expanded(&self) -> InFile { - self.expanded.clone() + self.expanded.clone().into() } pub fn call_node(&self) -> Option> { @@ -659,7 +629,7 @@ impl ExpansionInfo { let token_id_in_attr_input = if let Some(item) = item { // check if we are mapping down in an attribute input // this is a special case as attributes can have two inputs - let call_id = self.expanded.file_id.macro_file()?.macro_call_id; + let call_id = self.expanded.file_id.macro_call_id; let loc = db.lookup_intern_macro_call(call_id); let token_range = token.value.text_range(); @@ -705,7 +675,7 @@ impl ExpansionInfo { let relative_range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; let token_id = self.macro_arg.1.token_by_range(relative_range)?; - // conditionally shift the id by a declaratives macro definition + // conditionally shift the id by a declarative macro definition self.macro_def.map_id_down(token_id) } }; @@ -715,7 +685,7 @@ impl ExpansionInfo { .ranges_by_token(token_id, token.value.kind()) .flat_map(move |range| self.expanded.value.covering_element(range).into_token()); - Some(tokens.map(move |token| self.expanded.with_value(token))) + Some(tokens.map(move |token| InFile::new(self.expanded.file_id.into(), token))) } /// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion. @@ -724,18 +694,17 @@ impl ExpansionInfo { db: &dyn db::ExpandDatabase, token: InFile<&SyntaxToken>, ) -> Option<(InFile, Origin)> { + assert_eq!(token.file_id, self.expanded.file_id.into()); // Fetch the id through its text range, let token_id = self.exp_map.token_by_range(token.value.text_range())?; // conditionally unshifting the id to accommodate for macro-rules def site let (mut token_id, origin) = self.macro_def.map_id_up(token_id); - let call_id = self.expanded.file_id.macro_file()?.macro_call_id; + let call_id = self.expanded.file_id.macro_call_id; let loc = db.lookup_intern_macro_call(call_id); // Special case: map tokens from `include!` expansions to the included file - if loc.def.is_include() - && matches!(loc.eager.as_deref(), Some(EagerCallInfo { arg_id: Some(_), .. })) - { + if loc.def.is_include() { if let Ok((tt_and_map, file_id)) = db.include_expand(call_id) { let range = tt_and_map.1.first_range_by_token(token_id, token.value.kind())?; let source = db.parse(file_id); @@ -765,9 +734,9 @@ impl ExpansionInfo { } _ => match origin { mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), - mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def) { - (TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => { - (def_site_token_map, tt.syntax().cloned()) + mbe::Origin::Def => match (&self.macro_def, &self.attr_input_or_mac_def) { + (TokenExpander::DeclarativeMacro(expander), Some(tt)) => { + (&expander.def_site_token_map, tt.syntax().cloned()) } _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), }, @@ -779,6 +748,58 @@ impl ExpansionInfo { tt.value.covering_element(range + tt.value.text_range().start()).into_token()?; Some((tt.with_value(token), origin)) } + + fn new(db: &dyn db::ExpandDatabase, macro_file: MacroFile) -> Option { + let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); + + let arg_tt = loc.kind.arg(db)?; + + let macro_def = db.macro_expander(loc.def); + let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; + let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; + + let macro_arg = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| { + Arc::new(( + tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() }, + Default::default(), + Default::default(), + )) + }); + + let def = loc.def.ast_id().left().and_then(|id| { + let def_tt = match id.to_node(db) { + ast::Macro::MacroRules(mac) => mac.token_tree()?, + ast::Macro::MacroDef(_) if matches!(macro_def, TokenExpander::BuiltInAttr(_)) => { + return None + } + ast::Macro::MacroDef(mac) => mac.body()?, + }; + Some(InFile::new(id.file_id, def_tt)) + }); + let attr_input_or_mac_def = def.or_else(|| match loc.kind { + MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + // FIXME: handle `cfg_attr` + let tt = ast_id + .to_node(db) + .doc_comments_and_attrs() + .nth(invoc_attr_index.ast_index()) + .and_then(Either::left)? + .token_tree()?; + Some(InFile::new(ast_id.file_id, tt)) + } + _ => None, + }); + + Some(ExpansionInfo { + expanded, + arg: arg_tt, + attr_input_or_mac_def, + macro_arg_shift: mbe::Shift::new(¯o_arg.0), + macro_arg, + macro_def, + exp_map, + }) + } } /// `AstId` points to an AST node in any file. @@ -786,10 +807,26 @@ impl ExpansionInfo { /// It is stable across reparses, and can be used as salsa key/value. pub type AstId = InFile>; -impl AstId { +impl AstId { pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N { - let root = db.parse_or_expand(self.file_id); - db.ast_id_map(self.file_id).get(self.value).to_node(&root) + self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) + } + pub fn to_in_file_node(&self, db: &dyn db::ExpandDatabase) -> InFile { + InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))) + } + pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr { + db.ast_id_map(self.file_id).get(self.value) + } +} + +pub type ErasedAstId = InFile; + +impl ErasedAstId { + pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode { + self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) + } + pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr { + db.ast_id_map(self.file_id).get_raw(self.value) } } @@ -850,7 +887,7 @@ impl InFile> { } } -impl<'a> InFile<&'a SyntaxNode> { +impl InFile<&SyntaxNode> { pub fn ancestors_with_macros( self, db: &dyn db::ExpandDatabase, @@ -1011,6 +1048,18 @@ impl InFile { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub struct InMacroFile { + pub file_id: MacroFile, + pub value: T, +} + +impl From> for InFile { + fn from(macro_file: InMacroFile) -> Self { + InFile { file_id: macro_file.file_id.into(), value: macro_file.value } + } +} + fn ascend_node_border_tokens( db: &dyn db::ExpandDatabase, InFile { file_id, value: node }: InFile<&SyntaxNode>, diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 47a8ab7de77f..69aa09c4a521 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -126,7 +126,7 @@ struct Display<'a> { path: &'a ModPath, } -impl<'a> fmt::Display for Display<'a> { +impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { display_fmt_path(self.db, self.path, f, true) } @@ -137,7 +137,7 @@ struct UnescapedDisplay<'a> { path: &'a UnescapedModPath<'a>, } -impl<'a> fmt::Display for UnescapedDisplay<'a> { +impl fmt::Display for UnescapedDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { display_fmt_path(self.db, self.path.0, f, false) } diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index f8dbb842775c..7c179c0cf959 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -24,7 +24,7 @@ enum Repr { TupleField(usize), } -impl<'a> UnescapedName<'a> { +impl UnescapedName<'_> { /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case. pub fn to_smol_str(&self) -> SmolStr { @@ -40,7 +40,7 @@ impl<'a> UnescapedName<'a> { } } - pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + pub fn display(&self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + '_ { _ = db; UnescapedDisplay { name: self } } @@ -96,6 +96,15 @@ impl Name { Name::new_inline("[missing name]") } + /// Returns true if this is a fake name for things missing in the source code. See + /// [`missing()`][Self::missing] for details. + /// + /// Use this method instead of comparing with `Self::missing()` as missing names + /// (ideally should) have a `gensym` semantics. + pub fn is_missing(&self) -> bool { + self == &Name::missing() + } + /// Generates a new name which is only equal to itself, by incrementing a counter. Due /// its implementation, it should not be used in things that salsa considers, like /// type names or field names, and it should be only used in names of local variables @@ -162,7 +171,7 @@ struct Display<'a> { name: &'a Name, } -impl<'a> fmt::Display for Display<'a> { +impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.name.0 { Repr::Text(text) => fmt::Display::fmt(&text, f), @@ -175,7 +184,7 @@ struct UnescapedDisplay<'a> { name: &'a UnescapedName<'a>, } -impl<'a> fmt::Display for UnescapedDisplay<'a> { +impl fmt::Display for UnescapedDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.name.0 .0 { Repr::Text(text) => { @@ -282,8 +291,10 @@ pub mod known { alloc, iter, ops, + fmt, future, result, + string, boxed, option, prelude, @@ -311,6 +322,7 @@ pub mod known { RangeToInclusive, RangeTo, Range, + String, Neg, Not, None, @@ -321,6 +333,7 @@ pub mod known { iter_mut, len, is_empty, + as_str, new, // Builtin macros asm, @@ -334,6 +347,7 @@ pub mod known { core_panic, env, file, + format, format_args_nl, format_args, global_asm, @@ -365,6 +379,7 @@ pub mod known { cfg_eval, crate_type, derive, + derive_const, global_allocator, no_core, no_std, diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index c8bea34507c7..abc19d63abf0 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -19,14 +19,15 @@ bitflags = "2.1.0" smallvec.workspace = true ena = "0.14.0" either = "1.7.0" +oorandom = "11.1.3" tracing = "0.1.35" rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.91.0", default-features = false } -chalk-ir = "0.91.0" -chalk-recursive = { version = "0.91.0", default-features = false } -chalk-derive = "0.91.0" -la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +chalk-solve = { version = "0.92.0", default-features = false } +chalk-ir = "0.92.0" +chalk-recursive = { version = "0.92.0", default-features = false } +chalk-derive = "0.92.0" +la-arena.workspace = true once_cell = "1.17.0" triomphe.workspace = true nohash-hasher.workspace = true @@ -47,7 +48,6 @@ limit.workspace = true expect-test = "1.4.0" tracing = "0.1.35" tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "env-filter", "registry", ] } tracing-tree = "0.2.1" diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs index 3860bccec8bb..4625a3b01a36 100644 --- a/crates/hir-ty/src/autoderef.rs +++ b/crates/hir-ty/src/autoderef.rs @@ -36,7 +36,7 @@ pub fn autoderef( ) -> impl Iterator { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new(&mut table, ty); + let mut autoderef = Autoderef::new(&mut table, ty, false); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be @@ -63,12 +63,13 @@ pub(crate) struct Autoderef<'a, 'db> { ty: Ty, at_start: bool, steps: Vec<(AutoderefKind, Ty)>, + explicit: bool, } impl<'a, 'db> Autoderef<'a, 'db> { - pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty) -> Self { + pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: Vec::new() } + Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit } } pub(crate) fn step_count(&self) -> usize { @@ -97,7 +98,7 @@ impl Iterator for Autoderef<'_, '_> { return None; } - let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?; + let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?; self.steps.push((kind, self.ty.clone())); self.ty = new_ty; @@ -109,8 +110,9 @@ impl Iterator for Autoderef<'_, '_> { pub(crate) fn autoderef_step( table: &mut InferenceTable<'_>, ty: Ty, + explicit: bool, ) -> Option<(AutoderefKind, Ty)> { - if let Some(derefed) = builtin_deref(table, &ty, false) { + if let Some(derefed) = builtin_deref(table, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) @@ -124,7 +126,6 @@ pub(crate) fn builtin_deref<'ty>( ) -> Option<&'ty Ty> { match ty.kind(Interner) { TyKind::Ref(.., ty) => Some(ty), - // FIXME: Maybe accept this but diagnose if its not explicit? TyKind::Raw(.., ty) if explicit => Some(ty), &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => { if crate::lang_items::is_box(table.db, adt) { diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index 5dd8e2719a23..f4fbace19e35 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -5,13 +5,13 @@ use std::{iter, sync::Arc}; use tracing::debug; -use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds}; +use chalk_ir::{cast::Caster, fold::shift::Shift, CanonicalVarKinds}; use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use base_db::CrateId; use hir_def::{ hir::Movability, - lang_item::{lang_attr, LangItem, LangItemTarget}, + lang_item::{LangItem, LangItemTarget}, AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, }; use hir_expand::name::name; @@ -46,7 +46,7 @@ pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue; pub(crate) type Variances = chalk_ir::Variances; -impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { +impl chalk_solve::RustIrDatabase for ChalkContext<'_> { fn associated_ty_data(&self, id: AssocTypeId) -> Arc { self.db.associated_ty_data(id) } @@ -60,9 +60,37 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { // FIXME: keep track of these Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None }) } - fn discriminant_type(&self, _ty: chalk_ir::Ty) -> chalk_ir::Ty { - // FIXME: keep track of this - chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner) + fn discriminant_type(&self, ty: chalk_ir::Ty) -> chalk_ir::Ty { + if let chalk_ir::TyKind::Adt(id, _) = ty.kind(Interner) { + if let hir_def::AdtId::EnumId(e) = id.0 { + let enum_data = self.db.enum_data(e); + let ty = enum_data.repr.unwrap_or_default().discr_type(); + return chalk_ir::TyKind::Scalar(match ty { + hir_def::layout::IntegerType::Pointer(is_signed) => match is_signed { + true => chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize), + false => chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize), + }, + hir_def::layout::IntegerType::Fixed(size, is_signed) => match is_signed { + true => chalk_ir::Scalar::Int(match size { + hir_def::layout::Integer::I8 => chalk_ir::IntTy::I8, + hir_def::layout::Integer::I16 => chalk_ir::IntTy::I16, + hir_def::layout::Integer::I32 => chalk_ir::IntTy::I32, + hir_def::layout::Integer::I64 => chalk_ir::IntTy::I64, + hir_def::layout::Integer::I128 => chalk_ir::IntTy::I128, + }), + false => chalk_ir::Scalar::Uint(match size { + hir_def::layout::Integer::I8 => chalk_ir::UintTy::U8, + hir_def::layout::Integer::I16 => chalk_ir::UintTy::U16, + hir_def::layout::Integer::I32 => chalk_ir::UintTy::U32, + hir_def::layout::Integer::I64 => chalk_ir::UintTy::U64, + hir_def::layout::Integer::I128 => chalk_ir::UintTy::U128, + }), + }, + }) + .intern(Interner); + } + } + chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8)).intern(Interner) } fn impl_datum(&self, impl_id: ImplId) -> Arc { self.db.impl_datum(self.krate, impl_id) @@ -565,7 +593,7 @@ pub(crate) fn trait_datum_query( let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect(); let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; - let well_known = lang_attr(db.upcast(), trait_).and_then(well_known_trait_from_lang_item); + let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item); let trait_datum = TraitDatum { id: trait_id, binders: make_binders(db, &generic_params, trait_datum_bound), @@ -593,6 +621,7 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option { LangItem::Unsize => WellKnownTrait::Unsize, LangItem::Tuple => WellKnownTrait::Tuple, LangItem::PointeeTrait => WellKnownTrait::Pointee, + LangItem::FnPtrTrait => WellKnownTrait::FnPtr, _ => return None, }) } @@ -614,6 +643,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem { WellKnownTrait::Unpin => LangItem::Unpin, WellKnownTrait::Unsize => LangItem::Unsize, WellKnownTrait::Pointee => LangItem::PointeeTrait, + WellKnownTrait::FnPtr => LangItem::FnPtrTrait, } } @@ -844,28 +874,34 @@ pub(super) fn generic_predicate_to_inline_bound( } let args_no_self = trait_ref.substitution.as_slice(Interner)[1..] .iter() - .map(|ty| ty.clone().cast(Interner)) + .cloned() + .casted(Interner) .collect(); let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self }; Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound))) } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { - let trait_ = projection_ty.trait_(db); - if projection_ty.self_type_parameter(db) != self_ty_shifted_in { + let generics = + generics(db.upcast(), from_assoc_type_id(projection_ty.associated_ty_id).into()); + let (assoc_args, trait_args) = + projection_ty.substitution.as_slice(Interner).split_at(generics.len_self()); + let (self_ty, args_no_self) = + trait_args.split_first().expect("projection without trait self type"); + if self_ty.assert_ty_ref(Interner) != &self_ty_shifted_in { return None; } - let args_no_self = projection_ty.substitution.as_slice(Interner)[1..] - .iter() - .map(|ty| ty.clone().cast(Interner)) - .collect(); + + let args_no_self = args_no_self.iter().cloned().casted(Interner).collect(); + let parameters = assoc_args.to_vec(); + let alias_eq_bound = rust_ir::AliasEqBound { value: ty.clone(), trait_bound: rust_ir::TraitBound { - trait_id: to_chalk_trait_id(trait_), + trait_id: to_chalk_trait_id(projection_ty.trait_(db)), args_no_self, }, associated_ty_id: projection_ty.associated_ty_id, - parameters: Vec::new(), // FIXME we don't support generic associated types yet + parameters, }; Some(chalk_ir::Binders::new( binders, diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index a8071591adac..c0b243ea2485 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -343,7 +343,8 @@ impl TyExt for Ty { fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool { let crate_id = owner.module(db.upcast()).krate(); - let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else { + let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|it| it.as_trait()) + else { return false; }; let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build(); diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 262341c6e9ee..4de90d40a7c7 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -88,7 +88,7 @@ pub(crate) fn path_to_const( ConstValue::Placeholder(to_placeholder_idx(db, p.into())) } ParamLoweringMode::Variable => match args.param_idx(p.into()) { - Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)), + Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), None => { never!( "Generic list doesn't contain this param: {:?}, {:?}, {:?}", @@ -139,11 +139,11 @@ pub fn intern_const_ref( let bytes = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. - let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } LiteralConstRef::UInt(i) => { - let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()), @@ -171,7 +171,7 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { chalk_ir::ConstValue::InferenceVar(_) => None, chalk_ir::ConstValue::Placeholder(_) => None, chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))), + ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(&it, false))), ConstScalar::UnevaluatedConst(c, subst) => { let ec = db.const_eval(*c, subst.clone()).ok()?; try_const_usize(db, &ec) @@ -228,7 +228,7 @@ pub(crate) fn const_eval_query( } GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?, }; - let c = interpret_mir(db, &body, false).0?; + let c = interpret_mir(db, body, false).0?; Ok(c) } @@ -241,7 +241,7 @@ pub(crate) fn const_eval_static_query( Substitution::empty(Interner), db.trait_environment_for_body(def.into()), )?; - let c = interpret_mir(db, &body, false).0?; + let c = interpret_mir(db, body, false).0?; Ok(c) } @@ -268,7 +268,7 @@ pub(crate) fn const_eval_discriminant_variant( Substitution::empty(Interner), db.trait_environment_for_body(def), )?; - let c = interpret_mir(db, &mir_body, false).0?; + let c = interpret_mir(db, mir_body, false).0?; let c = try_const_usize(db, &c).unwrap() as i128; Ok(c) } @@ -293,7 +293,7 @@ pub(crate) fn eval_to_const( } let infer = ctx.clone().resolve_all(); if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, &mir_body, true).0 { + if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true).0 { return result; } } diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 0db1fefbfef5..5bb327606d32 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1,10 +1,11 @@ use base_db::{fixture::WithFixture, FileId}; use chalk_ir::Substitution; use hir_def::db::DefDatabase; +use test_utils::skip_slow_tests; use crate::{ consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar, - Interner, + Interner, MemoryMap, }; use super::{ @@ -16,7 +17,7 @@ mod intrinsics; fn simplify(e: ConstEvalError) -> ConstEvalError { match e { - ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => { + ConstEvalError::MirEvalError(MirEvalError::InFunction(e, _)) => { simplify(ConstEvalError::MirEvalError(*e)) } _ => e, @@ -36,7 +37,37 @@ fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) { #[track_caller] fn check_number(ra_fixture: &str, answer: i128) { - let (db, file_id) = TestDB::with_single_file(ra_fixture); + check_answer(ra_fixture, |b, _| { + assert_eq!( + b, + &answer.to_le_bytes()[0..b.len()], + "Bytes differ. In decimal form: actual = {}, expected = {answer}", + i128::from_le_bytes(pad16(b, true)) + ); + }); +} + +#[track_caller] +fn check_str(ra_fixture: &str, answer: &str) { + check_answer(ra_fixture, |b, mm| { + let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); + let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); + let Some(bytes) = mm.get(addr, size) else { + panic!("string data missed in the memory map"); + }; + assert_eq!( + bytes, + answer.as_bytes(), + "Bytes differ. In string form: actual = {}, expected = {answer}", + String::from_utf8_lossy(bytes) + ); + }); +} + +#[track_caller] +fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) { + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); let r = match eval_goal(&db, file_id) { Ok(t) => t, Err(e) => { @@ -46,13 +77,8 @@ fn check_number(ra_fixture: &str, answer: i128) { }; match &r.data(Interner).value { chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(b, _) => { - assert_eq!( - b, - &answer.to_le_bytes()[0..b.len()], - "Bytes differ. In decimal form: actual = {}, expected = {answer}", - i128::from_le_bytes(pad16(b, true)) - ); + ConstScalar::Bytes(b, mm) => { + check(b, mm); } x => panic!("Expected number but found {:?}", x), }, @@ -87,7 +113,7 @@ fn eval_goal(db: &TestDB, file_id: FileId) -> Result { } _ => None, }) - .unwrap(); + .expect("No const named GOAL found in the test"); db.const_eval(const_id.into(), Substitution::empty(Interner)) } @@ -108,6 +134,7 @@ fn bit_op() { check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| { e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string())) }); + check_number(r#"const GOAL: i32 = 100000000i32 << 11"#, (100000000i32 << 11) as i128); } #[test] @@ -166,14 +193,21 @@ fn casts() { check_number( r#" //- minicore: coerce_unsized, index, slice + struct X { + unsize_field: [u8], + } + const GOAL: usize = { let a = [10, 20, 3, 15]; let x: &[i32] = &a; - let y: *const [i32] = x; - let z = y as *const [u8]; // slice fat pointer cast don't touch metadata - let q = z as *const str; - let p = q as *const [u8]; - let w = unsafe { &*z }; + let x: *const [i32] = x; + let x = x as *const [u8]; // slice fat pointer cast don't touch metadata + let x = x as *const str; + let x = x as *const X; + let x = x as *const [i16]; + let x = x as *const X; + let x = x as *const [u8]; + let w = unsafe { &*x }; w.len() }; "#, @@ -198,6 +232,30 @@ fn raw_pointer_equality() { ); } +#[test] +fn alignment() { + check_answer( + r#" +//- minicore: transmute +use core::mem::transmute; +const GOAL: usize = { + let x: i64 = 2; + transmute(&x) +} + "#, + |b, _| assert_eq!(b[0] % 8, 0), + ); + check_answer( + r#" +//- minicore: transmute +use core::mem::transmute; +static X: i64 = 12; +const GOAL: usize = transmute(&X); + "#, + |b, _| assert_eq!(b[0] % 8, 0), + ); +} + #[test] fn locals() { check_number( @@ -1550,6 +1608,30 @@ fn closures() { ); } +#[test] +fn manual_fn_trait_impl() { + check_number( + r#" +//- minicore: fn, copy +struct S(i32); + +impl FnOnce<(i32, i32)> for S { + type Output = i32; + + extern "rust-call" fn call_once(self, arg: (i32, i32)) -> i32 { + arg.0 + arg.1 + self.0 + } +} + +const GOAL: i32 = { + let s = S(1); + s(2, 3) +}; +"#, + 6, + ); +} + #[test] fn closure_and_impl_fn() { check_number( @@ -1661,6 +1743,18 @@ fn function_pointer() { "#, 5, ); + check_number( + r#" + fn add2(x: u8) -> u8 { + x + 2 + } + const GOAL: u8 = { + let plus2 = add2 as fn(u8) -> u8; + plus2(3) + }; + "#, + 5, + ); check_number( r#" //- minicore: coerce_unsized, index, slice @@ -1849,6 +1943,38 @@ fn dyn_trait() { ); } +#[test] +fn coerce_unsized() { + check_number( + r#" +//- minicore: coerce_unsized, deref_mut, slice, index, transmute, non_null +use core::ops::{Deref, DerefMut, CoerceUnsized}; +use core::{marker::Unsize, mem::transmute, ptr::NonNull}; + +struct ArcInner { + strong: usize, + weak: usize, + data: T, +} + +pub struct Arc { + inner: NonNull>, +} + +impl, U: ?Sized> CoerceUnsized> for Arc {} + +const GOAL: usize = { + let x = transmute::>(12); + let y: Arc<[i32]> = x; + let z = transmute::, (usize, usize)>(y); + z.1 +}; + + "#, + 3, + ); +} + #[test] fn boxes() { check_number( @@ -1960,6 +2086,17 @@ fn array_and_index() { ); } +#[test] +fn string() { + check_str( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: &str = "hello"; + "#, + "hello", + ); +} + #[test] fn byte_string() { check_number( @@ -2018,6 +2155,57 @@ fn consts() { "#, 6, ); + + check_number( + r#" + const F1: i32 = 2147483647; + const F2: i32 = F1 - 25; + const GOAL: i32 = F2; + "#, + 2147483622, + ); + + check_number( + r#" + const F1: i32 = -2147483648; + const F2: i32 = F1 + 18; + const GOAL: i32 = F2; + "#, + -2147483630, + ); + + check_number( + r#" + const F1: i32 = 10; + const F2: i32 = F1 - 20; + const GOAL: i32 = F2; + "#, + -10, + ); + + check_number( + r#" + const F1: i32 = 25; + const F2: i32 = F1 - 25; + const GOAL: i32 = F2; + "#, + 0, + ); + + check_number( + r#" + const A: i32 = -2147483648; + const GOAL: bool = A > 0; + "#, + 0, + ); + + check_number( + r#" + const GOAL: i64 = (-2147483648_i32) as i64; + "#, + -2147483648, + ); } #[test] @@ -2285,6 +2473,25 @@ fn const_trait_assoc() { "#, 32, ); + check_number( + r#" + //- /a/lib.rs crate:a + pub trait ToConst { + const VAL: usize; + } + pub const fn to_const() -> usize { + T::VAL + } + //- /main.rs crate:main deps:a + use a::{ToConst, to_const}; + struct U0; + impl ToConst for U0 { + const VAL: usize = 5; + } + const GOAL: usize = to_const::(); + "#, + 5, + ); check_number( r#" struct S(*mut T); @@ -2310,22 +2517,12 @@ fn const_trait_assoc() { ); } -#[test] -fn panic_messages() { - check_fail( - r#" - //- minicore: panic - const GOAL: u8 = { - let x: u16 = 2; - panic!("hello"); - }; - "#, - |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())), - ); -} - #[test] fn exec_limits() { + if skip_slow_tests() { + return; + } + check_fail( r#" const GOAL: usize = loop {}; @@ -2339,7 +2536,7 @@ fn exec_limits() { } const GOAL: i32 = f(0); "#, - |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow), + |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded), ); // Reasonable code should still work check_number( @@ -2362,6 +2559,28 @@ fn exec_limits() { ); } +#[test] +fn memory_limit() { + check_fail( + r#" + extern "Rust" { + #[rustc_allocator] + fn __rust_alloc(size: usize, align: usize) -> *mut u8; + } + + const GOAL: u8 = unsafe { + __rust_alloc(30_000_000_000, 1); // 30GB + 2 + }; + "#, + |e| { + e == ConstEvalError::MirEvalError(MirEvalError::Panic( + "Memory allocation of 30000000000 bytes failed".to_string(), + )) + }, + ); +} + #[test] fn type_error() { check_fail( @@ -2376,6 +2595,37 @@ fn type_error() { ); } +#[test] +fn unsized_field() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice, transmute + use core::mem::transmute; + + struct Slice([u8]); + struct Slice2(Slice); + + impl Slice2 { + fn as_inner(&self) -> &Slice { + &self.0 + } + + fn as_bytes(&self) -> &[u8] { + &self.as_inner().0 + } + } + + const GOAL: u8 = unsafe { + let x: &[u8] = &[1, 2, 3]; + let x: &Slice2 = transmute(x); + let x = x.as_bytes(); + x[0] + x[1] + x[2] + }; + "#, + 6, + ); +} + #[test] fn unsized_local() { check_fail( diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index e05d824dbacf..9253e31d77b6 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -14,6 +14,171 @@ fn size_of() { ); } +#[test] +fn size_of_val() { + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + struct X(i32, u8); + + const GOAL: usize = size_of_val(&X(1, 2)); + "#, + 8, + ); + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + const GOAL: usize = { + let it: &[i32] = &[1, 2, 3]; + size_of_val(it) + }; + "#, + 12, + ); + check_number( + r#" + //- minicore: coerce_unsized, transmute + use core::mem::transmute; + + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + struct X { + x: i64, + y: u8, + t: [i32], + } + + const GOAL: usize = unsafe { + let y: &X = transmute([0usize, 3]); + size_of_val(y) + }; + "#, + 24, + ); + check_number( + r#" + //- minicore: coerce_unsized, transmute + use core::mem::transmute; + + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + struct X { + x: i32, + y: i64, + t: [u8], + } + + const GOAL: usize = unsafe { + let y: &X = transmute([0usize, 15]); + size_of_val(y) + }; + "#, + 32, + ); + check_number( + r#" + //- minicore: coerce_unsized, fmt, builtin_impls + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + const GOAL: usize = { + let x: &i16 = &5; + let y: &dyn core::fmt::Debug = x; + let z: &dyn core::fmt::Debug = &y; + size_of_val(x) + size_of_val(y) * 10 + size_of_val(z) * 100 + }; + "#, + 1622, + ); + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + const GOAL: usize = { + size_of_val("salam") + }; + "#, + 5, + ); +} + +#[test] +fn min_align_of_val() { + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn min_align_of_val(_: *const T) -> usize; + } + + struct X(i32, u8); + + const GOAL: usize = min_align_of_val(&X(1, 2)); + "#, + 4, + ); + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn min_align_of_val(_: *const T) -> usize; + } + + const GOAL: usize = { + let x: &[i32] = &[1, 2, 3]; + min_align_of_val(x) + }; + "#, + 4, + ); +} + +#[test] +fn type_name() { + check_str( + r#" + extern "rust-intrinsic" { + pub fn type_name() -> &'static str; + } + + const GOAL: &str = type_name::(); + "#, + "i32", + ); + check_str( + r#" + extern "rust-intrinsic" { + pub fn type_name() -> &'static str; + } + + mod mod1 { + pub mod mod2 { + pub struct Ty; + } + } + + const GOAL: &str = type_name::(); + "#, + "mod1::mod2::Ty", + ); +} + #[test] fn transmute() { check_number( @@ -28,10 +193,29 @@ fn transmute() { ); } +#[test] +fn read_via_copy() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn read_via_copy(e: *const T) -> T; + pub fn volatile_load(e: *const T) -> T; + } + + const GOAL: i32 = { + let x = 2; + read_via_copy(&x) + volatile_load(&x) + }; + "#, + 4, + ); +} + #[test] fn const_eval_select() { check_number( r#" + //- minicore: fn extern "rust-intrinsic" { pub fn const_eval_select(arg: ARG, called_in_const: F, called_at_rt: G) -> RET where @@ -68,7 +252,7 @@ fn wrapping_add() { } #[test] -fn saturating_add() { +fn saturating() { check_number( r#" extern "rust-intrinsic" { @@ -79,6 +263,16 @@ fn saturating_add() { "#, 255, ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn saturating_sub(a: T, b: T) -> T; + } + + const GOAL: bool = saturating_sub(5u8, 7) == 0 && saturating_sub(8u8, 4) == 4; + "#, + 1, + ); check_number( r#" extern "rust-intrinsic" { @@ -112,6 +306,7 @@ fn allocator() { *ptr = 23; *ptr2 = 32; let ptr = __rust_realloc(ptr, 4, 1, 8); + let ptr = __rust_realloc(ptr, 8, 1, 3); let ptr2 = ((ptr as usize) + 1) as *mut u8; *ptr + *ptr2 }; @@ -159,6 +354,24 @@ fn needs_drop() { ); } +#[test] +fn discriminant_value() { + check_number( + r#" + //- minicore: discriminant, option + use core::marker::DiscriminantKind; + extern "rust-intrinsic" { + pub fn discriminant_value(v: &T) -> ::Discriminant; + } + const GOAL: bool = { + discriminant_value(&Some(2i32)) == discriminant_value(&Some(5i32)) + && discriminant_value(&Some(2i32)) != discriminant_value(&None::) + }; + "#, + 1, + ); +} + #[test] fn likely() { check_number( @@ -327,6 +540,24 @@ fn copy_nonoverlapping() { ); } +#[test] +fn write_bytes() { + check_number( + r#" + extern "rust-intrinsic" { + fn write_bytes(dst: *mut T, val: u8, count: usize); + } + + const GOAL: i32 = unsafe { + let mut x = 2; + write_bytes(&mut x, 5, 1); + x + }; + "#, + 0x05050505, + ); +} + #[test] fn copy() { check_number( @@ -362,6 +593,20 @@ fn ctpop() { ); } +#[test] +fn ctlz() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn ctlz(x: T) -> T; + } + + const GOAL: u8 = ctlz(0b0001_1100_u8); + "#, + 3, + ); +} + #[test] fn cttz() { check_number( @@ -375,3 +620,85 @@ fn cttz() { 3, ); } + +#[test] +fn rotate() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn rotate_left(x: T, y: T) -> T; + } + + const GOAL: i64 = rotate_left(0xaa00000000006e1i64, 12); + "#, + 0x6e10aa, + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn rotate_right(x: T, y: T) -> T; + } + + const GOAL: i64 = rotate_right(0x6e10aa, 12); + "#, + 0xaa00000000006e1, + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn rotate_left(x: T, y: T) -> T; + } + + const GOAL: i8 = rotate_left(129, 2); + "#, + 6, + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn rotate_right(x: T, y: T) -> T; + } + + const GOAL: i32 = rotate_right(10006016, 1020315); + "#, + 320192512, + ); +} + +#[test] +fn simd() { + check_number( + r#" + pub struct i8x16( + i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8, + ); + extern "platform-intrinsic" { + pub fn simd_bitmask(x: T) -> U; + } + const GOAL: u16 = simd_bitmask(i8x16( + 0, 1, 0, 0, 2, 255, 100, 0, 50, 0, 1, 1, 0, 0, 0, 0 + )); + "#, + 0b0000110101110010, + ); + check_number( + r#" + pub struct i8x16( + i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8, + ); + extern "platform-intrinsic" { + pub fn simd_lt(x: T, y: T) -> U; + pub fn simd_bitmask(x: T) -> U; + } + const GOAL: u16 = simd_bitmask(simd_lt::( + i8x16( + -105, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + ), + i8x16( + -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + ), + )); + "#, + 0xFFFF, + ); +} diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 9dd810f844d4..14b719ea4124 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -110,6 +110,14 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; + #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] + fn lookup_impl_method( + &self, + env: Arc, + func: FunctionId, + fn_subst: Substitution, + ) -> (FunctionId, Substitution); + #[salsa::invoke(crate::lower::callable_item_sig)] fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; diff --git a/crates/hir-ty/src/diagnostics.rs b/crates/hir-ty/src/diagnostics.rs index 4b147b99707c..ef43ed5c463f 100644 --- a/crates/hir-ty/src/diagnostics.rs +++ b/crates/hir-ty/src/diagnostics.rs @@ -5,7 +5,7 @@ mod unsafe_check; mod decl_check; pub use crate::diagnostics::{ - decl_check::{incorrect_case, IncorrectCase}, + decl_check::{incorrect_case, CaseType, IncorrectCase}, expr::{ record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic, }, diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 1233469b947b..73c8ad3dd5a9 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -57,11 +57,11 @@ pub fn incorrect_case( #[derive(Debug)] pub enum CaseType { - // `some_var` + /// `some_var` LowerSnakeCase, - // `SOME_CONST` + /// `SOME_CONST` UpperSnakeCase, - // `SomeStruct` + /// `SomeStruct` UpperCamelCase, } @@ -181,6 +181,7 @@ impl<'a> DeclValidator<'a> { AttrDefId::TraitAliasId(taid) => Some(taid.lookup(self.db.upcast()).container.into()), AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()), AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()), + AttrDefId::ExternCrateId(id) => Some(id.lookup(self.db.upcast()).container.into()), // These warnings should not explore macro definitions at all AttrDefId::MacroId(_) => None, AttrDefId::AdtId(aid) => match aid { diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index c1df24d17291..8cffdef289ea 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -192,7 +192,7 @@ pub trait HirDisplay { } } -impl<'a> HirFormatter<'a> { +impl HirFormatter<'_> { pub fn write_joined( &mut self, iter: impl IntoIterator, @@ -342,7 +342,7 @@ impl HirDisplayWrapper<'_, T> { } } -impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> +impl fmt::Display for HirDisplayWrapper<'_, T> where T: HirDisplay, { @@ -360,7 +360,7 @@ where const TYPE_HINT_TRUNCATION: &str = "…"; -impl HirDisplay for &'_ T { +impl HirDisplay for &T { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { HirDisplay::hir_fmt(*self, f) } @@ -446,28 +446,6 @@ impl HirDisplay for Const { } } -pub struct HexifiedConst(pub Const); - -impl HirDisplay for HexifiedConst { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let data = &self.0.data(Interner); - if let TyKind::Scalar(s) = data.ty.kind(Interner) { - if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) { - if let ConstValue::Concrete(c) = &data.value { - if let ConstScalar::Bytes(b, m) = &c.interned { - let value = u128::from_le_bytes(pad16(b, false)); - if value >= 10 { - render_const_scalar(f, &b, m, &data.ty)?; - return write!(f, " ({:#X})", value); - } - } - } - } - } - self.0.hir_fmt(f) - } -} - fn render_const_scalar( f: &mut HirFormatter<'_>, b: &[u8], @@ -481,28 +459,28 @@ fn render_const_scalar( TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), Scalar::Char => { - let x = u128::from_le_bytes(pad16(b, false)) as u32; - let Ok(c) = char::try_from(x) else { + let it = u128::from_le_bytes(pad16(b, false)) as u32; + let Ok(c) = char::try_from(it) else { return f.write_str(""); }; write!(f, "{c:?}") } Scalar::Int(_) => { - let x = i128::from_le_bytes(pad16(b, true)); - write!(f, "{x}") + let it = i128::from_le_bytes(pad16(b, true)); + write!(f, "{it}") } Scalar::Uint(_) => { - let x = u128::from_le_bytes(pad16(b, false)); - write!(f, "{x}") + let it = u128::from_le_bytes(pad16(b, false)); + write!(f, "{it}") } Scalar::Float(fl) => match fl { chalk_ir::FloatTy::F32 => { - let x = f32::from_le_bytes(b.try_into().unwrap()); - write!(f, "{x:?}") + let it = f32::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") } chalk_ir::FloatTy::F64 => { - let x = f64::from_le_bytes(b.try_into().unwrap()); - write!(f, "{x:?}") + let it = f64::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") } }, }, @@ -636,7 +614,8 @@ fn render_const_scalar( } hir_def::AdtId::EnumId(e) => { let Some((var_id, var_layout)) = - detect_variant_from_bytes(&layout, f.db, krate, b, e) else { + detect_variant_from_bytes(&layout, f.db, krate, b, e) + else { return f.write_str(""); }; let data = &f.db.enum_data(e).variants[var_id]; @@ -658,8 +637,8 @@ fn render_const_scalar( } TyKind::FnDef(..) => ty.hir_fmt(f), TyKind::Function(_) | TyKind::Raw(_, _) => { - let x = u128::from_le_bytes(pad16(b, false)); - write!(f, "{:#X} as ", x)?; + let it = u128::from_le_bytes(pad16(b, false)); + write!(f, "{:#X} as ", it)?; ty.hir_fmt(f) } TyKind::Array(ty, len) => { @@ -735,7 +714,7 @@ fn render_variant_after_name( } write!(f, " }}")?; } else { - let mut it = it.map(|x| x.0); + let mut it = it.map(|it| it.0); write!(f, "(")?; if let Some(id) = it.next() { render_field(f, id)?; @@ -1277,19 +1256,20 @@ fn hir_fmt_generics( i: usize, parameters: &Substitution, ) -> bool { - if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error) + if parameter.ty(Interner).map(|it| it.kind(Interner)) + == Some(&TyKind::Error) { return true; } if let Some(ConstValue::Concrete(c)) = - parameter.constant(Interner).map(|x| &x.data(Interner).value) + parameter.constant(Interner).map(|it| &it.data(Interner).value) { if c.interned == ConstScalar::Unknown { return true; } } let default_parameter = match default_parameters.get(i) { - Some(x) => x, + Some(it) => it, None => return true, }; let actual_default = diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 1ac0837b5b2c..0a617dae7d43 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -290,7 +290,7 @@ impl Default for InternedStandardTypes { /// ``` /// /// Note that for a struct, the 'deep' unsizing of the struct is not recorded. -/// E.g., `struct Foo { x: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]> +/// E.g., `struct Foo { it: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]> /// The autoderef and -ref are the same as in the above example, but the type /// stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about /// the underlying conversions from `[i32; 4]` to `[i32]`. @@ -1172,7 +1172,7 @@ impl<'a> InferenceContext<'a> { unresolved: Option, path: &ModPath, ) -> (Ty, Option) { - let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0); + let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0); match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { @@ -1232,7 +1232,9 @@ impl<'a> InferenceContext<'a> { .as_function()? .lookup(self.db.upcast()) .container - else { return None }; + else { + return None; + }; self.resolve_output_on(trait_) } @@ -1322,7 +1324,7 @@ impl Expectation { /// The primary use case is where the expected type is a fat pointer, /// like `&[isize]`. For example, consider the following statement: /// - /// let x: &[isize] = &[1, 2, 3]; + /// let it: &[isize] = &[1, 2, 3]; /// /// In this case, the expected type for the `&[1, 2, 3]` expression is /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index ff64ae252bce..972e5321a475 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -139,7 +139,7 @@ impl HirPlace { ) -> CaptureKind { match current_capture { CaptureKind::ByRef(BorrowKind::Mut { .. }) => { - if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) { + if self.projections[len..].iter().any(|it| *it == ProjectionElem::Deref) { current_capture = CaptureKind::ByRef(BorrowKind::Unique); } } @@ -199,7 +199,7 @@ impl CapturedItem { .to_string(), VariantData::Tuple(fields) => fields .iter() - .position(|x| x.0 == f.local_id) + .position(|it| it.0 == f.local_id) .unwrap_or_default() .to_string(), VariantData::Unit => "[missing field]".to_string(), @@ -439,10 +439,10 @@ impl InferenceContext<'_> { } fn walk_expr(&mut self, tgt_expr: ExprId) { - if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) { + if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) { // FIXME: this take is completely unneeded, and just is here to make borrow checker // happy. Remove it if you can. - let x_taken = mem::take(x); + let x_taken = mem::take(it); self.walk_expr_with_adjust(tgt_expr, &x_taken); *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken; } else { @@ -536,7 +536,7 @@ impl InferenceContext<'_> { if let &Some(expr) = spread { self.consume_expr(expr); } - self.consume_exprs(fields.iter().map(|x| x.expr)); + self.consume_exprs(fields.iter().map(|it| it.expr)); } Expr::Field { expr, name: _ } => self.select_from_expr(*expr), Expr::UnaryOp { expr, op: UnaryOp::Deref } => { @@ -548,7 +548,7 @@ impl InferenceContext<'_> { } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { let mutability = 'b: { if let Some(deref_trait) = - self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait()) + self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) { if let Some(deref_fn) = self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) @@ -615,8 +615,8 @@ impl InferenceContext<'_> { "We sort closures, so we should always have data for inner closures", ); let mut cc = mem::take(&mut self.current_captures); - cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| { - CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span } + cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| { + CapturedItemWithoutTy { place: it.place.clone(), kind: it.kind, span: it.span } })); self.current_captures = cc; } @@ -694,7 +694,7 @@ impl InferenceContext<'_> { }, }, } - if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) { + if self.result.pat_adjustments.get(&p).map_or(false, |it| !it.is_empty()) { for_mut = BorrowKind::Unique; } self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); @@ -706,9 +706,9 @@ impl InferenceContext<'_> { fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { let mut ty = None; - if let Some(x) = self.result.expr_adjustments.get(&e) { - if let Some(x) = x.last() { - ty = Some(x.target.clone()); + if let Some(it) = self.result.expr_adjustments.get(&e) { + if let Some(it) = it.last() { + ty = Some(it.target.clone()); } } ty.unwrap_or_else(|| self.expr_ty(e)) @@ -727,7 +727,7 @@ impl InferenceContext<'_> { // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We // should probably let chalk know which closures are copy, but I don't know how doing it // without creating query cycles. - return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true); + return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true); } self.table.resolve_completely(ty).is_copy(self.db, self.owner) } @@ -748,7 +748,7 @@ impl InferenceContext<'_> { } fn minimize_captures(&mut self) { - self.current_captures.sort_by_key(|x| x.place.projections.len()); + self.current_captures.sort_by_key(|it| it.place.projections.len()); let mut hash_map = HashMap::::new(); let result = mem::take(&mut self.current_captures); for item in result { @@ -759,7 +759,7 @@ impl InferenceContext<'_> { break Some(*k); } match it.next() { - Some(x) => lookup_place.projections.push(x.clone()), + Some(it) => lookup_place.projections.push(it.clone()), None => break None, } }; @@ -780,7 +780,7 @@ impl InferenceContext<'_> { } fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) { - let cnt = self.result.pat_adjustments.get(&pat).map(|x| x.len()).unwrap_or_default(); + let cnt = self.result.pat_adjustments.get(&pat).map(|it| it.len()).unwrap_or_default(); place.projections = place .projections .iter() @@ -894,10 +894,10 @@ impl InferenceContext<'_> { fn closure_kind(&self) -> FnTrait { let mut r = FnTrait::Fn; - for x in &self.current_captures { + for it in &self.current_captures { r = cmp::min( r, - match &x.kind { + match &it.kind { CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => { FnTrait::FnMut } @@ -933,7 +933,7 @@ impl InferenceContext<'_> { } self.minimize_captures(); let result = mem::take(&mut self.current_captures); - let captures = result.into_iter().map(|x| x.with_ty(self)).collect::>(); + let captures = result.into_iter().map(|it| it.with_ty(self)).collect::>(); self.result.closure_info.insert(closure, (captures, closure_kind)); closure_kind } @@ -973,20 +973,20 @@ impl InferenceContext<'_> { fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { let mut deferred_closures = mem::take(&mut self.deferred_closures); let mut dependents_count: FxHashMap = - deferred_closures.keys().map(|x| (*x, 0)).collect(); + deferred_closures.keys().map(|it| (*it, 0)).collect(); for (_, deps) in &self.closure_dependencies { for dep in deps { *dependents_count.entry(*dep).or_default() += 1; } } let mut queue: Vec<_> = - deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect(); + deferred_closures.keys().copied().filter(|it| dependents_count[it] == 0).collect(); let mut result = vec![]; - while let Some(x) = queue.pop() { - if let Some(d) = deferred_closures.remove(&x) { - result.push((x, d)); + while let Some(it) = queue.pop() { + if let Some(d) = deferred_closures.remove(&it) { + result.push((it, d)); } - for dep in self.closure_dependencies.get(&x).into_iter().flat_map(|x| x.iter()) { + for dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) { let cnt = dependents_count.get_mut(dep).unwrap(); *cnt -= 1; if *cnt == 0 { diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 05a476f632dc..8e7e62c4961c 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -220,7 +220,7 @@ pub(crate) fn coerce( Ok((adjustments, table.resolve_with_fallback(ty, &fallback))) } -impl<'a> InferenceContext<'a> { +impl InferenceContext<'_> { /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. pub(super) fn coerce( @@ -239,7 +239,7 @@ impl<'a> InferenceContext<'a> { } } -impl<'a> InferenceTable<'a> { +impl InferenceTable<'_> { /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. pub(crate) fn coerce( @@ -377,7 +377,7 @@ impl<'a> InferenceTable<'a> { let snapshot = self.snapshot(); - let mut autoderef = Autoderef::new(self, from_ty.clone()); + let mut autoderef = Autoderef::new(self, from_ty.clone(), false); let mut first_error = None; let mut found = None; diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 194471f0048f..4b14345aa39f 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -50,7 +50,7 @@ use super::{ InferenceContext, InferenceDiagnostic, TypeMismatch, }; -impl<'a> InferenceContext<'a> { +impl InferenceContext<'_> { pub(crate) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(tgt_expr, expected); if let Some(expected_ty) = expected.only_has_type(&mut self.table) { @@ -316,7 +316,7 @@ impl<'a> InferenceContext<'a> { } Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone()); + let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); let (res, derefed_callee) = 'b: { // manual loop to be able to access `derefs.table` while let Some((callee_deref_ty, _)) = derefs.next() { @@ -928,7 +928,7 @@ impl<'a> InferenceContext<'a> { if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) { if adjustments .last() - .map(|x| matches!(x.kind, Adjust::Borrow(_))) + .map(|it| matches!(it.kind, Adjust::Borrow(_))) .unwrap_or(true) { // prefer reborrow to move @@ -1385,7 +1385,7 @@ impl<'a> InferenceContext<'a> { receiver_ty: &Ty, name: &Name, ) -> Option<(Ty, Option, Vec, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone()); + let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind(Interner) { @@ -1449,6 +1449,13 @@ impl<'a> InferenceContext<'a> { fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none()); + + if name.is_missing() { + // Bail out early, don't even try to look up field. Also, we don't issue an unresolved + // field diagnostic because this is a syntax error rather than a semantic error. + return self.err_ty(); + } + match self.lookup_field(&receiver_ty, name) { Some((ty, field_id, adjustments, is_public)) => { self.write_expr_adj(receiver, adjustments); diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 46f2e1d7d125..f517bc2c09f1 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -12,7 +12,7 @@ use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Ov use super::InferenceContext; -impl<'a> InferenceContext<'a> { +impl InferenceContext<'_> { pub(crate) fn infer_mut_body(&mut self) { self.infer_mut_expr(self.body.body_expr, Mutability::Not); } @@ -73,12 +73,12 @@ impl<'a> InferenceContext<'a> { self.infer_mut_expr(c, Mutability::Not); self.infer_mut_expr(body, Mutability::Not); } - Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ } - | Expr::Call { callee: x, args, is_assignee_expr: _ } => { - self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x))); + Expr::MethodCall { receiver: it, method_name: _, args, generic_args: _ } + | Expr::Call { callee: it, args, is_assignee_expr: _ } => { + self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*it))); } Expr::Match { expr, arms } => { - let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat)); + let m = self.pat_iter_bound_mutability(arms.iter().map(|it| it.pat)); self.infer_mut_expr(*expr, m); for arm in arms.iter() { self.infer_mut_expr(arm.expr, Mutability::Not); @@ -96,7 +96,7 @@ impl<'a> InferenceContext<'a> { } } Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => { - self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread)) + self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } &Expr::Index { base, index } => { if mutability == Mutability::Mut { @@ -204,8 +204,8 @@ impl<'a> InferenceContext<'a> { } /// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions - /// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in - /// `let (ref x0, ref x1) = *x;` we should use `Deref`. + /// mutable. For example in `let (ref mut x0, ref x1) = *it;` we need to use `DerefMut` for `*it` but in + /// `let (ref x0, ref x1) = *it;` we should use `Deref`. fn pat_bound_mutability(&self, pat: PatId) -> Mutability { let mut r = Mutability::Not; self.body.walk_bindings_in_pat(pat, |b| { diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 2480f8babac4..5da0ab76b880 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -56,7 +56,7 @@ impl PatLike for PatId { } } -impl<'a> InferenceContext<'a> { +impl InferenceContext<'_> { /// Infers type for tuple struct pattern or its corresponding assignee expression. /// /// Ellipses found in the original pattern or expression must be filtered out. @@ -306,7 +306,7 @@ impl<'a> InferenceContext<'a> { self.result .pat_adjustments .get(&pat) - .and_then(|x| x.first()) + .and_then(|it| it.first()) .unwrap_or(&self.result.type_of_pat[pat]) .clone() } diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index e33d8f1795e2..385f39f5374d 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -22,7 +22,7 @@ use crate::{ TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, }; -impl<'a> InferenceContext<'a> { +impl InferenceContext<'_> { pub(super) fn canonicalize + HasInterner>( &mut self, t: T, @@ -91,7 +91,7 @@ pub(crate) fn unify( let mut table = InferenceTable::new(db, env); let vars = Substitution::from_iter( Interner, - tys.binders.iter(Interner).map(|x| match &x.kind { + tys.binders.iter(Interner).map(|it| match &it.kind { chalk_ir::VariableKind::Ty(_) => { GenericArgData::Ty(table.new_type_var()).intern(Interner) } @@ -547,7 +547,7 @@ impl<'a> InferenceTable<'a> { table: &'a mut InferenceTable<'b>, highest_known_var: InferenceVar, } - impl<'a, 'b> TypeFolder for VarFudger<'a, 'b> { + impl TypeFolder for VarFudger<'_, '_> { fn as_dyn(&mut self) -> &mut dyn TypeFolder { self } @@ -686,8 +686,8 @@ impl<'a> InferenceTable<'a> { let mut arg_tys = vec![]; let arg_ty = TyBuilder::tuple(num_args) - .fill(|x| { - let arg = match x { + .fill(|it| { + let arg = match it { ParamKind::Type => self.new_type_var(), ParamKind::Const(ty) => { never!("Tuple with const parameter"); @@ -753,7 +753,7 @@ impl<'a> InferenceTable<'a> { { fold_tys_and_consts( ty, - |x, _| match x { + |it, _| match it { Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), }, @@ -798,7 +798,7 @@ impl<'a> InferenceTable<'a> { } } -impl<'a> fmt::Debug for InferenceTable<'a> { +impl fmt::Debug for InferenceTable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish() } @@ -826,7 +826,7 @@ mod resolve { pub(super) var_stack: &'a mut Vec, pub(super) fallback: F, } - impl<'a, 'b, F> TypeFolder for Resolver<'a, 'b, F> + impl TypeFolder for Resolver<'_, '_, F> where F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 35d3407c16c1..72e2bcc55590 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -7,7 +7,7 @@ use hir_def::{ Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, }, - LocalEnumVariantId, LocalFieldId, + LocalEnumVariantId, LocalFieldId, StructId, }; use la_arena::{Idx, RawIdx}; use stdx::never; @@ -24,8 +24,8 @@ pub use self::{ }; macro_rules! user_error { - ($x: expr) => { - return Err(LayoutError::UserError(format!($x))) + ($it: expr) => { + return Err(LayoutError::UserError(format!($it))) }; } @@ -77,18 +77,101 @@ impl<'a> LayoutCalculator for LayoutCx<'a> { } } +// FIXME: move this to the `rustc_abi`. +fn layout_of_simd_ty( + db: &dyn HirDatabase, + id: StructId, + subst: &Substitution, + krate: CrateId, + dl: &TargetDataLayout, +) -> Result, LayoutError> { + let fields = db.field_types(id.into()); + + // Supported SIMD vectors are homogeneous ADTs with at least one field: + // + // * #[repr(simd)] struct S(T, T, T, T); + // * #[repr(simd)] struct S { it: T, y: T, z: T, w: T } + // * #[repr(simd)] struct S([T; 4]) + // + // where T is a primitive scalar (integer/float/pointer). + + let f0_ty = match fields.iter().next() { + Some(it) => it.1.clone().substitute(Interner, subst), + None => { + user_error!("simd type with zero fields"); + } + }; + + // The element type and number of elements of the SIMD vector + // are obtained from: + // + // * the element type and length of the single array field, if + // the first field is of array type, or + // + // * the homogeneous field type and the number of fields. + let (e_ty, e_len, is_array) = if let TyKind::Array(e_ty, _) = f0_ty.kind(Interner) { + // Extract the number of elements from the layout of the array field: + let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), krate)?.fields else { + user_error!("Array with non array layout"); + }; + + (e_ty.clone(), count, true) + } else { + // First ADT field is not an array: + (f0_ty, fields.iter().count() as u64, false) + }; + + // Compute the ABI of the element type: + let e_ly = db.layout_of_ty(e_ty, krate)?; + let Abi::Scalar(e_abi) = e_ly.abi else { + user_error!("simd type with inner non scalar type"); + }; + + // Compute the size and alignment of the vector: + let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow)?; + let align = dl.vector_align(size); + let size = size.align_to(align.abi); + + // Compute the placement of the vector fields: + let fields = if is_array { + FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), memory_index: [0].into() } + } else { + FieldsShape::Array { stride: e_ly.size, count: e_len } + }; + + Ok(Arc::new(Layout { + variants: Variants::Single { index: struct_variant_idx() }, + fields, + abi: Abi::Vector { element: e_abi, count: e_len }, + largest_niche: e_ly.largest_niche, + size, + align, + })) +} + pub fn layout_of_ty_query( db: &dyn HirDatabase, ty: Ty, krate: CrateId, ) -> Result, LayoutError> { - let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; + let Some(target) = db.target_data_layout(krate) else { + return Err(LayoutError::TargetLayoutNotAvailable); + }; let cx = LayoutCx { krate, target: &target }; let dl = &*cx.current_data_layout(); let trait_env = Arc::new(TraitEnvironment::empty(krate)); let ty = normalize(db, trait_env, ty.clone()); let result = match ty.kind(Interner) { - TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate), + TyKind::Adt(AdtId(def), subst) => { + if let hir_def::AdtId::StructId(s) = def { + let data = db.struct_data(*s); + let repr = data.repr.unwrap_or_default(); + if repr.simd() { + return layout_of_simd_ty(db, *s, subst, krate, &target); + } + }; + return db.layout_of_adt(*def, subst.clone(), krate); + } TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( dl, @@ -147,7 +230,7 @@ pub fn layout_of_ty_query( .iter(Interner) .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), krate)) .collect::, _>>()?; - let fields = fields.iter().map(|x| &**x).collect::>(); + let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } @@ -265,14 +348,14 @@ pub fn layout_of_ty_query( let (captures, _) = infer.closure_info(c); let fields = captures .iter() - .map(|x| { + .map(|it| { db.layout_of_ty( - x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), + it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), krate, ) }) .collect::, _>>()?; - let fields = fields.iter().map(|x| &**x).collect::>(); + let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized) .ok_or(LayoutError::Unknown)? @@ -315,7 +398,10 @@ fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { let data = db.struct_data(*i); let mut it = data.variant_data.fields().iter().rev(); match it.next() { - Some((f, _)) => field_ty(db, (*i).into(), f, subst), + Some((f, _)) => { + let last_field_ty = field_ty(db, (*i).into(), f, subst); + struct_tail_erasing_lifetimes(db, last_field_ty) + } None => pointee, } } diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index bd2752a7119a..19d5e98e7389 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -31,7 +31,9 @@ pub fn layout_of_adt_query( subst: Substitution, krate: CrateId, ) -> Result, LayoutError> { - let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; + let Some(target) = db.target_data_layout(krate) else { + return Err(LayoutError::TargetLayoutNotAvailable); + }; let cx = LayoutCx { krate, target: &target }; let dl = cx.current_data_layout(); let handle_variant = |def: VariantId, var: &VariantData| { @@ -70,9 +72,9 @@ pub fn layout_of_adt_query( }; let variants = variants .iter() - .map(|x| x.iter().map(|x| &**x).collect::>()) + .map(|it| it.iter().map(|it| &**it).collect::>()) .collect::>(); - let variants = variants.iter().map(|x| x.iter().collect()).collect(); + let variants = variants.iter().map(|it| it.iter().collect()).collect(); let result = if matches!(def, AdtId::UnionId(..)) { cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)? } else { @@ -103,7 +105,7 @@ pub fn layout_of_adt_query( && variants .iter() .next() - .and_then(|x| x.last().map(|x| x.is_unsized())) + .and_then(|it| it.last().map(|it| !it.is_unsized())) .unwrap_or(true), ) .ok_or(LayoutError::SizeOverflow)? @@ -116,9 +118,9 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, let get = |name| { let attr = attrs.by_key(name).tt_values(); for tree in attr { - if let Some(x) = tree.token_trees.first() { - if let Ok(x) = x.to_string().parse() { - return Bound::Included(x); + if let Some(it) = tree.token_trees.first() { + if let Ok(it) = it.to_string().parse() { + return Bound::Included(it); } } } diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 0ff8c532d47c..a3ced2bac0a2 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -270,6 +270,20 @@ struct Goal(Foo); ); } +#[test] +fn simd_types() { + check_size_and_align( + r#" + #[repr(simd)] + struct SimdType(i64, i64); + struct Goal(SimdType); + "#, + "", + 16, + 16, + ); +} + #[test] fn return_position_impl_trait() { size_and_align_expr! { @@ -343,6 +357,24 @@ fn return_position_impl_trait() { } } +#[test] +fn unsized_ref() { + size_and_align! { + struct S1([u8]); + struct S2(S1); + struct S3(i32, str); + struct S4(u64, S3); + #[allow(dead_code)] + struct S5 { + field1: u8, + field2: i16, + field_last: S4, + } + + struct Goal(&'static S1, &'static S2, &'static S3, &'static S4, &'static S5); + } +} + #[test] fn enums() { size_and_align! { @@ -369,11 +401,11 @@ fn tuple() { } #[test] -fn non_zero() { +fn non_zero_and_non_null() { size_and_align! { - minicore: non_zero, option; - use core::num::NonZeroU8; - struct Goal(Option); + minicore: non_zero, non_null, option; + use core::{num::NonZeroU8, ptr::NonNull}; + struct Goal(Option, Option>); } } @@ -432,3 +464,41 @@ fn enums_with_discriminants() { } } } + +#[test] +fn core_mem_discriminant() { + size_and_align! { + minicore: discriminant; + struct S(i32, u64); + struct Goal(core::mem::Discriminant); + } + size_and_align! { + minicore: discriminant; + #[repr(u32)] + enum S { + A, + B, + C, + } + struct Goal(core::mem::Discriminant); + } + size_and_align! { + minicore: discriminant; + enum S { + A(i32), + B(i64), + C(u8), + } + struct Goal(core::mem::Discriminant); + } + size_and_align! { + minicore: discriminant; + #[repr(C, u16)] + enum S { + A(i32), + B(i64) = 200, + C = 1000, + } + struct Goal(core::mem::Discriminant); + } +} diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 1a4d003bf5e9..b3ca2a222580 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -180,9 +180,16 @@ impl MemoryMap { /// allocator function as `f` and it will return a mapping of old addresses to new addresses. fn transform_addresses( &self, - mut f: impl FnMut(&[u8]) -> Result, + mut f: impl FnMut(&[u8], usize) -> Result, ) -> Result, MirEvalError> { - self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect() + self.memory + .iter() + .map(|x| { + let addr = *x.0; + let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) }; + Ok((addr, f(x.1, align)?)) + }) + .collect() } fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> { diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 9951a1c750bd..2837f400bceb 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -23,7 +23,7 @@ use hir_def::{ generics::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, - lang_item::{lang_attr, LangItem}, + lang_item::LangItem, nameres::MacroSubNs, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, @@ -959,10 +959,10 @@ impl<'a> TyLoweringContext<'a> { } pub(crate) fn lower_where_predicate( - &'a self, - where_predicate: &'a WherePredicate, + &self, + where_predicate: &WherePredicate, ignore_bindings: bool, - ) -> impl Iterator + 'a { + ) -> impl Iterator { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { @@ -1012,7 +1012,7 @@ impl<'a> TyLoweringContext<'a> { // (So ideally, we'd only ignore `~const Drop` here) // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until // the builtin impls are supported by Chalk, we ignore them here. - if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) { + if let Some(lang) = self.db.lang_attr(tr.hir_trait_id().into()) { if matches!(lang, LangItem::Drop | LangItem::Destruct) { return false; } diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index ab6430e8f195..5e1040bc6aac 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -534,7 +534,7 @@ impl ReceiverAdjustments { let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { - match autoderef::autoderef_step(table, ty.clone()) { + match autoderef::autoderef_step(table, ty.clone(), true) { None => { never!("autoderef not possible for {:?}", ty); ty = TyKind::Error.intern(Interner); @@ -559,10 +559,10 @@ impl ReceiverAdjustments { adjust.push(a); } if self.unsize_array { - ty = 'x: { + ty = 'it: { if let TyKind::Ref(m, l, inner) = ty.kind(Interner) { if let TyKind::Array(inner, _) = inner.kind(Interner) { - break 'x TyKind::Ref( + break 'it TyKind::Ref( m.clone(), l.clone(), TyKind::Slice(inner.clone()).intern(Interner), @@ -666,7 +666,7 @@ pub fn is_dyn_method( let self_ty = trait_ref.self_type_parameter(Interner); if let TyKind::Dyn(d) = self_ty.kind(Interner) { let is_my_trait_in_bounds = - d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() { + d.bounds.skip_binders().as_slice(Interner).iter().any(|it| match it.skip_binders() { // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter // what the generics are, we are sure that the method is come from the vtable. WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id, @@ -682,14 +682,14 @@ pub fn is_dyn_method( /// Looks up the impl method that actually runs for the trait method `func`. /// /// Returns `func` if it's not a method defined in a trait or the lookup failed. -pub fn lookup_impl_method( +pub(crate) fn lookup_impl_method_query( db: &dyn HirDatabase, env: Arc, func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution) { let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { - return (func, fn_subst) + return (func, fn_subst); }; let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); let fn_params = fn_subst.len(Interner) - trait_params; @@ -699,8 +699,8 @@ pub fn lookup_impl_method( }; let name = &db.function_data(func).name; - let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) - .and_then(|assoc| { + let Some((impl_fn, impl_subst)) = + lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name).and_then(|assoc| { if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { @@ -731,7 +731,7 @@ fn lookup_impl_assoc_item_for_trait_ref( let impls = db.trait_impls_in_deps(env.krate); let self_impls = match self_ty.kind(Interner) { TyKind::Adt(id, _) => { - id.0.module(db.upcast()).containing_block().map(|x| db.trait_impls_in_block(x)) + id.0.module(db.upcast()).containing_block().map(|it| db.trait_impls_in_block(it)) } _ => None, }; @@ -895,8 +895,8 @@ pub fn iterate_method_candidates_dyn( // (just as rustc does an autoderef and then autoref again). // We have to be careful about the order we're looking at candidates - // in here. Consider the case where we're resolving `x.clone()` - // where `x: &Vec<_>`. This resolves to the clone method with self + // in here. Consider the case where we're resolving `it.clone()` + // where `it: &Vec<_>`. This resolves to the clone method with self // type `Vec<_>`, *not* `&_`. I.e. we need to consider methods where // the receiver type exactly matches before cases where we have to // do autoref. But in the autoderef steps, the `&_` self type comes @@ -1012,8 +1012,8 @@ fn iterate_method_candidates_by_receiver( let snapshot = table.snapshot(); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through - // that. - let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone()); + // that, including raw derefs. + let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( &self_ty, @@ -1028,7 +1028,7 @@ fn iterate_method_candidates_by_receiver( table.rollback_to(snapshot); - let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone()); + let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true); while let Some((self_ty, _)) = autoderef.next() { iterate_trait_method_candidates( &self_ty, @@ -1480,8 +1480,8 @@ fn generic_implements_goal( .push(self_ty.value.clone()) .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) .build(); - kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|x| { - let vk = match x.data(Interner) { + kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|it| { + let vk = match it.data(Interner) { chalk_ir::GenericArgData::Ty(_) => { chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) } @@ -1504,7 +1504,7 @@ fn autoderef_method_receiver( ty: Ty, ) -> Vec<(Canonical, ReceiverAdjustments)> { let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new(table, ty); + let mut autoderef = autoderef::Autoderef::new(table, ty, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty).value, diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 2345bab0bb4d..da5b496e141e 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -3,9 +3,14 @@ use std::{fmt::Display, iter}; use crate::{ - consteval::usize_const, db::HirDatabase, display::HirDisplay, infer::PointerCast, - lang_items::is_box, mapping::ToChalk, CallableDefId, ClosureId, Const, ConstScalar, - InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind, + consteval::usize_const, + db::HirDatabase, + display::HirDisplay, + infer::{normalize, PointerCast}, + lang_items::is_box, + mapping::ToChalk, + CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap, + Substitution, TraitEnvironment, Ty, TyKind, }; use base_db::CrateId; use chalk_ir::Mutability; @@ -22,7 +27,9 @@ mod pretty; mod monomorphization; pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason}; -pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap}; +pub use eval::{ + interpret_mir, pad16, render_const_using_debug_impl, Evaluator, MirEvalError, VTableMap, +}; pub use lower::{ lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError, }; @@ -32,6 +39,7 @@ pub use monomorphization::{ }; use smallvec::{smallvec, SmallVec}; use stdx::{impl_from, never}; +use triomphe::Arc; use super::consteval::{intern_const_scalar, try_const_usize}; @@ -129,11 +137,19 @@ pub enum ProjectionElem { impl ProjectionElem { pub fn projected_ty( &self, - base: Ty, + mut base: Ty, db: &dyn HirDatabase, closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty, krate: CrateId, ) -> Ty { + if matches!(base.data(Interner).kind, TyKind::Alias(_) | TyKind::AssociatedType(..)) { + base = normalize( + db, + // FIXME: we should get this from caller + Arc::new(TraitEnvironment::empty(krate)), + base, + ); + } match self { ProjectionElem::Deref => match &base.data(Interner).kind { TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(), @@ -321,8 +337,8 @@ impl SwitchTargets { #[derive(Debug, PartialEq, Eq, Clone)] pub struct Terminator { - span: MirSpan, - kind: TerminatorKind, + pub span: MirSpan, + pub kind: TerminatorKind, } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index a5dd0182eb65..ad98e8fa1810 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -52,7 +52,7 @@ fn all_mir_bodies( let closures = body.closures.clone(); Box::new( iter::once(Ok(body)) - .chain(closures.into_iter().flat_map(|x| for_closure(db, x))), + .chain(closures.into_iter().flat_map(|it| for_closure(db, it))), ) } Err(e) => Box::new(iter::once(Err(e))), @@ -62,7 +62,7 @@ fn all_mir_bodies( Ok(body) => { let closures = body.closures.clone(); Box::new( - iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))), + iter::once(Ok(body)).chain(closures.into_iter().flat_map(|it| for_closure(db, it))), ) } Err(e) => Box::new(iter::once(Err(e))), @@ -171,7 +171,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec } TerminatorKind::Call { func, args, .. } => { for_operand(func, terminator.span); - args.iter().for_each(|x| for_operand(x, terminator.span)); + args.iter().for_each(|it| for_operand(it, terminator.span)); } TerminatorKind::Assert { cond, .. } => { for_operand(cond, terminator.span); @@ -245,7 +245,7 @@ fn ever_initialized_map( body: &MirBody, ) -> ArenaMap> { let mut result: ArenaMap> = - body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect(); + body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect(); fn dfs( db: &dyn HirDatabase, body: &MirBody, @@ -271,7 +271,10 @@ fn ever_initialized_map( } } let Some(terminator) = &block.terminator else { - never!("Terminator should be none only in construction.\nThe body:\n{}", body.pretty_print(db)); + never!( + "Terminator should be none only in construction.\nThe body:\n{}", + body.pretty_print(db) + ); return; }; let targets = match &terminator.kind { @@ -311,7 +314,7 @@ fn ever_initialized_map( result[body.start_block].insert(l, true); dfs(db, body, body.start_block, l, &mut result); } - for l in body.locals.iter().map(|x| x.0) { + for l in body.locals.iter().map(|it| it.0) { if !result[body.start_block].contains_idx(l) { result[body.start_block].insert(l, false); dfs(db, body, body.start_block, l, &mut result); @@ -325,10 +328,10 @@ fn mutability_of_locals( body: &MirBody, ) -> ArenaMap { let mut result: ArenaMap = - body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect(); + body.locals.iter().map(|it| (it.0, MutabilityReason::Not)).collect(); let mut push_mut_span = |local, span| match &mut result[local] { MutabilityReason::Mut { spans } => spans.push(span), - x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] }, + it @ MutabilityReason::Not => *it = MutabilityReason::Mut { spans: vec![span] }, }; let ever_init_maps = ever_initialized_map(db, body); for (block_id, mut ever_init_map) in ever_init_maps.into_iter() { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 9acf9d39e56c..d7820de629ae 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1,6 +1,6 @@ //! This module provides a MIR interpreter, which is used in const eval. -use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range}; +use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Write, iter, mem, ops::Range}; use base_db::{CrateId, FileId}; use chalk_ir::Mutability; @@ -8,12 +8,13 @@ use either::Either; use hir_def::{ builtin_type::BuiltinType, data::adt::{StructFlags, VariantData}, - lang_item::{lang_attr, LangItem}, + lang_item::LangItem, layout::{TagEncoding, Variants}, - AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, - VariantId, + resolver::{HasResolver, TypeNs, ValueNs}, + AdtId, ConstId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, + StaticId, VariantId, }; -use hir_expand::InFile; +use hir_expand::{mod_path::ModPath, InFile}; use intern::Interned; use la_arena::ArenaMap; use rustc_hash::{FxHashMap, FxHashSet}; @@ -28,7 +29,7 @@ use crate::{ infer::PointerCast, layout::{Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, - method_resolution::{is_dyn_method, lookup_impl_method}, + method_resolution::{is_dyn_method, lookup_impl_const}, name, static_lifetime, traits::FnTrait, utils::{detect_variant_from_bytes, ClosureSubst}, @@ -37,8 +38,8 @@ use crate::{ }; use super::{ - return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand, - Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, + return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, + MirSpan, Operand, Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, }; mod shim; @@ -48,15 +49,15 @@ mod tests; macro_rules! from_bytes { ($ty:tt, $value:expr) => { ($ty::from_le_bytes(match ($value).try_into() { - Ok(x) => x, + Ok(it) => it, Err(_) => return Err(MirEvalError::TypeError(stringify!(mismatched size in constructing $ty))), })) }; } macro_rules! not_supported { - ($x: expr) => { - return Err(MirEvalError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirEvalError::NotSupported(format!($it))) }; } @@ -68,8 +69,8 @@ pub struct VTableMap { impl VTableMap { fn id(&mut self, ty: Ty) -> usize { - if let Some(x) = self.ty_to_id.get(&ty) { - return *x; + if let Some(it) = self.ty_to_id.get(&ty) { + return *it; } let id = self.id_to_ty.len(); self.id_to_ty.push(ty.clone()); @@ -114,11 +115,20 @@ impl TlsData { } } +struct StackFrame { + body: Arc, + locals: Locals, + destination: Option, + prev_stack_ptr: usize, + span: (MirSpan, DefWithBodyId), +} + pub struct Evaluator<'a> { db: &'a dyn HirDatabase, trait_env: Arc, stack: Vec, heap: Vec, + code_stack: Vec, /// Stores the global location of the statics. We const evaluate every static first time we need it /// and see it's missing, then we add it to this to reuse. static_locations: FxHashMap, @@ -127,8 +137,10 @@ pub struct Evaluator<'a> { /// time of use. vtable_map: VTableMap, thread_local_storage: TlsData, + random_state: oorandom::Rand64, stdout: Vec, stderr: Vec, + layout_cache: RefCell>>, crate_id: CrateId, // FIXME: This is a workaround, see the comment on `interpret_mir` assert_placeholder_ty_is_unused: bool, @@ -136,6 +148,8 @@ pub struct Evaluator<'a> { execution_limit: usize, /// An additional limit on stack depth, to prevent stack overflow stack_depth_limit: usize, + /// Maximum count of bytes that heap and stack can grow + memory_limit: usize, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -192,7 +206,7 @@ impl IntervalAndTy { addr: Address, ty: Ty, evaluator: &Evaluator<'_>, - locals: &Locals<'_>, + locals: &Locals, ) -> Result { let size = evaluator.size_of_sized(&ty, locals, "type of interval")?; Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) @@ -226,18 +240,28 @@ impl IntervalOrOwned { } } +#[cfg(target_pointer_width = "64")] +const STACK_OFFSET: usize = 1 << 60; +#[cfg(target_pointer_width = "64")] +const HEAP_OFFSET: usize = 1 << 59; + +#[cfg(target_pointer_width = "32")] +const STACK_OFFSET: usize = 1 << 30; +#[cfg(target_pointer_width = "32")] +const HEAP_OFFSET: usize = 1 << 29; + impl Address { - fn from_bytes(x: &[u8]) -> Result { - Ok(Address::from_usize(from_bytes!(usize, x))) + fn from_bytes(it: &[u8]) -> Result { + Ok(Address::from_usize(from_bytes!(usize, it))) } - fn from_usize(x: usize) -> Self { - if x > usize::MAX / 2 { - Stack(x - usize::MAX / 2) - } else if x > usize::MAX / 4 { - Heap(x - usize::MAX / 4) + fn from_usize(it: usize) -> Self { + if it > STACK_OFFSET { + Stack(it - STACK_OFFSET) + } else if it > HEAP_OFFSET { + Heap(it - HEAP_OFFSET) } else { - Invalid(x) + Invalid(it) } } @@ -247,23 +271,23 @@ impl Address { fn to_usize(&self) -> usize { let as_num = match self { - Stack(x) => *x + usize::MAX / 2, - Heap(x) => *x + usize::MAX / 4, - Invalid(x) => *x, + Stack(it) => *it + STACK_OFFSET, + Heap(it) => *it + HEAP_OFFSET, + Invalid(it) => *it, }; as_num } fn map(&self, f: impl FnOnce(usize) -> usize) -> Address { match self { - Stack(x) => Stack(f(*x)), - Heap(x) => Heap(f(*x)), - Invalid(x) => Invalid(f(*x)), + Stack(it) => Stack(f(*it)), + Heap(it) => Heap(f(*it)), + Invalid(it) => Invalid(f(*it)), } } fn offset(&self, offset: usize) -> Address { - self.map(|x| x + offset) + self.map(|it| it + offset) } } @@ -282,7 +306,7 @@ pub enum MirEvalError { TypeIsUnsized(Ty, &'static str), NotSupported(String), InvalidConst(Const), - InFunction(Either, Box, MirSpan, DefWithBodyId), + InFunction(Box, Vec<(Either, MirSpan, DefWithBodyId)>), ExecutionLimitExceeded, StackOverflow, TargetDataLayoutNotAvailable, @@ -300,40 +324,42 @@ impl MirEvalError { ) -> std::result::Result<(), std::fmt::Error> { writeln!(f, "Mir eval error:")?; let mut err = self; - while let MirEvalError::InFunction(func, e, span, def) = err { + while let MirEvalError::InFunction(e, stack) = err { err = e; - match func { - Either::Left(func) => { - let function_name = db.function_data(*func); - writeln!( - f, - "In function {} ({:?})", - function_name.name.display(db.upcast()), - func - )?; - } - Either::Right(clos) => { - writeln!(f, "In {:?}", clos)?; + for (func, span, def) in stack.iter().take(30).rev() { + match func { + Either::Left(func) => { + let function_name = db.function_data(*func); + writeln!( + f, + "In function {} ({:?})", + function_name.name.display(db.upcast()), + func + )?; + } + Either::Right(clos) => { + writeln!(f, "In {:?}", clos)?; + } } + let source_map = db.body_with_source_map(*def).1; + let span: InFile = match span { + MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { + Ok(s) => s.map(|it| it.into()), + Err(_) => continue, + }, + MirSpan::PatId(p) => match source_map.pat_syntax(*p) { + Ok(s) => s.map(|it| match it { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + MirSpan::Unknown => continue, + }; + let file_id = span.file_id.original_file(db.upcast()); + let text_range = span.value.text_range(); + writeln!(f, "{}", span_formatter(file_id, text_range))?; } - let source_map = db.body_with_source_map(*def).1; - let span: InFile = match span { - MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|x| x.into()), - Err(_) => continue, - }, - MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|x| match x { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), - Err(_) => continue, - }, - MirSpan::Unknown => continue, - }; - let file_id = span.file_id.original_file(db.upcast()); - let text_range = span.value.text_range(); - writeln!(f, "{}", span_formatter(file_id, text_range))?; } match err { MirEvalError::InFunction(..) => unreachable!(), @@ -413,13 +439,7 @@ impl std::fmt::Debug for MirEvalError { let data = &arg0.data(Interner); f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish() } - Self::InFunction(func, e, span, _) => { - let mut e = &**e; - let mut stack = vec![(*func, *span)]; - while let Self::InFunction(f, next_e, span, _) = e { - e = &next_e; - stack.push((*f, *span)); - } + Self::InFunction(e, stack) => { f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() } } @@ -435,10 +455,10 @@ struct DropFlags { impl DropFlags { fn add_place(&mut self, p: Place) { - if p.iterate_over_parents().any(|x| self.need_drop.contains(&x)) { + if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|x| !p.is_parent(x)); + self.need_drop.retain(|it| !p.is_parent(it)); self.need_drop.insert(p); } @@ -449,15 +469,15 @@ impl DropFlags { } #[derive(Debug)] -struct Locals<'a> { - ptr: &'a ArenaMap, - body: &'a MirBody, +struct Locals { + ptr: ArenaMap, + body: Arc, drop_flags: DropFlags, } pub fn interpret_mir( db: &dyn HirDatabase, - body: &MirBody, + body: Arc, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has @@ -466,19 +486,22 @@ pub fn interpret_mir( assert_placeholder_ty_is_unused: bool, ) -> (Result, String, String) { let ty = body.locals[return_slot()].ty.clone(); - let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); - let x: Result = (|| { - let bytes = evaluator.interpret_mir(&body, None.into_iter())?; + let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused); + let it: Result = (|| { + if evaluator.ptr_size() != std::mem::size_of::() { + not_supported!("targets with different pointer size from host"); + } + let bytes = evaluator.interpret_mir(body.clone(), None.into_iter())?; let mut memory_map = evaluator.create_memory_map( &bytes, &ty, - &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }, + &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, )?; memory_map.vtable = evaluator.vtable_map.clone(); return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); })(); ( - x, + it, String::from_utf8_lossy(&evaluator.stdout).into_owned(), String::from_utf8_lossy(&evaluator.stderr).into_owned(), ) @@ -487,18 +510,20 @@ pub fn interpret_mir( impl Evaluator<'_> { pub fn new<'a>( db: &'a dyn HirDatabase, - body: &MirBody, + owner: DefWithBodyId, assert_placeholder_ty_is_unused: bool, ) -> Evaluator<'a> { - let crate_id = body.owner.module(db.upcast()).krate(); - let trait_env = db.trait_environment_for_body(body.owner); + let crate_id = owner.module(db.upcast()).krate(); + let trait_env = db.trait_environment_for_body(owner); Evaluator { stack: vec![0], heap: vec![0], + code_stack: vec![], vtable_map: VTableMap::default(), thread_local_storage: TlsData::default(), static_locations: HashMap::default(), db, + random_state: oorandom::Rand64::new(0), trait_env, crate_id, stdout: vec![], @@ -506,14 +531,16 @@ impl Evaluator<'_> { assert_placeholder_ty_is_unused, stack_depth_limit: 100, execution_limit: 1000_000, + memory_limit: 1000_000_000, // 2GB, 1GB for stack and 1GB for heap + layout_cache: RefCell::new(HashMap::default()), } } - fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result
{ + fn place_addr(&self, p: &Place, locals: &Locals) -> Result
{ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) } - fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result { + fn place_interval(&self, p: &Place, locals: &Locals) -> Result { let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; Ok(Interval { addr: place_addr_and_ty.0, @@ -527,7 +554,7 @@ impl Evaluator<'_> { fn ptr_size(&self) -> usize { match self.db.target_data_layout(self.crate_id) { - Some(x) => x.pointer_size.bytes_usize(), + Some(it) => it.pointer_size.bytes_usize(), None => 8, } } @@ -535,7 +562,7 @@ impl Evaluator<'_> { fn place_addr_and_ty_and_metadata<'a>( &'a self, p: &Place, - locals: &'a Locals<'a>, + locals: &'a Locals, ) -> Result<(Address, Ty, Option)> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = locals.body.locals[p.local].ty.clone(); @@ -569,8 +596,8 @@ impl Evaluator<'_> { } else { None }; - let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?); - addr = Address::from_usize(x); + let it = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?); + addr = Address::from_usize(it); } ProjectionElem::Index(op) => { let offset = from_bytes!( @@ -586,13 +613,13 @@ impl Evaluator<'_> { let offset = if from_end { let len = match prev_ty.kind(Interner) { TyKind::Array(_, c) => match try_const_usize(self.db, c) { - Some(x) => x as u64, + Some(it) => it as u64, None => { not_supported!("indexing array with unknown const from end") } }, TyKind::Slice(_) => match metadata { - Some(x) => from_bytes!(u64, x.get(self)?), + Some(it) => from_bytes!(u64, it.get(self)?), None => not_supported!("slice place without metadata"), }, _ => not_supported!("bad type for const index"), @@ -612,8 +639,8 @@ impl Evaluator<'_> { _ => TyKind::Error.intern(Interner), }; metadata = match metadata { - Some(x) => { - let prev_len = from_bytes!(u64, x.get(self)?); + Some(it) => { + let prev_len = from_bytes!(u64, it.get(self)?); Some(IntervalOrOwned::Owned( (prev_len - from - to).to_le_bytes().to_vec(), )) @@ -636,8 +663,8 @@ impl Evaluator<'_> { Variants::Single { .. } => &layout, Variants::Multiple { variants, .. } => { &variants[match f.parent { - hir_def::VariantId::EnumVariantId(x) => { - RustcEnumVariantIdx(x.local_id) + hir_def::VariantId::EnumVariantId(it) => { + RustcEnumVariantIdx(it.local_id) } _ => { return Err(MirEvalError::TypeError( @@ -662,9 +689,15 @@ impl Evaluator<'_> { } fn layout(&self, ty: &Ty) -> Result> { - self.db + if let Some(x) = self.layout_cache.borrow().get(ty) { + return Ok(x.clone()); + } + let r = self + .db .layout_of_ty(ty.clone(), self.crate_id) - .map_err(|e| MirEvalError::LayoutError(e, ty.clone())) + .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))?; + self.layout_cache.borrow_mut().insert(ty.clone(), r.clone()); + Ok(r) } fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { @@ -673,11 +706,11 @@ impl Evaluator<'_> { }) } - fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result { + fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result { + fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result { Ok(match o { Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?, Operand::Constant(c) => c.data(Interner).ty.clone(), @@ -688,11 +721,7 @@ impl Evaluator<'_> { }) } - fn operand_ty_and_eval( - &mut self, - o: &Operand, - locals: &mut Locals<'_>, - ) -> Result { + fn operand_ty_and_eval(&mut self, o: &Operand, locals: &mut Locals) -> Result { Ok(IntervalAndTy { interval: self.eval_operand(o, locals)?, ty: self.operand_ty(o, locals)?, @@ -701,39 +730,172 @@ impl Evaluator<'_> { fn interpret_mir( &mut self, - body: &MirBody, - args: impl Iterator>, + body: Arc, + args: impl Iterator, ) -> Result> { - if let Some(x) = self.stack_depth_limit.checked_sub(1) { - self.stack_depth_limit = x; + if let Some(it) = self.stack_depth_limit.checked_sub(1) { + self.stack_depth_limit = it; } else { return Err(MirEvalError::StackOverflow); } let mut current_block_idx = body.start_block; - let mut locals = - Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }; - let (locals_ptr, stack_size) = { - let mut stack_ptr = self.stack.len(); - let addr = body - .locals - .iter() - .map(|(id, x)| { - let size = - self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?; - let my_ptr = stack_ptr; - stack_ptr += size; - Ok((id, Interval { addr: Stack(my_ptr), size })) - }) - .collect::>>()?; - let stack_size = stack_ptr - self.stack.len(); - (addr, stack_size) - }; - locals.ptr = &locals_ptr; - self.stack.extend(iter::repeat(0).take(stack_size)); + let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body.clone(), None)?; + self.fill_locals_for_body(&body, &mut locals, args)?; + let prev_code_stack = mem::take(&mut self.code_stack); + let span = (MirSpan::Unknown, body.owner); + self.code_stack.push(StackFrame { body, locals, destination: None, prev_stack_ptr, span }); + 'stack: loop { + let Some(mut my_stack_frame) = self.code_stack.pop() else { + not_supported!("missing stack frame"); + }; + let e = (|| { + let mut locals = &mut my_stack_frame.locals; + let body = &*my_stack_frame.body; + loop { + let current_block = &body.basic_blocks[current_block_idx]; + if let Some(it) = self.execution_limit.checked_sub(1) { + self.execution_limit = it; + } else { + return Err(MirEvalError::ExecutionLimitExceeded); + } + for statement in ¤t_block.statements { + match &statement.kind { + StatementKind::Assign(l, r) => { + let addr = self.place_addr(l, &locals)?; + let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; + self.write_memory(addr, &result)?; + locals.drop_flags.add_place(l.clone()); + } + StatementKind::Deinit(_) => not_supported!("de-init statement"), + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + let Some(terminator) = current_block.terminator.as_ref() else { + not_supported!("block without terminator"); + }; + match &terminator.kind { + TerminatorKind::Goto { target } => { + current_block_idx = *target; + } + TerminatorKind::Call { + func, + args, + destination, + target, + cleanup: _, + from_hir_call: _, + } => { + let destination_interval = self.place_interval(destination, &locals)?; + let fn_ty = self.operand_ty(func, &locals)?; + let args = args + .iter() + .map(|it| self.operand_ty_and_eval(it, &mut locals)) + .collect::>>()?; + let stack_frame = match &fn_ty.data(Interner).kind { + TyKind::Function(_) => { + let bytes = self.eval_operand(func, &mut locals)?; + self.exec_fn_pointer( + bytes, + destination_interval, + &args, + &locals, + *target, + terminator.span, + )? + } + TyKind::FnDef(def, generic_args) => self.exec_fn_def( + *def, + generic_args, + destination_interval, + &args, + &locals, + *target, + terminator.span, + )?, + it => not_supported!("unknown function type {it:?}"), + }; + locals.drop_flags.add_place(destination.clone()); + if let Some(stack_frame) = stack_frame { + self.code_stack.push(my_stack_frame); + current_block_idx = stack_frame.body.start_block; + self.code_stack.push(stack_frame); + return Ok(None); + } else { + current_block_idx = + target.ok_or(MirEvalError::UndefinedBehavior( + "Diverging function returned".to_owned(), + ))?; + } + } + TerminatorKind::SwitchInt { discr, targets } => { + let val = u128::from_le_bytes(pad16( + self.eval_operand(discr, &mut locals)?.get(&self)?, + false, + )); + current_block_idx = targets.target_for_value(val); + } + TerminatorKind::Return => { + break; + } + TerminatorKind::Unreachable => { + return Err(MirEvalError::UndefinedBehavior( + "unreachable executed".to_owned(), + )); + } + TerminatorKind::Drop { place, target, unwind: _ } => { + self.drop_place(place, &mut locals, terminator.span)?; + current_block_idx = *target; + } + _ => not_supported!("unknown terminator"), + } + } + Ok(Some(my_stack_frame)) + })(); + let my_stack_frame = match e { + Ok(None) => continue 'stack, + Ok(Some(x)) => x, + Err(e) => { + let my_code_stack = mem::replace(&mut self.code_stack, prev_code_stack); + let mut error_stack = vec![]; + for frame in my_code_stack.into_iter().rev() { + if let DefWithBodyId::FunctionId(f) = frame.body.owner { + error_stack.push((Either::Left(f), frame.span.0, frame.span.1)); + } + } + return Err(MirEvalError::InFunction(Box::new(e), error_stack)); + } + }; + match my_stack_frame.destination { + None => { + self.code_stack = prev_code_stack; + self.stack_depth_limit += 1; + return Ok(my_stack_frame.locals.ptr[return_slot()].get(self)?.to_vec()); + } + Some(bb) => { + // We don't support const promotion, so we can't truncate the stack yet. + let _ = my_stack_frame.prev_stack_ptr; + // self.stack.truncate(my_stack_frame.prev_stack_ptr); + current_block_idx = bb; + } + } + } + } + + fn fill_locals_for_body( + &mut self, + body: &MirBody, + locals: &mut Locals, + args: impl Iterator, + ) -> Result<()> { let mut remain_args = body.param_locals.len(); - for ((l, interval), value) in locals_ptr.iter().skip(1).zip(args) { + for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { locals.drop_flags.add_place(l.into()); - interval.write_from_bytes(self, &value)?; + match value { + IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, + IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, + } if remain_args == 0 { return Err(MirEvalError::TypeError("more arguments provided")); } @@ -742,101 +904,58 @@ impl Evaluator<'_> { if remain_args > 0 { return Err(MirEvalError::TypeError("not enough arguments provided")); } - loop { - let current_block = &body.basic_blocks[current_block_idx]; - if let Some(x) = self.execution_limit.checked_sub(1) { - self.execution_limit = x; - } else { - return Err(MirEvalError::ExecutionLimitExceeded); - } - for statement in ¤t_block.statements { - match &statement.kind { - StatementKind::Assign(l, r) => { - let addr = self.place_addr(l, &locals)?; - let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; - self.write_memory(addr, &result)?; - locals.drop_flags.add_place(l.clone()); - } - StatementKind::Deinit(_) => not_supported!("de-init statement"), - StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Nop => (), - } - } - let Some(terminator) = current_block.terminator.as_ref() else { - not_supported!("block without terminator"); - }; - match &terminator.kind { - TerminatorKind::Goto { target } => { - current_block_idx = *target; - } - TerminatorKind::Call { - func, - args, - destination, - target, - cleanup: _, - from_hir_call: _, - } => { - let destination_interval = self.place_interval(destination, &locals)?; - let fn_ty = self.operand_ty(func, &locals)?; - let args = args - .iter() - .map(|x| self.operand_ty_and_eval(x, &mut locals)) - .collect::>>()?; - match &fn_ty.data(Interner).kind { - TyKind::Function(_) => { - let bytes = self.eval_operand(func, &mut locals)?; - self.exec_fn_pointer( - bytes, - destination_interval, - &args, - &locals, - terminator.span, - )?; - } - TyKind::FnDef(def, generic_args) => { - self.exec_fn_def( - *def, - generic_args, - destination_interval, - &args, - &locals, - terminator.span, - )?; - } - x => not_supported!("unknown function type {x:?}"), - } - locals.drop_flags.add_place(destination.clone()); - current_block_idx = target.expect("broken mir, function without target"); - } - TerminatorKind::SwitchInt { discr, targets } => { - let val = u128::from_le_bytes(pad16( - self.eval_operand(discr, &mut locals)?.get(&self)?, - false, - )); - current_block_idx = targets.target_for_value(val); - } - TerminatorKind::Return => { - self.stack_depth_limit += 1; - return Ok(locals.ptr[return_slot()].get(self)?.to_vec()); - } - TerminatorKind::Unreachable => { - return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned())); - } - TerminatorKind::Drop { place, target, unwind: _ } => { - self.drop_place(place, &mut locals, terminator.span)?; - current_block_idx = *target; - } - _ => not_supported!("unknown terminator"), - } - } + Ok(()) } - fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result { + fn create_locals_for_body( + &mut self, + body: Arc, + destination: Option, + ) -> Result<(Locals, usize)> { + let mut locals = + Locals { ptr: ArenaMap::new(), body: body.clone(), drop_flags: DropFlags::default() }; + let (locals_ptr, stack_size) = { + let mut stack_ptr = self.stack.len(); + let addr = body + .locals + .iter() + .map(|(id, it)| { + if id == return_slot() { + if let Some(destination) = destination { + return Ok((id, destination)); + } + } + let (size, align) = self.size_align_of_sized( + &it.ty, + &locals, + "no unsized local in extending stack", + )?; + while stack_ptr % align != 0 { + stack_ptr += 1; + } + let my_ptr = stack_ptr; + stack_ptr += size; + Ok((id, Interval { addr: Stack(my_ptr), size })) + }) + .collect::>>()?; + let stack_size = stack_ptr - self.stack.len(); + (addr, stack_size) + }; + locals.ptr = locals_ptr; + let prev_stack_pointer = self.stack.len(); + if stack_size > self.memory_limit { + return Err(MirEvalError::Panic(format!( + "Stack overflow. Tried to grow stack to {stack_size} bytes" + ))); + } + self.stack.extend(iter::repeat(0).take(stack_size)); + Ok((locals, prev_stack_pointer)) + } + + fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result { use IntervalOrOwned::*; Ok(match r { - Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?), + Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), Rvalue::Ref(_, p) => { let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?; let mut r = addr.to_bytes(); @@ -881,9 +1000,9 @@ impl Evaluator<'_> { c[0] = 1 - c[0]; } else { match op { - UnOp::Not => c.iter_mut().for_each(|x| *x = !*x), + UnOp::Not => c.iter_mut().for_each(|it| *it = !*it), UnOp::Neg => { - c.iter_mut().for_each(|x| *x = !*x); + c.iter_mut().for_each(|it| *it = !*it); for k in c.iter_mut() { let o; (*k, o) = k.overflowing_add(1); @@ -948,8 +1067,8 @@ impl Evaluator<'_> { }; Owned(r.to_le_bytes().into()) } - x => not_supported!( - "invalid binop {x:?} on floating point operators" + it => not_supported!( + "invalid binop {it:?} on floating point operators" ), } } @@ -976,8 +1095,8 @@ impl Evaluator<'_> { }; Owned(r.to_le_bytes().into()) } - x => not_supported!( - "invalid binop {x:?} on floating point operators" + it => not_supported!( + "invalid binop {it:?} on floating point operators" ), } } @@ -1034,13 +1153,18 @@ impl Evaluator<'_> { BinOp::Shr => l128.checked_shr(shift_amount), _ => unreachable!(), }; + if shift_amount as usize >= lc.len() * 8 { + return Err(MirEvalError::Panic(format!( + "Overflow in {op:?}" + ))); + } if let Some(r) = r { break 'b r; } }; return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); }; - check_overflow(r)? + Owned(r.to_le_bytes()[..lc.len()].to_vec()) } BinOp::Offset => not_supported!("offset binop"), } @@ -1049,64 +1173,15 @@ impl Evaluator<'_> { Rvalue::Discriminant(p) => { let ty = self.place_ty(p, locals)?; let bytes = self.eval_place(p, locals)?.get(&self)?; - let layout = self.layout(&ty)?; - let enum_id = 'b: { - match ty.kind(Interner) { - TyKind::Adt(e, _) => match e.0 { - AdtId::EnumId(e) => break 'b e, - _ => (), - }, - _ => (), - } - return Ok(Owned(0u128.to_le_bytes().to_vec())); - }; - match &layout.variants { - Variants::Single { index } => { - let r = self.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id: index.0, - })?; - Owned(r.to_le_bytes().to_vec()) - } - Variants::Multiple { tag, tag_encoding, variants, .. } => { - let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else { - not_supported!("missing target data layout"); - }; - let size = tag.size(&*target_data_layout).bytes_usize(); - let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field - match tag_encoding { - TagEncoding::Direct => { - let tag = &bytes[offset..offset + size]; - Owned(pad16(tag, false).to_vec()) - } - TagEncoding::Niche { untagged_variant, niche_start, .. } => { - let tag = &bytes[offset..offset + size]; - let candidate_tag = i128::from_le_bytes(pad16(tag, false)) - .wrapping_sub(*niche_start as i128) - as usize; - let variant = variants - .iter_enumerated() - .map(|(x, _)| x) - .filter(|x| x != untagged_variant) - .nth(candidate_tag) - .unwrap_or(*untagged_variant) - .0; - let result = self.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id: variant, - })?; - Owned(result.to_le_bytes().to_vec()) - } - } - } - } + let result = self.compute_discriminant(ty, bytes)?; + Owned(result.to_le_bytes().to_vec()) } - Rvalue::Repeat(x, len) => { + Rvalue::Repeat(it, len) => { let len = match try_const_usize(self.db, &len) { - Some(x) => x as usize, + Some(it) => it as usize, None => not_supported!("non evaluatable array len in repeat Rvalue"), }; - let val = self.eval_operand(x, locals)?.get(self)?; + let val = self.eval_operand(it, locals)?.get(self)?; let size = len * val.len(); Owned(val.iter().copied().cycle().take(size).collect()) } @@ -1115,20 +1190,20 @@ impl Evaluator<'_> { let Some((size, align)) = self.size_align_of(ty, locals)? else { not_supported!("unsized box initialization"); }; - let addr = self.heap_allocate(size, align); + let addr = self.heap_allocate(size, align)?; Owned(addr.to_bytes()) } Rvalue::CopyForDeref(_) => not_supported!("copy for deref"), Rvalue::Aggregate(kind, values) => { let values = values .iter() - .map(|x| self.eval_operand(x, locals)) + .map(|it| self.eval_operand(it, locals)) .collect::>>()?; match kind { AggregateKind::Array(_) => { let mut r = vec![]; - for x in values { - let value = x.get(&self)?; + for it in values { + let value = it.get(&self)?; r.extend(value); } Owned(r) @@ -1139,11 +1214,12 @@ impl Evaluator<'_> { layout.size.bytes_usize(), &layout, None, - values.iter().map(|&x| x.into()), + values.iter().map(|&it| it.into()), )?) } - AggregateKind::Union(x, f) => { - let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?; + AggregateKind::Union(it, f) => { + let layout = + self.layout_adt((*it).into(), Substitution::empty(Interner))?; let offset = layout .fields .offset(u32::from(f.local_id.into_raw()) as usize) @@ -1153,14 +1229,14 @@ impl Evaluator<'_> { result[offset..offset + op.len()].copy_from_slice(op); Owned(result) } - AggregateKind::Adt(x, subst) => { + AggregateKind::Adt(it, subst) => { let (size, variant_layout, tag) = - self.layout_of_variant(*x, subst.clone(), locals)?; + self.layout_of_variant(*it, subst.clone(), locals)?; Owned(self.make_by_layout( size, &variant_layout, tag, - values.iter().map(|&x| x.into()), + values.iter().map(|&it| it.into()), )?) } AggregateKind::Closure(ty) => { @@ -1169,7 +1245,7 @@ impl Evaluator<'_> { layout.size.bytes_usize(), &layout, None, - values.iter().map(|&x| x.into()), + values.iter().map(|&it| it.into()), )?) } } @@ -1229,21 +1305,75 @@ impl Evaluator<'_> { }) } + fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result { + let layout = self.layout(&ty)?; + let enum_id = 'b: { + match ty.kind(Interner) { + TyKind::Adt(e, _) => match e.0 { + AdtId::EnumId(e) => break 'b e, + _ => (), + }, + _ => (), + } + return Ok(0); + }; + match &layout.variants { + Variants::Single { index } => { + let r = self.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id: index.0, + })?; + Ok(r) + } + Variants::Multiple { tag, tag_encoding, variants, .. } => { + let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else { + not_supported!("missing target data layout"); + }; + let size = tag.size(&*target_data_layout).bytes_usize(); + let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field + match tag_encoding { + TagEncoding::Direct => { + let tag = &bytes[offset..offset + size]; + Ok(i128::from_le_bytes(pad16(tag, false))) + } + TagEncoding::Niche { untagged_variant, niche_start, .. } => { + let tag = &bytes[offset..offset + size]; + let candidate_tag = i128::from_le_bytes(pad16(tag, false)) + .wrapping_sub(*niche_start as i128) + as usize; + let variant = variants + .iter_enumerated() + .map(|(it, _)| it) + .filter(|it| it != untagged_variant) + .nth(candidate_tag) + .unwrap_or(*untagged_variant) + .0; + let result = self.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id: variant, + })?; + Ok(result) + } + } + } + } + } + fn coerce_unsized_look_through_fields( &self, ty: &Ty, goal: impl Fn(&TyKind) -> Option, ) -> Result { let kind = ty.kind(Interner); - if let Some(x) = goal(kind) { - return Ok(x); + if let Some(it) = goal(kind) { + return Ok(it); } if let TyKind::Adt(id, subst) = kind { if let AdtId::StructId(struct_id) = id.0 { let field_types = self.db.field_types(struct_id.into()); let mut field_types = field_types.iter(); if let Some(ty) = - field_types.next().map(|x| x.1.clone().substitute(Interner, subst)) + field_types.next().map(|it| it.1.clone().substitute(Interner, subst)) { return self.coerce_unsized_look_through_fields(&ty, goal); } @@ -1258,66 +1388,99 @@ impl Evaluator<'_> { current_ty: &Ty, target_ty: &Ty, ) -> Result { - use IntervalOrOwned::*; - fn for_ptr(x: &TyKind) -> Option { - match x { + fn for_ptr(it: &TyKind) -> Option { + match it { TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()), _ => None, } } - Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? { - ty => match &ty.data(Interner).kind { - TyKind::Slice(_) => { - match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? { - ty => match &ty.data(Interner).kind { - TyKind::Array(_, size) => { - let len = match try_const_usize(self.db, size) { - None => not_supported!( - "unevaluatble len of array in coerce unsized" - ), - Some(x) => x as usize, - }; - let mut r = Vec::with_capacity(16); - let addr = addr.get(self)?; - r.extend(addr.iter().copied()); - r.extend(len.to_le_bytes().into_iter()); - Owned(r) - } - t => { - not_supported!("slice unsizing from non array type {t:?}") - } - }, - } + let target_ty = self.coerce_unsized_look_through_fields(target_ty, for_ptr)?; + let current_ty = self.coerce_unsized_look_through_fields(current_ty, for_ptr)?; + + self.unsizing_ptr_from_addr(target_ty, current_ty, addr) + } + + /// Adds metadata to the address and create the fat pointer result of the unsizing operation. + fn unsizing_ptr_from_addr( + &mut self, + target_ty: Ty, + current_ty: Ty, + addr: Interval, + ) -> Result { + use IntervalOrOwned::*; + Ok(match &target_ty.data(Interner).kind { + TyKind::Slice(_) => match ¤t_ty.data(Interner).kind { + TyKind::Array(_, size) => { + let len = match try_const_usize(self.db, size) { + None => { + not_supported!("unevaluatble len of array in coerce unsized") + } + Some(it) => it as usize, + }; + let mut r = Vec::with_capacity(16); + let addr = addr.get(self)?; + r.extend(addr.iter().copied()); + r.extend(len.to_le_bytes().into_iter()); + Owned(r) + } + t => { + not_supported!("slice unsizing from non array type {t:?}") } - TyKind::Dyn(_) => match ¤t_ty.data(Interner).kind { - TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { - let vtable = self.vtable_map.id(ty.clone()); - let mut r = Vec::with_capacity(16); - let addr = addr.get(self)?; - r.extend(addr.iter().copied()); - r.extend(vtable.to_le_bytes().into_iter()); - Owned(r) - } - _ => not_supported!("dyn unsizing from non pointers"), - }, - _ => not_supported!("unknown unsized cast"), }, + TyKind::Dyn(_) => { + let vtable = self.vtable_map.id(current_ty.clone()); + let mut r = Vec::with_capacity(16); + let addr = addr.get(self)?; + r.extend(addr.iter().copied()); + r.extend(vtable.to_le_bytes().into_iter()); + Owned(r) + } + TyKind::Adt(id, target_subst) => match ¤t_ty.data(Interner).kind { + TyKind::Adt(current_id, current_subst) => { + if id != current_id { + not_supported!("unsizing struct with different type"); + } + let id = match id.0 { + AdtId::StructId(s) => s, + AdtId::UnionId(_) => not_supported!("unsizing unions"), + AdtId::EnumId(_) => not_supported!("unsizing enums"), + }; + let Some((last_field, _)) = + self.db.struct_data(id).variant_data.fields().iter().rev().next() + else { + not_supported!("unsizing struct without field"); + }; + let target_last_field = self.db.field_types(id.into())[last_field] + .clone() + .substitute(Interner, target_subst); + let current_last_field = self.db.field_types(id.into())[last_field] + .clone() + .substitute(Interner, current_subst); + return self.unsizing_ptr_from_addr( + target_last_field, + current_last_field, + addr, + ); + } + _ => not_supported!("unsizing struct with non adt type"), + }, + _ => not_supported!("unknown unsized cast"), }) } fn layout_of_variant( &mut self, - x: VariantId, + it: VariantId, subst: Substitution, - locals: &Locals<'_>, + locals: &Locals, ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { - let adt = x.adt_id(); + let adt = it.adt_id(); if let DefWithBodyId::VariantId(f) = locals.body.owner { - if let VariantId::EnumVariantId(x) = x { + if let VariantId::EnumVariantId(it) = it { if AdtId::from(f.parent) == adt { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy layout - let i = self.const_eval_discriminant(x)?; + let i = self.const_eval_discriminant(it)?; return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); } } @@ -1330,8 +1493,8 @@ impl Evaluator<'_> { .db .target_data_layout(self.crate_id) .ok_or(MirEvalError::TargetDataLayoutNotAvailable)?; - let enum_variant_id = match x { - VariantId::EnumVariantId(x) => x, + let enum_variant_id = match it { + VariantId::EnumVariantId(it) => it, _ => not_supported!("multi variant layout for non-enums"), }; let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id); @@ -1345,8 +1508,8 @@ impl Evaluator<'_> { } else { discriminant = (variants .iter_enumerated() - .filter(|(x, _)| x != untagged_variant) - .position(|(x, _)| x == rustc_enum_variant_idx) + .filter(|(it, _)| it != untagged_variant) + .position(|(it, _)| it == rustc_enum_variant_idx) .unwrap() as i128) .wrapping_add(*niche_start as i128); true @@ -1389,8 +1552,8 @@ impl Evaluator<'_> { Ok(result) } - fn eval_operand(&mut self, x: &Operand, locals: &mut Locals<'_>) -> Result { - Ok(match x { + fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result { + Ok(match it { Operand::Copy(p) | Operand::Move(p) => { locals.drop_flags.remove_place(p); self.eval_place(p, locals)? @@ -1399,61 +1562,63 @@ impl Evaluator<'_> { let addr = self.eval_static(*st, locals)?; Interval::new(addr, self.ptr_size()) } - Operand::Constant(konst) => { - let data = &konst.data(Interner); - match &data.value { - chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"), - chalk_ir::ConstValue::InferenceVar(_) => { - not_supported!("inference var constant") - } - chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"), - chalk_ir::ConstValue::Concrete(c) => { - self.allocate_const_in_heap(c, &data.ty, locals, konst)? - } - } - } + Operand::Constant(konst) => self.allocate_const_in_heap(locals, konst)?, }) } - fn allocate_const_in_heap( - &mut self, - c: &chalk_ir::ConcreteConst, - ty: &Ty, - locals: &Locals<'_>, - konst: &chalk_ir::Const, - ) -> Result { - Ok(match &c.interned { - ConstScalar::Bytes(v, memory_map) => { - let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); - let patch_map = memory_map.transform_addresses(|b| { - let addr = self.heap_allocate(b.len(), 1); // FIXME: align is wrong - self.write_memory(addr, b)?; - Ok(addr.to_usize()) + fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result { + let ty = &konst.data(Interner).ty; + let chalk_ir::ConstValue::Concrete(c) = &konst.data(Interner).value else { + not_supported!("evaluating non concrete constant"); + }; + let result_owner; + let (v, memory_map) = match &c.interned { + ConstScalar::Bytes(v, mm) => (v, mm), + ConstScalar::UnevaluatedConst(const_id, subst) => 'b: { + let mut const_id = *const_id; + let mut subst = subst.clone(); + if let hir_def::GeneralConstId::ConstId(c) = const_id { + let (c, s) = lookup_impl_const(self.db, self.trait_env.clone(), c, subst); + const_id = hir_def::GeneralConstId::ConstId(c); + subst = s; + } + result_owner = self.db.const_eval(const_id.into(), subst).map_err(|e| { + let name = const_id.name(self.db.upcast()); + MirEvalError::ConstEvalError(name, Box::new(e)) })?; - let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1)); - if size != v.len() { - // Handle self enum - if size == 16 && v.len() < 16 { - v = Cow::Owned(pad16(&v, false).to_vec()); - } else if size < 16 && v.len() == 16 { - v = Cow::Owned(v[0..size].to_vec()); - } else { - return Err(MirEvalError::InvalidConst(konst.clone())); + if let chalk_ir::ConstValue::Concrete(c) = &result_owner.data(Interner).value { + if let ConstScalar::Bytes(v, mm) = &c.interned { + break 'b (v, mm); } } - let addr = self.heap_allocate(size, align); - self.write_memory(addr, &v)?; - self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?; - Interval::new(addr, size) - } - ConstScalar::UnevaluatedConst(..) => { - not_supported!("unevaluated const present in monomorphized mir"); + not_supported!("unevaluatable constant"); } ConstScalar::Unknown => not_supported!("evaluating unknown const"), - }) + }; + let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); + let patch_map = memory_map.transform_addresses(|b, align| { + let addr = self.heap_allocate(b.len(), align)?; + self.write_memory(addr, b)?; + Ok(addr.to_usize()) + })?; + let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1)); + if size != v.len() { + // Handle self enum + if size == 16 && v.len() < 16 { + v = Cow::Owned(pad16(&v, false).to_vec()); + } else if size < 16 && v.len() == 16 { + v = Cow::Owned(v[0..size].to_vec()); + } else { + return Err(MirEvalError::InvalidConst(konst.clone())); + } + } + let addr = self.heap_allocate(size, align)?; + self.write_memory(addr, &v)?; + self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?; + Ok(Interval::new(addr, size)) } - fn eval_place(&mut self, p: &Place, locals: &Locals<'_>) -> Result { + fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result { let addr = self.place_addr(p, locals)?; Ok(Interval::new( addr, @@ -1466,11 +1631,11 @@ impl Evaluator<'_> { return Ok(&[]); } let (mem, pos) = match addr { - Stack(x) => (&self.stack, x), - Heap(x) => (&self.heap, x), - Invalid(x) => { + Stack(it) => (&self.stack, it), + Heap(it) => (&self.heap, it), + Invalid(it) => { return Err(MirEvalError::UndefinedBehavior(format!( - "read invalid memory address {x} with size {size}" + "read invalid memory address {it} with size {size}" ))); } }; @@ -1478,28 +1643,30 @@ impl Evaluator<'_> { .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string())) } + fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<&mut [u8]> { + let (mem, pos) = match addr { + Stack(it) => (&mut self.stack, it), + Heap(it) => (&mut self.heap, it), + Invalid(it) => { + return Err(MirEvalError::UndefinedBehavior(format!( + "write invalid memory address {it} with size {size}" + ))); + } + }; + Ok(mem.get_mut(pos..pos + size).ok_or_else(|| { + MirEvalError::UndefinedBehavior("out of bound memory write".to_string()) + })?) + } + fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> { if r.is_empty() { return Ok(()); } - let (mem, pos) = match addr { - Stack(x) => (&mut self.stack, x), - Heap(x) => (&mut self.heap, x), - Invalid(x) => { - return Err(MirEvalError::UndefinedBehavior(format!( - "write invalid memory address {x} with content {r:?}" - ))); - } - }; - mem.get_mut(pos..pos + r.len()) - .ok_or_else(|| { - MirEvalError::UndefinedBehavior("out of bound memory write".to_string()) - })? - .copy_from_slice(r); + self.write_memory_using_ref(addr, r.len())?.copy_from_slice(r); Ok(()) } - fn size_align_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result> { + fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result> { if let DefWithBodyId::VariantId(f) = locals.body.owner { if let Some((adt, _)) = ty.as_adt() { if AdtId::from(f.parent) == adt { @@ -1523,17 +1690,40 @@ impl Evaluator<'_> { /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` - fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result { + fn size_of_sized(&self, ty: &Ty, locals: &Locals, what: &'static str) -> Result { match self.size_align_of(ty, locals)? { - Some(x) => Ok(x.0), + Some(it) => Ok(it.0), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), } } - fn heap_allocate(&mut self, size: usize, _align: usize) -> Address { + /// A version of `self.size_align_of` which returns error if the type is unsized. `what` argument should + /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` + fn size_align_of_sized( + &self, + ty: &Ty, + locals: &Locals, + what: &'static str, + ) -> Result<(usize, usize)> { + match self.size_align_of(ty, locals)? { + Some(it) => Ok(it), + None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), + } + } + + fn heap_allocate(&mut self, size: usize, align: usize) -> Result
{ + if !align.is_power_of_two() || align > 10000 { + return Err(MirEvalError::UndefinedBehavior(format!("Alignment {align} is invalid"))); + } + while self.heap.len() % align != 0 { + self.heap.push(0); + } + if size.checked_add(self.heap.len()).map_or(true, |x| x > self.memory_limit) { + return Err(MirEvalError::Panic(format!("Memory allocation of {size} bytes failed"))); + } let pos = self.heap.len(); self.heap.extend(iter::repeat(0).take(size)); - Address::Heap(pos) + Ok(Address::Heap(pos)) } fn detect_fn_trait(&self, def: FunctionId) -> Option { @@ -1541,7 +1731,7 @@ impl Evaluator<'_> { let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else { return None; }; - let l = lang_attr(self.db.upcast(), parent)?; + let l = self.db.lang_attr(parent.into())?; match l { FnOnce => Some(FnTrait::FnOnce), FnMut => Some(FnTrait::FnMut), @@ -1550,12 +1740,12 @@ impl Evaluator<'_> { } } - fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result { + fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals) -> Result { fn rec( this: &Evaluator<'_>, bytes: &[u8], ty: &Ty, - locals: &Locals<'_>, + locals: &Locals, mm: &mut MemoryMap, ) -> Result<()> { match ty.kind(Interner) { @@ -1661,7 +1851,7 @@ impl Evaluator<'_> { old_vtable: &VTableMap, addr: Address, ty: &Ty, - locals: &Locals<'_>, + locals: &Locals, ) -> Result<()> { // FIXME: support indirect references let layout = self.layout(ty)?; @@ -1672,14 +1862,14 @@ impl Evaluator<'_> { match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); - if let Some(x) = patch_map.get(¤t) { - self.write_memory(addr, &x.to_le_bytes())?; + if let Some(it) = patch_map.get(¤t) { + self.write_memory(addr, &it.to_le_bytes())?; } } None => { let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); - if let Some(x) = patch_map.get(¤t) { - self.write_memory(addr, &x.to_le_bytes())?; + if let Some(it) = patch_map.get(¤t) { + self.write_memory(addr, &it.to_le_bytes())?; } } } @@ -1735,21 +1925,21 @@ impl Evaluator<'_> { bytes: Interval, destination: Interval, args: &[IntervalAndTy], - locals: &Locals<'_>, + locals: &Locals, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?.clone(); match &next_ty.data(Interner).kind { TyKind::FnDef(def, generic_args) => { - self.exec_fn_def(*def, generic_args, destination, args, &locals, span)?; + self.exec_fn_def(*def, generic_args, destination, args, &locals, target_bb, span) } TyKind::Closure(id, subst) => { - self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)?; + self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span) } - _ => return Err(MirEvalError::TypeError("function pointer to non function")), + _ => Err(MirEvalError::TypeError("function pointer to non function")), } - Ok(()) } fn exec_closure( @@ -1759,9 +1949,9 @@ impl Evaluator<'_> { generic_args: &Substitution, destination: Interval, args: &[IntervalAndTy], - locals: &Locals<'_>, + locals: &Locals, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let mir_body = self .db .monomorphized_mir_body_for_closure( @@ -1769,7 +1959,7 @@ impl Evaluator<'_> { generic_args.clone(), self.trait_env.clone(), ) - .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?; + .map_err(|it| MirEvalError::MirLowerErrorForClosure(closure, it))?; let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some() { closure_data.addr.to_bytes() @@ -1777,12 +1967,18 @@ impl Evaluator<'_> { closure_data.get(self)?.to_owned() }; let arg_bytes = iter::once(Ok(closure_data)) - .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned()))) + .chain(args.iter().map(|it| Ok(it.get(&self)?.to_owned()))) .collect::>>()?; - let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| { - MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner) - })?; - destination.write_from_bytes(self, &bytes) + let bytes = self + .interpret_mir(mir_body, arg_bytes.into_iter().map(IntervalOrOwned::Owned)) + .map_err(|e| { + MirEvalError::InFunction( + Box::new(e), + vec![(Either::Right(closure), span, locals.body.owner)], + ) + })?; + destination.write_from_bytes(self, &bytes)?; + Ok(None) } fn exec_fn_def( @@ -1791,18 +1987,34 @@ impl Evaluator<'_> { generic_args: &Substitution, destination: Interval, args: &[IntervalAndTy], - locals: &Locals<'_>, + locals: &Locals, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let def: CallableDefId = from_chalk(self.db, def); let generic_args = generic_args.clone(); match def { CallableDefId::FunctionId(def) => { if let Some(_) = self.detect_fn_trait(def) { - self.exec_fn_trait(&args, destination, locals, span)?; - return Ok(()); + return self.exec_fn_trait( + def, + args, + generic_args, + locals, + destination, + target_bb, + span, + ); } - self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?; + self.exec_fn_with_args( + def, + args, + generic_args, + locals, + destination, + target_bb, + span, + ) } CallableDefId::StructId(id) => { let (size, variant_layout, tag) = @@ -1811,9 +2023,10 @@ impl Evaluator<'_> { size, &variant_layout, tag, - args.iter().map(|x| x.interval.into()), + args.iter().map(|it| it.interval.into()), )?; destination.write_from_bytes(self, &result)?; + Ok(None) } CallableDefId::EnumVariantId(id) => { let (size, variant_layout, tag) = @@ -1822,12 +2035,12 @@ impl Evaluator<'_> { size, &variant_layout, tag, - args.iter().map(|x| x.interval.into()), + args.iter().map(|it| it.interval.into()), )?; destination.write_from_bytes(self, &result)?; + Ok(None) } } - Ok(()) } fn exec_fn_with_args( @@ -1835,10 +2048,11 @@ impl Evaluator<'_> { def: FunctionId, args: &[IntervalAndTy], generic_args: Substitution, - locals: &Locals<'_>, + locals: &Locals, destination: Interval, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { if self.detect_and_exec_special_function( def, args, @@ -1847,10 +2061,9 @@ impl Evaluator<'_> { destination, span, )? { - return Ok(()); + return Ok(None); } - let arg_bytes = - args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; + let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)); if let Some(self_ty_idx) = is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) { @@ -1858,74 +2071,103 @@ impl Evaluator<'_> { // `&T`, `&mut T`, `Box`, `Rc`, `Arc`, and `Pin

` where `P` is one of possible recievers, // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on // the type. + let first_arg = arg_bytes.clone().next().unwrap(); + let first_arg = first_arg.get(self)?; let ty = - self.vtable_map.ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?; + self.vtable_map.ty_of_bytes(&first_arg[self.ptr_size()..self.ptr_size() * 2])?; let mut args_for_target = args.to_vec(); args_for_target[0] = IntervalAndTy { interval: args_for_target[0].interval.slice(0..self.ptr_size()), ty: ty.clone(), }; let ty = GenericArgData::Ty(ty.clone()).intern(Interner); - let generics_for_target = - Substitution::from_iter( - Interner, - generic_args.iter(Interner).enumerate().map(|(i, x)| { - if i == self_ty_idx { - &ty - } else { - x - } - }), - ); + let generics_for_target = Substitution::from_iter( + Interner, + generic_args.iter(Interner).enumerate().map(|(i, it)| { + if i == self_ty_idx { + &ty + } else { + it + } + }), + ); return self.exec_fn_with_args( def, &args_for_target, generics_for_target, locals, destination, + target_bb, span, ); } let (imp, generic_args) = - lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args); - self.exec_looked_up_function(generic_args, locals, imp, arg_bytes, span, destination) + self.db.lookup_impl_method(self.trait_env.clone(), def, generic_args); + self.exec_looked_up_function( + generic_args, + locals, + imp, + arg_bytes, + span, + destination, + target_bb, + ) } fn exec_looked_up_function( &mut self, generic_args: Substitution, - locals: &Locals<'_>, + locals: &Locals, imp: FunctionId, - arg_bytes: Vec>, + arg_bytes: impl Iterator, span: MirSpan, destination: Interval, - ) -> Result<()> { + target_bb: Option, + ) -> Result> { let def = imp.into(); let mir_body = self .db .monomorphized_mir_body(def, generic_args, self.trait_env.clone()) .map_err(|e| { MirEvalError::InFunction( - Either::Left(imp), Box::new(MirEvalError::MirLowerError(imp, e)), - span, - locals.body.owner, + vec![(Either::Left(imp), span, locals.body.owner)], ) })?; - let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| { - MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) - })?; - destination.write_from_bytes(self, &result)?; - Ok(()) + Ok(if let Some(target_bb) = target_bb { + let (mut locals, prev_stack_ptr) = + self.create_locals_for_body(mir_body.clone(), Some(destination))?; + self.fill_locals_for_body(&mir_body, &mut locals, arg_bytes.into_iter())?; + let span = (span, locals.body.owner); + Some(StackFrame { + body: mir_body, + locals, + destination: Some(target_bb), + prev_stack_ptr, + span, + }) + } else { + let result = self.interpret_mir(mir_body, arg_bytes).map_err(|e| { + MirEvalError::InFunction( + Box::new(e), + vec![(Either::Left(imp), span, locals.body.owner)], + ) + })?; + destination.write_from_bytes(self, &result)?; + None + }) } fn exec_fn_trait( &mut self, + def: FunctionId, args: &[IntervalAndTy], + generic_args: Substitution, + locals: &Locals, destination: Interval, - locals: &Locals<'_>, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; let mut func_ty = func.ty.clone(); let mut func_data = func.interval; @@ -1942,13 +2184,28 @@ impl Evaluator<'_> { } match &func_ty.data(Interner).kind { TyKind::FnDef(def, subst) => { - self.exec_fn_def(*def, subst, destination, &args[1..], locals, span)?; + return self.exec_fn_def( + *def, + subst, + destination, + &args[1..], + locals, + target_bb, + span, + ); } TyKind::Function(_) => { - self.exec_fn_pointer(func_data, destination, &args[1..], locals, span)?; + return self.exec_fn_pointer( + func_data, + destination, + &args[1..], + locals, + target_bb, + span, + ); } TyKind::Closure(closure, subst) => { - self.exec_closure( + return self.exec_closure( *closure, func_data, &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()), @@ -1956,14 +2213,45 @@ impl Evaluator<'_> { &args[1..], locals, span, - )?; + ); + } + _ => { + // try to execute the manual impl of `FnTrait` for structs (nightly feature used in std) + let arg0 = func; + let args = &args[1..]; + let arg1 = { + let ty = TyKind::Tuple( + args.len(), + Substitution::from_iter(Interner, args.iter().map(|it| it.ty.clone())), + ) + .intern(Interner); + let layout = self.layout(&ty)?; + let result = self.make_by_layout( + layout.size.bytes_usize(), + &layout, + None, + args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)), + )?; + // FIXME: there is some leak here + let size = layout.size.bytes_usize(); + let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize)?; + self.write_memory(addr, &result)?; + IntervalAndTy { interval: Interval { addr, size }, ty } + }; + return self.exec_fn_with_args( + def, + &[arg0.clone(), arg1], + generic_args, + locals, + destination, + target_bb, + span, + ); } - x => not_supported!("Call FnTrait methods with type {x:?}"), } - Ok(()) } - fn eval_static(&mut self, st: StaticId, locals: &Locals<'_>) -> Result

{ + fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result
{ if let Some(o) = self.static_locations.get(&st) { return Ok(*o); }; @@ -1975,21 +2263,16 @@ impl Evaluator<'_> { Box::new(e), ) })?; - let data = &konst.data(Interner); - if let chalk_ir::ConstValue::Concrete(c) = &data.value { - self.allocate_const_in_heap(&c, &data.ty, locals, &konst)? - } else { - not_supported!("unevaluatable static"); - } + self.allocate_const_in_heap(locals, &konst)? } else { let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr]; let Some((size, align)) = self.size_align_of(&ty, locals)? else { not_supported!("unsized extern static"); }; - let addr = self.heap_allocate(size, align); + let addr = self.heap_allocate(size, align)?; Interval::new(addr, size) }; - let addr = self.heap_allocate(self.ptr_size(), self.ptr_size()); + let addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?; self.write_memory(addr, &result.addr.to_bytes())?; self.static_locations.insert(st, addr); Ok(addr) @@ -2011,13 +2294,13 @@ impl Evaluator<'_> { } } - fn drop_place(&mut self, place: &Place, locals: &mut Locals<'_>, span: MirSpan) -> Result<()> { + fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; if !locals.drop_flags.remove_place(place) { return Ok(()); } let metadata = match metadata { - Some(x) => x.get(self)?.to_vec(), + Some(it) => it.get(self)?.to_vec(), None => vec![], }; self.run_drop_glue_deep(ty, locals, addr, &metadata, span) @@ -2026,7 +2309,7 @@ impl Evaluator<'_> { fn run_drop_glue_deep( &mut self, ty: Ty, - locals: &Locals<'_>, + locals: &Locals, addr: Address, _metadata: &[u8], span: MirSpan, @@ -2039,8 +2322,7 @@ impl Evaluator<'_> { // we can ignore drop in them. return Ok(()); }; - let (impl_drop_candidate, subst) = lookup_impl_method( - self.db, + let (impl_drop_candidate, subst) = self.db.lookup_impl_method( self.trait_env.clone(), drop_fn, Substitution::from1(Interner, ty.clone()), @@ -2050,9 +2332,10 @@ impl Evaluator<'_> { subst, locals, impl_drop_candidate, - vec![addr.to_bytes()], + [IntervalOrOwned::Owned(addr.to_bytes())].into_iter(), span, Interval { addr: Address::Invalid(0), size: 0 }, + None, )?; } match ty.kind(Interner) { @@ -2121,10 +2404,77 @@ impl Evaluator<'_> { } } -pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] { - let is_negative = is_signed && x.last().unwrap_or(&0) > &128; +pub fn render_const_using_debug_impl( + db: &dyn HirDatabase, + owner: ConstId, + c: &Const, +) -> Result { + let mut evaluator = Evaluator::new(db, owner.into(), false); + let locals = &Locals { + ptr: ArenaMap::new(), + body: db + .mir_body(owner.into()) + .map_err(|_| MirEvalError::NotSupported("unreachable".to_string()))?, + drop_flags: DropFlags::default(), + }; + let data = evaluator.allocate_const_in_heap(locals, c)?; + let resolver = owner.resolver(db.upcast()); + let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully( + db.upcast(), + &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![core], name![fmt], name![Debug]].into_iter(), + )), + ) else { + not_supported!("core::fmt::Debug not found"); + }; + let Some(debug_fmt_fn) = db.trait_data(debug_trait).method_by_name(&name![fmt]) else { + not_supported!("core::fmt::Debug::fmt not found"); + }; + // a1 = &[""] + let a1 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?; + // a2 = &[::core::fmt::ArgumentV1::new(&(THE_CONST), ::core::fmt::Debug::fmt)] + // FIXME: we should call the said function, but since its name is going to break in the next rustc version + // and its ABI doesn't break yet, we put it in memory manually. + let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?; + evaluator.write_memory(a2, &data.addr.to_bytes())?; + let debug_fmt_fn_ptr = evaluator.vtable_map.id(TyKind::FnDef( + db.intern_callable_def(debug_fmt_fn.into()).into(), + Substitution::from1(Interner, c.data(Interner).ty.clone()), + ) + .intern(Interner)); + evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?; + // a3 = ::core::fmt::Arguments::new_v1(a1, a2) + // FIXME: similarly, we should call function here, not directly working with memory. + let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?; + evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a1.to_bytes())?; + evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?; + evaluator.write_memory(a3.offset(4 * evaluator.ptr_size()), &a2.to_bytes())?; + evaluator.write_memory(a3.offset(5 * evaluator.ptr_size()), &[1])?; + let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully( + db.upcast(), + &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![std], name![fmt], name![format]].into_iter(), + )), + ) else { + not_supported!("std::fmt::format not found"); + }; + let message_string = evaluator.interpret_mir( + db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, + [IntervalOrOwned::Borrowed(Interval { addr: a3, size: evaluator.ptr_size() * 6 })] + .into_iter(), + )?; + let addr = + Address::from_bytes(&message_string[evaluator.ptr_size()..2 * evaluator.ptr_size()])?; + let size = from_bytes!(usize, message_string[2 * evaluator.ptr_size()..]); + Ok(std::string::String::from_utf8_lossy(evaluator.read_memory(addr, size)?).into_owned()) +} + +pub fn pad16(it: &[u8], is_signed: bool) -> [u8; 16] { + let is_negative = is_signed && it.last().unwrap_or(&0) > &127; let fill_with = if is_negative { 255 } else { 0 }; - x.iter() + it.iter() .copied() .chain(iter::repeat(fill_with)) .take(16) diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 3b9ef03c369f..9ad6087cad9e 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -3,20 +3,26 @@ use std::cmp; +use chalk_ir::TyKind; +use hir_def::resolver::HasResolver; +use hir_expand::mod_path::ModPath; + use super::*; +mod simd; + macro_rules! from_bytes { ($ty:tt, $value:expr) => { ($ty::from_le_bytes(match ($value).try_into() { - Ok(x) => x, + Ok(it) => it, Err(_) => return Err(MirEvalError::TypeError("mismatched size")), })) }; } macro_rules! not_supported { - ($x: expr) => { - return Err(MirEvalError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirEvalError::NotSupported(format!($it))) }; } @@ -26,7 +32,7 @@ impl Evaluator<'_> { def: FunctionId, args: &[IntervalAndTy], generic_args: &Substitution, - locals: &Locals<'_>, + locals: &Locals, destination: Interval, span: MirSpan, ) -> Result { @@ -53,6 +59,28 @@ impl Evaluator<'_> { )?; return Ok(true); } + let is_platform_intrinsic = match &function_data.abi { + Some(abi) => *abi == Interned::new_str("platform-intrinsic"), + None => match def.lookup(self.db.upcast()).container { + hir_def::ItemContainerId::ExternBlockId(block) => { + let id = block.lookup(self.db.upcast()).id; + id.item_tree(self.db.upcast())[id.value].abi.as_deref() + == Some("platform-intrinsic") + } + _ => false, + }, + }; + if is_platform_intrinsic { + self.exec_platform_intrinsic( + function_data.name.as_text().unwrap_or_default().as_str(), + args, + generic_args, + destination, + &locals, + span, + )?; + return Ok(true); + } let is_extern_c = match def.lookup(self.db.upcast()).container { hir_def::ItemContainerId::ExternBlockId(block) => { let id = block.lookup(self.db.upcast()).id; @@ -74,25 +102,25 @@ impl Evaluator<'_> { let alloc_fn = function_data .attrs .iter() - .filter_map(|x| x.path().as_ident()) - .filter_map(|x| x.as_str()) - .find(|x| { + .filter_map(|it| it.path().as_ident()) + .filter_map(|it| it.as_str()) + .find(|it| { [ "rustc_allocator", "rustc_deallocator", "rustc_reallocator", "rustc_allocator_zeroed", ] - .contains(x) + .contains(it) }); if let Some(alloc_fn) = alloc_fn { self.exec_alloc_fn(alloc_fn, args, destination)?; return Ok(true); } - if let Some(x) = self.detect_lang_function(def) { + if let Some(it) = self.detect_lang_function(def) { let arg_bytes = - args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; - let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?; + args.iter().map(|it| Ok(it.get(&self)?.to_owned())).collect::>>()?; + let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?; destination.write_from_bytes(self, &result)?; return Ok(true); } @@ -112,7 +140,7 @@ impl Evaluator<'_> { }; let size = from_bytes!(usize, size.get(self)?); let align = from_bytes!(usize, align.get(self)?); - let result = self.heap_allocate(size, align); + let result = self.heap_allocate(size, align)?; destination.write_from_bytes(self, &result.to_bytes())?; } "rustc_deallocator" => { /* no-op for now */ } @@ -120,14 +148,18 @@ impl Evaluator<'_> { let [ptr, old_size, align, new_size] = args else { return Err(MirEvalError::TypeError("rustc_allocator args are not provided")); }; - let ptr = Address::from_bytes(ptr.get(self)?)?; let old_size = from_bytes!(usize, old_size.get(self)?); let new_size = from_bytes!(usize, new_size.get(self)?); - let align = from_bytes!(usize, align.get(self)?); - let result = self.heap_allocate(new_size, align); - Interval { addr: result, size: old_size } - .write_from_interval(self, Interval { addr: ptr, size: old_size })?; - destination.write_from_bytes(self, &result.to_bytes())?; + if old_size >= new_size { + destination.write_from_interval(self, ptr.interval)?; + } else { + let ptr = Address::from_bytes(ptr.get(self)?)?; + let align = from_bytes!(usize, align.get(self)?); + let result = self.heap_allocate(new_size, align)?; + Interval { addr: result, size: old_size } + .write_from_interval(self, Interval { addr: ptr, size: old_size })?; + destination.write_from_bytes(self, &result.to_bytes())?; + } } _ => not_supported!("unknown alloc function"), } @@ -136,7 +168,7 @@ impl Evaluator<'_> { fn detect_lang_function(&self, def: FunctionId) -> Option { use LangItem::*; - let candidate = lang_attr(self.db.upcast(), def)?; + let candidate = self.db.lang_attr(def.into())?; // We want to execute these functions with special logic if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); @@ -146,56 +178,35 @@ impl Evaluator<'_> { fn exec_lang_item( &mut self, - x: LangItem, + it: LangItem, generic_args: &Substitution, args: &[Vec], - locals: &Locals<'_>, + locals: &Locals, span: MirSpan, ) -> Result> { use LangItem::*; let mut args = args.iter(); - match x { + match it { BeginPanic => Err(MirEvalError::Panic("".to_string())), PanicFmt => { let message = (|| { - let arguments_struct = - self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?; - let arguments_layout = self - .layout_adt(arguments_struct.into(), Substitution::empty(Interner)) - .ok()?; - let arguments_field_pieces = - self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?; - let pieces_offset = arguments_layout - .fields - .offset(u32::from(arguments_field_pieces.into_raw()) as usize) - .bytes_usize(); - let ptr_size = self.ptr_size(); - let arg = args.next()?; - let pieces_array_addr = - Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?; - let pieces_array_len = usize::from_le_bytes( - (&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size]) - .try_into() - .ok()?, - ); - let mut message = "".to_string(); - for i in 0..pieces_array_len { - let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size); - let piece_addr = - Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?) - .ok()?; - let piece_len = usize::from_le_bytes( - self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size) - .ok()? - .try_into() - .ok()?, - ); - let piece_data = self.read_memory(piece_addr, piece_len).ok()?; - message += &std::string::String::from_utf8_lossy(piece_data); - } - Some(message) + let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast()); + let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( + self.db.upcast(), + &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![std], name![fmt], name![format]].into_iter(), + )), + ) else { + not_supported!("std::fmt::format not found"); + }; + let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") }; + let message_string = self.interpret_mir(self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())))?; + let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; + let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); + Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned()) })() - .unwrap_or_else(|| "".to_string()); + .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); Err(MirEvalError::Panic(message)) } SliceLen => { @@ -207,7 +218,7 @@ impl Evaluator<'_> { } DropInPlace => { let ty = - generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or( + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)).ok_or( MirEvalError::TypeError( "generic argument of drop_in_place is not provided", ), @@ -224,7 +235,35 @@ impl Evaluator<'_> { )?; Ok(vec![]) } - x => not_supported!("Executing lang item {x:?}"), + it => not_supported!("Executing lang item {it:?}"), + } + } + + fn exec_syscall( + &mut self, + id: i64, + args: &[IntervalAndTy], + destination: Interval, + _locals: &Locals, + _span: MirSpan, + ) -> Result<()> { + match id { + 318 => { + // SYS_getrandom + let [buf, len, _flags] = args else { + return Err(MirEvalError::TypeError("SYS_getrandom args are not provided")); + }; + let addr = Address::from_bytes(buf.get(self)?)?; + let size = from_bytes!(usize, len.get(self)?); + for i in 0..size { + let rand_byte = self.random_state.rand_u64() as u8; + self.write_memory(addr.offset(i), &[rand_byte])?; + } + destination.write_from_interval(self, len.interval) + } + _ => { + not_supported!("Unknown syscall id {id:?}") + } } } @@ -234,8 +273,8 @@ impl Evaluator<'_> { args: &[IntervalAndTy], _generic_args: &Substitution, destination: Interval, - locals: &Locals<'_>, - _span: MirSpan, + locals: &Locals, + span: MirSpan, ) -> Result<()> { match as_str { "memcmp" => { @@ -299,7 +338,9 @@ impl Evaluator<'_> { } "pthread_getspecific" => { let Some(arg0) = args.get(0) else { - return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided")); + return Err(MirEvalError::TypeError( + "pthread_getspecific arg0 is not provided", + )); }; let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); let value = self.thread_local_storage.get_key(key)?; @@ -308,11 +349,15 @@ impl Evaluator<'_> { } "pthread_setspecific" => { let Some(arg0) = args.get(0) else { - return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided")); + return Err(MirEvalError::TypeError( + "pthread_setspecific arg0 is not provided", + )); }; let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); let Some(arg1) = args.get(1) else { - return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided")); + return Err(MirEvalError::TypeError( + "pthread_setspecific arg1 is not provided", + )); }; let value = from_bytes!(u128, pad16(arg1.get(self)?, false)); self.thread_local_storage.set_key(key, value)?; @@ -326,17 +371,52 @@ impl Evaluator<'_> { destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?; Ok(()) } + "syscall" => { + let Some((id, rest)) = args.split_first() else { + return Err(MirEvalError::TypeError( + "syscall arg1 is not provided", + )); + }; + let id = from_bytes!(i64, id.get(self)?); + self.exec_syscall(id, rest, destination, locals, span) + } + "sched_getaffinity" => { + let [_pid, _set_size, set] = args else { + return Err(MirEvalError::TypeError("libc::write args are not provided")); + }; + let set = Address::from_bytes(set.get(self)?)?; + // Only enable core 0 (we are single threaded anyway), which is bitset 0x0000001 + self.write_memory(set, &[1])?; + // return 0 as success + self.write_memory_using_ref(destination.addr, destination.size)?.fill(0); + Ok(()) + } _ => not_supported!("unknown external function {as_str}"), } } + fn exec_platform_intrinsic( + &mut self, + name: &str, + args: &[IntervalAndTy], + generic_args: &Substitution, + destination: Interval, + locals: &Locals, + span: MirSpan, + ) -> Result<()> { + if let Some(name) = name.strip_prefix("simd_") { + return self.exec_simd_intrinsic(name, args, generic_args, destination, locals, span); + } + not_supported!("unknown platform intrinsic {name}"); + } + fn exec_intrinsic( &mut self, name: &str, args: &[IntervalAndTy], generic_args: &Substitution, destination: Interval, - locals: &Locals<'_>, + locals: &Locals, span: MirSpan, ) -> Result<()> { if let Some(name) = name.strip_prefix("atomic_") { @@ -347,7 +427,9 @@ impl Evaluator<'_> { "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { let [arg] = args else { - return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64) -> f64")); + return Err(MirEvalError::TypeError( + "f64 intrinsic signature doesn't match fn (f64) -> f64", + )); }; let arg = from_bytes!(f64, arg.get(self)?); match name { @@ -373,7 +455,9 @@ impl Evaluator<'_> { } "pow" | "minnum" | "maxnum" | "copysign" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64, f64) -> f64")); + return Err(MirEvalError::TypeError( + "f64 intrinsic signature doesn't match fn (f64, f64) -> f64", + )); }; let arg1 = from_bytes!(f64, arg1.get(self)?); let arg2 = from_bytes!(f64, arg2.get(self)?); @@ -387,7 +471,9 @@ impl Evaluator<'_> { } "powi" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("powif64 signature doesn't match fn (f64, i32) -> f64")); + return Err(MirEvalError::TypeError( + "powif64 signature doesn't match fn (f64, i32) -> f64", + )); }; let arg1 = from_bytes!(f64, arg1.get(self)?); let arg2 = from_bytes!(i32, arg2.get(self)?); @@ -395,7 +481,9 @@ impl Evaluator<'_> { } "fma" => { let [arg1, arg2, arg3] = args else { - return Err(MirEvalError::TypeError("fmaf64 signature doesn't match fn (f64, f64, f64) -> f64")); + return Err(MirEvalError::TypeError( + "fmaf64 signature doesn't match fn (f64, f64, f64) -> f64", + )); }; let arg1 = from_bytes!(f64, arg1.get(self)?); let arg2 = from_bytes!(f64, arg2.get(self)?); @@ -411,7 +499,9 @@ impl Evaluator<'_> { "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { let [arg] = args else { - return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32) -> f32")); + return Err(MirEvalError::TypeError( + "f32 intrinsic signature doesn't match fn (f32) -> f32", + )); }; let arg = from_bytes!(f32, arg.get(self)?); match name { @@ -437,7 +527,9 @@ impl Evaluator<'_> { } "pow" | "minnum" | "maxnum" | "copysign" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32, f32) -> f32")); + return Err(MirEvalError::TypeError( + "f32 intrinsic signature doesn't match fn (f32, f32) -> f32", + )); }; let arg1 = from_bytes!(f32, arg1.get(self)?); let arg2 = from_bytes!(f32, arg2.get(self)?); @@ -451,7 +543,9 @@ impl Evaluator<'_> { } "powi" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("powif32 signature doesn't match fn (f32, i32) -> f32")); + return Err(MirEvalError::TypeError( + "powif32 signature doesn't match fn (f32, i32) -> f32", + )); }; let arg1 = from_bytes!(f32, arg1.get(self)?); let arg2 = from_bytes!(i32, arg2.get(self)?); @@ -459,7 +553,9 @@ impl Evaluator<'_> { } "fma" => { let [arg1, arg2, arg3] = args else { - return Err(MirEvalError::TypeError("fmaf32 signature doesn't match fn (f32, f32, f32) -> f32")); + return Err(MirEvalError::TypeError( + "fmaf32 signature doesn't match fn (f32, f32, f32) -> f32", + )); }; let arg1 = from_bytes!(f32, arg1.get(self)?); let arg2 = from_bytes!(f32, arg2.get(self)?); @@ -472,21 +568,74 @@ impl Evaluator<'_> { } match name { "size_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; let size = self.size_of_sized(ty, locals, "size_of arg")?; destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) } "min_align_of" | "pref_align_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("align_of generic arg is not provided")); }; let align = self.layout(ty)?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } + "size_of_val" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError("size_of_val generic arg is not provided")); + }; + let [arg] = args else { + return Err(MirEvalError::TypeError("size_of_val args are not provided")); + }; + if let Some((size, _)) = self.size_align_of(ty, locals)? { + destination.write_from_bytes(self, &size.to_le_bytes()) + } else { + let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); + let (size, _) = self.size_align_of_unsized(ty, metadata, locals)?; + destination.write_from_bytes(self, &size.to_le_bytes()) + } + } + "min_align_of_val" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { + return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided")); + }; + let [arg] = args else { + return Err(MirEvalError::TypeError("min_align_of_val args are not provided")); + }; + if let Some((_, align)) = self.size_align_of(ty, locals)? { + destination.write_from_bytes(self, &align.to_le_bytes()) + } else { + let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); + let (_, align) = self.size_align_of_unsized(ty, metadata, locals)?; + destination.write_from_bytes(self, &align.to_le_bytes()) + } + } + "type_name" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError("type_name generic arg is not provided")); + }; + let Ok(ty_name) = ty.display_source_code( + self.db, + locals.body.owner.module(self.db.upcast()), + true, + ) else { + not_supported!("fail in generating type_name using source code display"); + }; + let len = ty_name.len(); + let addr = self.heap_allocate(len, 1)?; + self.write_memory(addr, ty_name.as_bytes())?; + destination.slice(0..self.ptr_size()).write_from_bytes(self, &addr.to_bytes())?; + destination + .slice(self.ptr_size()..2 * self.ptr_size()) + .write_from_bytes(self, &len.to_le_bytes()) + } "needs_drop" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; let result = !ty.clone().is_copy(self.db, locals.body.owner); @@ -501,13 +650,17 @@ impl Evaluator<'_> { let ans = lhs.get(self)? == rhs.get(self)?; destination.write_from_bytes(self, &[u8::from(ans)]) } - "saturating_add" => { + "saturating_add" | "saturating_sub" => { let [lhs, rhs] = args else { return Err(MirEvalError::TypeError("saturating_add args are not provided")); }; let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); - let ans = lhs.saturating_add(rhs); + let ans = match name { + "saturating_add" => lhs.saturating_add(rhs), + "saturating_sub" => lhs.saturating_sub(rhs), + _ => unreachable!(), + }; let bits = destination.size * 8; // FIXME: signed let is_signed = false; @@ -544,6 +697,26 @@ impl Evaluator<'_> { let ans = lhs.wrapping_mul(rhs); destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) } + "wrapping_shl" | "unchecked_shl" => { + // FIXME: signed + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("unchecked_shl args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_shl(rhs as u32); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "wrapping_shr" | "unchecked_shr" => { + // FIXME: signed + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("unchecked_shr args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_shr(rhs as u32); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } "unchecked_rem" => { // FIXME: signed let [lhs, rhs] = args else { @@ -588,7 +761,7 @@ impl Evaluator<'_> { _ => unreachable!(), }; let is_overflow = u128overflow - || ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255); + || ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255); let is_overflow = vec![u8::from(is_overflow)]; let layout = self.layout(&result_ty)?; let result = self.make_by_layout( @@ -603,10 +776,15 @@ impl Evaluator<'_> { } "copy" | "copy_nonoverlapping" => { let [src, dst, offset] = args else { - return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided")); + return Err(MirEvalError::TypeError( + "copy_nonoverlapping args are not provided", + )); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided")); + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "copy_nonoverlapping generic arg is not provided", + )); }; let src = Address::from_bytes(src.get(self)?)?; let dst = Address::from_bytes(dst.get(self)?)?; @@ -621,7 +799,8 @@ impl Evaluator<'_> { let [ptr, offset] = args else { return Err(MirEvalError::TypeError("offset args are not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { return Err(MirEvalError::TypeError("offset generic arg is not provided")); }; let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false)); @@ -652,20 +831,106 @@ impl Evaluator<'_> { } "ctpop" => { let [arg] = args else { - return Err(MirEvalError::TypeError("likely arg is not provided")); + return Err(MirEvalError::TypeError("ctpop arg is not provided")); }; let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones(); destination .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) } + "ctlz" | "ctlz_nonzero" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("cttz arg is not provided")); + }; + let result = + u128::from_le_bytes(pad16(arg.get(self)?, false)).leading_zeros() as usize; + let result = result - (128 - arg.interval.size * 8); + destination + .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) + } "cttz" | "cttz_nonzero" => { let [arg] = args else { - return Err(MirEvalError::TypeError("likely arg is not provided")); + return Err(MirEvalError::TypeError("cttz arg is not provided")); }; let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros(); destination .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) } + "rotate_left" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("rotate_left args are not provided")); + }; + let lhs = &lhs.get(self)?[0..destination.size]; + let rhs = rhs.get(self)?[0] as u32; + match destination.size { + 1 => { + let r = from_bytes!(u8, lhs).rotate_left(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 2 => { + let r = from_bytes!(u16, lhs).rotate_left(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 4 => { + let r = from_bytes!(u32, lhs).rotate_left(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 8 => { + let r = from_bytes!(u64, lhs).rotate_left(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 16 => { + let r = from_bytes!(u128, lhs).rotate_left(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + s => not_supported!("destination with size {s} for rotate_left"), + } + } + "rotate_right" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("rotate_right args are not provided")); + }; + let lhs = &lhs.get(self)?[0..destination.size]; + let rhs = rhs.get(self)?[0] as u32; + match destination.size { + 1 => { + let r = from_bytes!(u8, lhs).rotate_right(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 2 => { + let r = from_bytes!(u16, lhs).rotate_right(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 4 => { + let r = from_bytes!(u32, lhs).rotate_right(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 8 => { + let r = from_bytes!(u64, lhs).rotate_right(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + 16 => { + let r = from_bytes!(u128, lhs).rotate_right(rhs); + destination.write_from_bytes(self, &r.to_le_bytes()) + } + s => not_supported!("destination with size {s} for rotate_right"), + } + } + "discriminant_value" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("discriminant_value arg is not provided")); + }; + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "discriminant_value generic arg is not provided", + )); + }; + let addr = Address::from_bytes(arg.get(self)?)?; + let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?; + let interval = Interval { addr, size }; + let r = self.compute_discriminant(ty.clone(), interval.get(self)?)?; + destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size]) + } "const_eval_select" => { let [tuple, const_fn, _] = args else { return Err(MirEvalError::TypeError("const_eval_select args are not provided")); @@ -681,24 +946,119 @@ impl Evaluator<'_> { let addr = tuple.interval.addr.offset(offset); args.push(IntervalAndTy::new(addr, field, self, locals)?); } - self.exec_fn_trait(&args, destination, locals, span) + if let Some(target) = self.db.lang_item(self.crate_id, LangItem::FnOnce) { + if let Some(def) = target + .as_trait() + .and_then(|it| self.db.trait_data(it).method_by_name(&name![call_once])) + { + self.exec_fn_trait( + def, + &args, + // FIXME: wrong for manual impls of `FnOnce` + Substitution::empty(Interner), + locals, + destination, + None, + span, + )?; + return Ok(()); + } + } + not_supported!("FnOnce was not available for executing const_eval_select"); + } + "read_via_copy" | "volatile_load" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("read_via_copy args are not provided")); + }; + let addr = Address::from_bytes(arg.interval.get(self)?)?; + destination.write_from_interval(self, Interval { addr, size: destination.size }) + } + "write_bytes" => { + let [dst, val, count] = args else { + return Err(MirEvalError::TypeError("write_bytes args are not provided")); + }; + let count = from_bytes!(usize, count.get(self)?); + let val = from_bytes!(u8, val.get(self)?); + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "write_bytes generic arg is not provided", + )); + }; + let dst = Address::from_bytes(dst.get(self)?)?; + let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?; + let size = count * size; + self.write_memory_using_ref(dst, size)?.fill(val); + Ok(()) } _ => not_supported!("unknown intrinsic {name}"), } } + fn size_align_of_unsized( + &mut self, + ty: &Ty, + metadata: Interval, + locals: &Locals, + ) -> Result<(usize, usize)> { + Ok(match ty.kind(Interner) { + TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), + TyKind::Slice(inner) => { + let len = from_bytes!(usize, metadata.get(self)?); + let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?; + (size * len, align) + } + TyKind::Dyn(_) => self.size_align_of_sized( + self.vtable_map.ty_of_bytes(metadata.get(self)?)?, + locals, + "dyn concrete type", + )?, + TyKind::Adt(id, subst) => { + let id = id.0; + let layout = self.layout_adt(id, subst.clone())?; + let id = match id { + AdtId::StructId(s) => s, + _ => not_supported!("unsized enum or union"), + }; + let field_types = &self.db.field_types(id.into()); + let last_field_ty = + field_types.iter().rev().next().unwrap().1.clone().substitute(Interner, subst); + let sized_part_size = + layout.fields.offset(field_types.iter().count() - 1).bytes_usize(); + let sized_part_align = layout.align.abi.bytes() as usize; + let (unsized_part_size, unsized_part_align) = + self.size_align_of_unsized(&last_field_ty, metadata, locals)?; + let align = sized_part_align.max(unsized_part_align) as isize; + let size = (sized_part_size + unsized_part_size) as isize; + // Must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + let size = (size + (align - 1)) & (-align); + (size as usize, align as usize) + } + _ => not_supported!("unsized type other than str, slice, struct and dyn"), + }) + } + fn exec_atomic_intrinsic( &mut self, name: &str, args: &[IntervalAndTy], generic_args: &Substitution, destination: Interval, - locals: &Locals<'_>, + locals: &Locals, _span: MirSpan, ) -> Result<()> { // We are a single threaded runtime with no UB checking and no optimization, so // we can implement these as normal functions. - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided")); }; let Some(arg0) = args.get(0) else { diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs new file mode 100644 index 000000000000..ec7463104879 --- /dev/null +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -0,0 +1,177 @@ +//! Shim implementation for simd intrinsics + +use std::cmp::Ordering; + +use crate::TyKind; + +use super::*; + +macro_rules! from_bytes { + ($ty:tt, $value:expr) => { + ($ty::from_le_bytes(match ($value).try_into() { + Ok(it) => it, + Err(_) => return Err(MirEvalError::TypeError("mismatched size")), + })) + }; +} + +macro_rules! not_supported { + ($it: expr) => { + return Err(MirEvalError::NotSupported(format!($it))) + }; +} + +impl Evaluator<'_> { + fn detect_simd_ty(&self, ty: &Ty) -> Result<(usize, Ty)> { + match ty.kind(Interner) { + TyKind::Adt(id, subst) => { + let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner)) + { + Some(len) => len, + _ => { + if let AdtId::StructId(id) = id.0 { + let struct_data = self.db.struct_data(id); + let fields = struct_data.variant_data.fields(); + let Some((first_field, _)) = fields.iter().next() else { + not_supported!("simd type with no field"); + }; + let field_ty = self.db.field_types(id.into())[first_field] + .clone() + .substitute(Interner, subst); + return Ok((fields.len(), field_ty)); + } + return Err(MirEvalError::TypeError("simd type with no len param")); + } + }; + match try_const_usize(self.db, len) { + Some(len) => { + let Some(ty) = subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { + return Err(MirEvalError::TypeError("simd type with no ty param")); + }; + Ok((len as usize, ty.clone())) + } + None => Err(MirEvalError::TypeError("simd type with unevaluatable len param")), + } + } + _ => Err(MirEvalError::TypeError("simd type which is not a struct")), + } + } + + pub(super) fn exec_simd_intrinsic( + &mut self, + name: &str, + args: &[IntervalAndTy], + _generic_args: &Substitution, + destination: Interval, + _locals: &Locals, + _span: MirSpan, + ) -> Result<()> { + match name { + "and" | "or" | "xor" => { + let [left, right] = args else { + return Err(MirEvalError::TypeError("simd bit op args are not provided")); + }; + let result = left + .get(self)? + .iter() + .zip(right.get(self)?) + .map(|(&it, &y)| match name { + "and" => it & y, + "or" => it | y, + "xor" => it ^ y, + _ => unreachable!(), + }) + .collect::>(); + destination.write_from_bytes(self, &result) + } + "eq" | "ne" | "lt" | "le" | "gt" | "ge" => { + let [left, right] = args else { + return Err(MirEvalError::TypeError("simd args are not provided")); + }; + let (len, ty) = self.detect_simd_ty(&left.ty)?; + let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); + let size = left.interval.size / len; + let dest_size = destination.size / len; + let mut destination_bytes = vec![]; + let vector = left.get(self)?.chunks(size).zip(right.get(self)?.chunks(size)); + for (l, r) in vector { + let mut result = Ordering::Equal; + for (l, r) in l.iter().zip(r).rev() { + let it = l.cmp(r); + if it != Ordering::Equal { + result = it; + break; + } + } + if is_signed { + if let Some((&l, &r)) = l.iter().zip(r).rev().next() { + if l != r { + result = (l as i8).cmp(&(r as i8)); + } + } + } + let result = match result { + Ordering::Less => ["lt", "le", "ne"].contains(&name), + Ordering::Equal => ["ge", "le", "eq"].contains(&name), + Ordering::Greater => ["ge", "gt", "ne"].contains(&name), + }; + let result = if result { 255 } else { 0 }; + destination_bytes.extend(std::iter::repeat(result).take(dest_size)); + } + + destination.write_from_bytes(self, &destination_bytes) + } + "bitmask" => { + let [op] = args else { + return Err(MirEvalError::TypeError("simd_bitmask args are not provided")); + }; + let (op_len, _) = self.detect_simd_ty(&op.ty)?; + let op_count = op.interval.size / op_len; + let mut result: u64 = 0; + for (i, val) in op.get(self)?.chunks(op_count).enumerate() { + if !val.iter().all(|&it| it == 0) { + result |= 1 << i; + } + } + destination.write_from_bytes(self, &result.to_le_bytes()[0..destination.size]) + } + "shuffle" => { + let [left, right, index] = args else { + return Err(MirEvalError::TypeError("simd_shuffle args are not provided")); + }; + let TyKind::Array(_, index_len) = index.ty.kind(Interner) else { + return Err(MirEvalError::TypeError( + "simd_shuffle index argument has non-array type", + )); + }; + let index_len = match try_const_usize(self.db, index_len) { + Some(it) => it as usize, + None => { + return Err(MirEvalError::TypeError( + "simd type with unevaluatable len param", + )) + } + }; + let (left_len, _) = self.detect_simd_ty(&left.ty)?; + let left_size = left.interval.size / left_len; + let vector = + left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size)); + let mut result = vec![]; + for index in index.get(self)?.chunks(index.interval.size / index_len) { + let index = from_bytes!(u32, index) as usize; + let val = match vector.clone().nth(index) { + Some(it) => it, + None => { + return Err(MirEvalError::TypeError( + "out of bound access in simd shuffle", + )) + } + }; + result.extend(val); + } + destination.write_from_bytes(self, &result) + } + _ => not_supported!("unknown simd intrinsic {name}"), + } + } +} diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index ca4268b8fb00..03c083bac422 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -30,7 +30,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; - let (result, stdout, stderr) = interpret_mir(db, &body, false); + let (result, stdout, stderr) = interpret_mir(db, body, false); result?; Ok((stdout, stderr)) } @@ -613,6 +613,34 @@ fn main() { ); } +#[test] +fn syscalls() { + check_pass( + r#" +//- minicore: option + +extern "C" { + pub unsafe extern "C" fn syscall(num: i64, ...) -> i64; +} + +const SYS_getrandom: i64 = 318; + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let mut x: i32 = 0; + let r = syscall(SYS_getrandom, &mut x, 4usize, 0); + if r != 4 { + should_not_reach(); + } +} + +"#, + ) +} + #[test] fn posix_tls() { check_pass( diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 2cb29b4ab91d..361085879041 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -146,12 +146,12 @@ impl MirLowerError { ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?, } } - MirLowerError::MissingFunctionDefinition(owner, x) => { + MirLowerError::MissingFunctionDefinition(owner, it) => { let body = db.body(*owner); writeln!( f, "Missing function definition for {}", - body.pretty_print_expr(db.upcast(), *owner, *x) + body.pretty_print_expr(db.upcast(), *owner, *it) )?; } MirLowerError::TypeMismatch(e) => { @@ -202,15 +202,15 @@ impl MirLowerError { } macro_rules! not_supported { - ($x: expr) => { - return Err(MirLowerError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirLowerError::NotSupported(format!($it))) }; } macro_rules! implementation_error { - ($x: expr) => {{ - ::stdx::never!("MIR lower implementation bug: {}", format!($x)); - return Err(MirLowerError::ImplementationError(format!($x))); + ($it: expr) => {{ + ::stdx::never!("MIR lower implementation bug: {}", format!($it)); + return Err(MirLowerError::ImplementationError(format!($it))); }}; } @@ -310,24 +310,30 @@ impl<'ctx> MirLowerCtx<'ctx> { self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) } Adjust::Deref(_) => { - let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else { - return Ok(None); - }; + let Some((p, current)) = + self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? + else { + return Ok(None); + }; self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); Ok(Some(current)) } Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => { - let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { - return Ok(None); - }; + let Some((p, current)) = + self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? + else { + return Ok(None); + }; let bk = BorrowKind::from_chalk(*m); self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); Ok(Some(current)) } Adjust::Pointer(cast) => { - let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { - return Ok(None); - }; + let Some((p, current)) = + self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? + else { + return Ok(None); + }; self.push_assignment( current, place, @@ -373,45 +379,49 @@ impl<'ctx> MirLowerCtx<'ctx> { } } Err(MirLowerError::IncompleteExpr) - }, + } Expr::Path(p) => { - let pr = if let Some((assoc, subst)) = self - .infer - .assoc_resolutions_for_expr(expr_id) - { - match assoc { - hir_def::AssocItemId::ConstId(c) => { - self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; - return Ok(Some(current)) - }, - hir_def::AssocItemId::FunctionId(_) => { - // FnDefs are zero sized, no action is needed. - return Ok(Some(current)) + let pr = + if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) { + match assoc { + hir_def::AssocItemId::ConstId(c) => { + self.lower_const( + c.into(), + current, + place, + subst, + expr_id.into(), + self.expr_ty_without_adjust(expr_id), + )?; + return Ok(Some(current)); + } + hir_def::AssocItemId::FunctionId(_) => { + // FnDefs are zero sized, no action is needed. + return Ok(Some(current)); + } + hir_def::AssocItemId::TypeAliasId(_) => { + // FIXME: If it is unreachable, use proper error instead of `not_supported`. + not_supported!("associated functions and types") + } } - hir_def::AssocItemId::TypeAliasId(_) => { - // FIXME: If it is unreachable, use proper error instead of `not_supported`. - not_supported!("associated functions and types") - }, - } - } else if let Some(variant) = self - .infer - .variant_resolution_for_expr(expr_id) - { - match variant { - VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), - VariantId::StructId(s) => ValueNs::StructId(s), - VariantId::UnionId(_) => implementation_error!("Union variant as path"), - } - } else { - let unresolved_name = || MirLowerError::unresolved_path(self.db, p); - let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); - resolver - .resolve_path_in_value_ns_fully(self.db.upcast(), p) - .ok_or_else(unresolved_name)? - }; + } else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) { + match variant { + VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), + VariantId::StructId(s) => ValueNs::StructId(s), + VariantId::UnionId(_) => implementation_error!("Union variant as path"), + } + } else { + let unresolved_name = || MirLowerError::unresolved_path(self.db, p); + let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); + resolver + .resolve_path_in_value_ns_fully(self.db.upcast(), p) + .ok_or_else(unresolved_name)? + }; match pr { ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => { - let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else { + let Some((temp, current)) = + self.lower_expr_as_place_without_adjust(current, expr_id, false)? + else { return Ok(None); }; self.push_assignment( @@ -423,11 +433,19 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(Some(current)) } ValueNs::ConstId(const_id) => { - self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + self.lower_const( + const_id.into(), + current, + place, + Substitution::empty(Interner), + expr_id.into(), + self.expr_ty_without_adjust(expr_id), + )?; Ok(Some(current)) } ValueNs::EnumVariantId(variant_id) => { - let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; + let variant_data = + &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; if variant_data.variant_data.kind() == StructKind::Unit { let ty = self.infer.type_of_expr[expr_id].clone(); current = self.lower_enum_variant( @@ -472,13 +490,15 @@ impl<'ctx> MirLowerCtx<'ctx> { // It's probably a unit struct or a zero sized function, so no action is needed. Ok(Some(current)) } - x => { - not_supported!("unknown name {x:?} in value name space"); + it => { + not_supported!("unknown name {it:?} in value name space"); } } } Expr::If { condition, then_branch, else_branch } => { - let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else { + let Some((discr, current)) = + self.lower_expr_to_some_operand(*condition, current)? + else { return Ok(None); }; let start_of_then = self.new_basic_block(); @@ -501,15 +521,12 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())) } Expr::Let { pat, expr } => { - let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? + else { return Ok(None); }; - let (then_target, else_target) = self.pattern_match( - current, - None, - cond_place, - *pat, - )?; + let (then_target, else_target) = + self.pattern_match(current, None, cond_place, *pat)?; self.write_bytes_to_place( then_target, place.clone(), @@ -533,31 +550,47 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Block { id: _, statements, tail, label } => { if let Some(label) = label { - self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| { - if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? { - let end = this.current_loop_end()?; - this.set_goto(current, end, expr_id.into()); - } - Ok(()) - }) + self.lower_loop( + current, + place.clone(), + Some(*label), + expr_id.into(), + |this, begin| { + if let Some(current) = this.lower_block_to_place( + statements, + begin, + *tail, + place, + expr_id.into(), + )? { + let end = this.current_loop_end()?; + this.set_goto(current, end, expr_id.into()); + } + Ok(()) + }, + ) } else { self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) } } - Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { - let scope = this.push_drop_scope(); - if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { - current = scope.pop_and_drop(this, current); - this.set_goto(current, begin, expr_id.into()); - } else { - scope.pop_assume_dropped(this); - } - Ok(()) - }), - Expr::While { condition, body, label } => { - self.lower_loop(current, place, *label, expr_id.into(),|this, begin| { + Expr::Loop { body, label } => { + self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); - let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { + if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { + current = scope.pop_and_drop(this, current); + this.set_goto(current, begin, expr_id.into()); + } else { + scope.pop_assume_dropped(this); + } + Ok(()) + }) + } + Expr::While { condition, body, label } => { + self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { + let scope = this.push_drop_scope(); + let Some((discr, to_switch)) = + this.lower_expr_to_some_operand(*condition, begin)? + else { return Ok(()); }; let fail_cond = this.new_basic_block(); @@ -583,8 +616,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }) } Expr::Call { callee, args, .. } => { - if let Some((func_id, generic_args)) = - self.infer.method_resolution(expr_id) { + if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { let ty = chalk_ir::TyKind::FnDef( CallableDefId::FunctionId(func_id).to_chalk(self.db), generic_args, @@ -604,21 +636,43 @@ impl<'ctx> MirLowerCtx<'ctx> { match &callee_ty.data(Interner).kind { chalk_ir::TyKind::FnDef(..) => { let func = Operand::from_bytes(vec![], callee_ty.clone()); - self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) + self.lower_call_and_args( + func, + args.iter().copied(), + place, + current, + self.is_uninhabited(expr_id), + expr_id.into(), + ) } chalk_ir::TyKind::Function(_) => { - let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else { + let Some((func, current)) = + self.lower_expr_to_some_operand(*callee, current)? + else { return Ok(None); }; - self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) + self.lower_call_and_args( + func, + args.iter().copied(), + place, + current, + self.is_uninhabited(expr_id), + expr_id.into(), + ) + } + TyKind::Error => { + return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)) } - TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)), _ => return Err(MirLowerError::TypeError("function call on bad type")), } } Expr::MethodCall { receiver, args, method_name, .. } => { let (func_id, generic_args) = - self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?; + self.infer.method_resolution(expr_id).ok_or_else(|| { + MirLowerError::UnresolvedMethod( + method_name.display(self.db.upcast()).to_string(), + ) + })?; let func = Operand::from_fn(self.db, func_id, generic_args); self.lower_call_and_args( func, @@ -630,23 +684,27 @@ impl<'ctx> MirLowerCtx<'ctx> { ) } Expr::Match { expr, arms } => { - let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)? + let Some((cond_place, mut current)) = + self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; let mut end = None; for MatchArm { pat, guard, expr } in arms.iter() { - let (then, mut otherwise) = self.pattern_match( - current, - None, - cond_place.clone(), - *pat, - )?; + let (then, mut otherwise) = + self.pattern_match(current, None, cond_place.clone(), *pat)?; let then = if let &Some(guard) = guard { let next = self.new_basic_block(); let o = otherwise.get_or_insert_with(|| self.new_basic_block()); if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? { - self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into()); + self.set_terminator( + c, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, next, *o), + }, + expr_id.into(), + ); } next } else { @@ -672,33 +730,53 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Continue { label } => { let loop_data = match label { - Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?, - None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?, + Some(l) => { + self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)? + } + None => self + .current_loop_blocks + .as_ref() + .ok_or(MirLowerError::ContinueWithoutLoop)?, }; let begin = loop_data.begin; current = self.drop_until_scope(loop_data.drop_scope_index, current); self.set_goto(current, begin, expr_id.into()); Ok(None) - }, + } &Expr::Break { expr, label } => { if let Some(expr) = expr { let loop_data = match label { - Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?, - None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?, + Some(l) => self + .labeled_loop_blocks + .get(&l) + .ok_or(MirLowerError::UnresolvedLabel)?, + None => self + .current_loop_blocks + .as_ref() + .ok_or(MirLowerError::BreakWithoutLoop)?, }; - let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else { + let Some(c) = + self.lower_expr_to_place(expr, loop_data.place.clone(), current)? + else { return Ok(None); }; current = c; } let (end, drop_scope) = match label { Some(l) => { - let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?; - (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index) - }, - None => { - (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index) - }, + let loop_blocks = self + .labeled_loop_blocks + .get(&l) + .ok_or(MirLowerError::UnresolvedLabel)?; + ( + loop_blocks.end.expect("We always generate end for labeled loops"), + loop_blocks.drop_scope_index, + ) + } + None => ( + self.current_loop_end()?, + self.current_loop_blocks.as_ref().unwrap().drop_scope_index, + ), }; current = self.drop_until_scope(drop_scope, current); self.set_goto(current, end, expr_id.into()); @@ -706,7 +784,9 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Return { expr } => { if let Some(expr) = expr { - if let Some(c) = self.lower_expr_to_place(*expr, return_slot().into(), current)? { + if let Some(c) = + self.lower_expr_to_place(*expr, return_slot().into(), current)? + { current = c; } else { return Ok(None); @@ -719,19 +799,17 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Yield { .. } => not_supported!("yield"), Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => { let spread_place = match spread { - &Some(x) => { - let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else { + &Some(it) => { + let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { return Ok(None); }; current = c; Some(p) - }, + } None => None, }; - let variant_id = self - .infer - .variant_resolution_for_expr(expr_id) - .ok_or_else(|| match path { + let variant_id = + self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()), None => MirLowerError::RecordLiteralWithoutPath, })?; @@ -746,7 +824,8 @@ impl<'ctx> MirLowerCtx<'ctx> { for RecordLitField { name, expr } in fields.iter() { let field_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else { + let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? + else { return Ok(None); }; current = c; @@ -758,18 +837,23 @@ impl<'ctx> MirLowerCtx<'ctx> { Rvalue::Aggregate( AggregateKind::Adt(variant_id, subst), match spread_place { - Some(sp) => operands.into_iter().enumerate().map(|(i, x)| { - match x { - Some(x) => x, + Some(sp) => operands + .into_iter() + .enumerate() + .map(|(i, it)| match it { + Some(it) => it, None => { - let p = sp.project(ProjectionElem::Field(FieldId { - parent: variant_id, - local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)), - })); + let p = + sp.project(ProjectionElem::Field(FieldId { + parent: variant_id, + local_id: LocalFieldId::from_raw( + RawIdx::from(i as u32), + ), + })); Operand::Copy(p) - }, - } - }).collect(), + } + }) + .collect(), None => operands.into_iter().collect::>().ok_or( MirLowerError::TypeError("missing field in record literal"), )?, @@ -785,7 +869,10 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id })); + let place = place.project(PlaceElem::Field(FieldId { + parent: union_id.into(), + local_id, + })); self.lower_expr_to_place(*expr, place, current) } } @@ -795,11 +882,18 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Async { .. } => not_supported!("async block"), &Expr::Const(id) => { let subst = self.placeholder_subst(); - self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + self.lower_const( + id.into(), + current, + place, + subst, + expr_id.into(), + self.expr_ty_without_adjust(expr_id), + )?; Ok(Some(current)) - }, + } Expr::Cast { expr, type_ref: _ } => { - let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; let source_ty = self.infer[*expr].clone(); @@ -807,7 +901,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.push_assignment( current, place, - Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty), + Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, it, target_ty), expr_id.into(), ); Ok(Some(current)) @@ -822,23 +916,37 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Box { expr } => { let ty = self.expr_ty_after_adjustments(*expr); - self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into()); - let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + self.push_assignment( + current, + place.clone(), + Rvalue::ShallowInitBoxWithAlloc(ty), + expr_id.into(), + ); + let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? + else { return Ok(None); }; let p = place.project(ProjectionElem::Deref); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) - }, - Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => { - let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else { + } + Expr::Field { .. } + | Expr::Index { .. } + | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => { + let Some((p, current)) = + self.lower_expr_as_place_without_adjust(current, expr_id, true)? + else { return Ok(None); }; self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); Ok(Some(current)) } - Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => { - let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + Expr::UnaryOp { + expr, + op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg), + } => { + let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? + else { return Ok(None); }; let operation = match op { @@ -853,7 +961,7 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id.into(), ); Ok(Some(current)) - }, + } Expr::BinaryOp { lhs, rhs, op } => { let op = op.ok_or(MirLowerError::IncompleteExpr)?; let is_builtin = 'b: { @@ -861,16 +969,19 @@ impl<'ctx> MirLowerCtx<'ctx> { // for binary operator, and use without adjust to simplify our conditions. let lhs_ty = self.expr_ty_without_adjust(*lhs); let rhs_ty = self.expr_ty_without_adjust(*rhs); - if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) { + if matches!(op, BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) { if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() { break 'b true; } } let builtin_inequal_impls = matches!( op, - BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } + BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) + | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } ); - lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls) + lhs_ty.is_scalar() + && rhs_ty.is_scalar() + && (lhs_ty == rhs_ty || builtin_inequal_impls) }; if !is_builtin { if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { @@ -892,34 +1003,45 @@ impl<'ctx> MirLowerCtx<'ctx> { .infer .expr_adjustments .get(lhs) - .and_then(|x| x.split_last()) - .map(|x| x.1) - .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?; + .and_then(|it| it.split_last()) + .map(|it| it.1) + .ok_or(MirLowerError::TypeError( + "adjustment of binary op was missing", + ))?; let Some((lhs_place, current)) = self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)? else { return Ok(None); }; - let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = + self.lower_expr_to_some_operand(*rhs, current)? + else { return Ok(None); }; - let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op); + let r_value = Rvalue::CheckedBinaryOp( + op.into(), + Operand::Copy(lhs_place.clone()), + rhs_op, + ); self.push_assignment(current, lhs_place, r_value, expr_id.into()); return Ok(Some(current)); } else { let Some((lhs_place, current)) = - self.lower_expr_as_place(current, *lhs, false)? + self.lower_expr_as_place(current, *lhs, false)? else { return Ok(None); }; - let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = + self.lower_expr_to_some_operand(*rhs, current)? + else { return Ok(None); }; self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); return Ok(Some(current)); } } - let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { + let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? + else { return Ok(None); }; if let hir_def::hir::BinaryOp::LogicOp(op) = op { @@ -928,22 +1050,31 @@ impl<'ctx> MirLowerCtx<'ctx> { syntax::ast::LogicOp::Or => 1, }; let start_of_then = self.new_basic_block(); - self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into()); + self.push_assignment( + start_of_then, + place.clone(), + lhs_op.clone().into(), + expr_id.into(), + ); let end_of_then = Some(start_of_then); let start_of_else = self.new_basic_block(); - let end_of_else = - self.lower_expr_to_place(*rhs, place, start_of_else)?; + let end_of_else = self.lower_expr_to_place(*rhs, place, start_of_else)?; self.set_terminator( current, TerminatorKind::SwitchInt { discr: lhs_op, - targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else), + targets: SwitchTargets::static_if( + value_to_short, + start_of_then, + start_of_else, + ), }, expr_id.into(), ); return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())); } - let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? + else { return Ok(None); }; self.push_assignment( @@ -976,15 +1107,15 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let mut lp = None; let mut rp = None; - if let Some(x) = lhs { - let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + if let Some(it) = lhs { + let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else { return Ok(None); }; lp = Some(o); current = c; } - if let Some(x) = rhs { - let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + if let Some(it) = rhs { + let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else { return Ok(None); }; rp = Some(o); @@ -995,20 +1126,28 @@ impl<'ctx> MirLowerCtx<'ctx> { place, Rvalue::Aggregate( AggregateKind::Adt(st.into(), subst.clone()), - self.db.struct_data(st).variant_data.fields().iter().map(|x| { - let o = match x.1.name.as_str() { - Some("start") => lp.take(), - Some("end") => rp.take(), - Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())), - _ => None, - }; - o.ok_or(MirLowerError::UnresolvedField) - }).collect::>()?, + self.db + .struct_data(st) + .variant_data + .fields() + .iter() + .map(|it| { + let o = match it.1.name.as_str() { + Some("start") => lp.take(), + Some("end") => rp.take(), + Some("exhausted") => { + Some(Operand::from_bytes(vec![0], TyBuilder::bool())) + } + _ => None, + }; + o.ok_or(MirLowerError::UnresolvedField) + }) + .collect::>()?, ), expr_id.into(), ); Ok(Some(current)) - }, + } Expr::Closure { .. } => { let ty = self.expr_ty_without_adjust(expr_id); let TyKind::Closure(id, _) = ty.kind(Interner) else { @@ -1020,22 +1159,33 @@ impl<'ctx> MirLowerCtx<'ctx> { for capture in captures.iter() { let p = Place { local: self.binding_local(capture.place.local)?, - projection: capture.place.projections.clone().into_iter().map(|x| { - match x { + projection: capture + .place + .projections + .clone() + .into_iter() + .map(|it| match it { ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(x) => ProjectionElem::Field(x), - ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x), - ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end }, - ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, - ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x), - ProjectionElem::Index(x) => match x { }, - } - }).collect(), + ProjectionElem::Field(it) => ProjectionElem::Field(it), + ProjectionElem::TupleOrClosureField(it) => { + ProjectionElem::TupleOrClosureField(it) + } + ProjectionElem::ConstantIndex { offset, from_end } => { + ProjectionElem::ConstantIndex { offset, from_end } + } + ProjectionElem::Subslice { from, to } => { + ProjectionElem::Subslice { from, to } + } + ProjectionElem::OpaqueCast(it) => ProjectionElem::OpaqueCast(it), + ProjectionElem::Index(it) => match it {}, + }) + .collect(), }; match &capture.kind { CaptureKind::ByRef(bk) => { let placeholder_subst = self.placeholder_subst(); - let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst); + let tmp_ty = + capture.ty.clone().substitute(Interner, &placeholder_subst); let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into(); self.push_assignment( current, @@ -1044,7 +1194,7 @@ impl<'ctx> MirLowerCtx<'ctx> { capture.span, ); operands.push(Operand::Move(tmp)); - }, + } CaptureKind::ByValue => operands.push(Operand::Move(p)), } } @@ -1055,18 +1205,18 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id.into(), ); Ok(Some(current)) - }, + } Expr::Tuple { exprs, is_assignee_expr: _ } => { let Some(values) = exprs - .iter() - .map(|x| { - let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { - return Ok(None); - }; - current = c; - Ok(Some(o)) - }) - .collect::>>()? + .iter() + .map(|it| { + let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) + }) + .collect::>>()? else { return Ok(None); }; @@ -1088,27 +1238,26 @@ impl<'ctx> MirLowerCtx<'ctx> { } }; let Some(values) = elements - .iter() - .map(|x| { - let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { - return Ok(None); - }; - current = c; - Ok(Some(o)) - }) - .collect::>>()? + .iter() + .map(|it| { + let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) + }) + .collect::>>()? else { return Ok(None); }; - let r = Rvalue::Aggregate( - AggregateKind::Array(elem_ty), - values, - ); + let r = Rvalue::Aggregate(AggregateKind::Array(elem_ty), values); self.push_assignment(current, place, r, expr_id.into()); Ok(Some(current)) } Array::Repeat { initializer, .. } => { - let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else { + let Some((init, current)) = + self.lower_expr_to_some_operand(*initializer, current)? + else { return Ok(None); }; let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { @@ -1122,7 +1271,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let r = Rvalue::Repeat(init, len); self.push_assignment(current, place, r, expr_id.into()); Ok(Some(current)) - }, + } }, Expr::Literal(l) => { let ty = self.expr_ty_without_adjust(expr_id); @@ -1136,7 +1285,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn placeholder_subst(&mut self) -> Substitution { let placeholder_subst = match self.owner.as_generic_def_id() { - Some(x) => TyBuilder::placeholder_subst(self.db, x), + Some(it) => TyBuilder::placeholder_subst(self.db, it), None => Substitution::empty(Interner), }; placeholder_subst @@ -1226,8 +1375,8 @@ impl<'ctx> MirLowerCtx<'ctx> { } hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), hir_def::hir::Literal::Bool(b) => vec![*b as u8], - hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(), - hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(), + hir_def::hir::Literal::Int(it, _) => it.to_le_bytes()[0..size].into(), + hir_def::hir::Literal::Uint(it, _) => it.to_le_bytes()[0..size].into(), hir_def::hir::Literal::Float(f, _) => match size { 8 => f.into_f64().to_le_bytes().into(), 4 => f.into_f32().to_le_bytes().into(), @@ -1377,9 +1526,9 @@ impl<'ctx> MirLowerCtx<'ctx> { fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { let mut ty = None; - if let Some(x) = self.infer.expr_adjustments.get(&e) { - if let Some(x) = x.last() { - ty = Some(x.target.clone()); + if let Some(it) = self.infer.expr_adjustments.get(&e) { + if let Some(it) = it.last() { + ty = Some(it.target.clone()); } } ty.unwrap_or_else(|| self.expr_ty_without_adjust(e)) @@ -1401,7 +1550,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { match &self.discr_temp { - Some(x) => x.clone(), + Some(it) => it.clone(), None => { let tmp: Place = self .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown) @@ -1448,7 +1597,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn has_adjustments(&self, expr_id: ExprId) -> bool { - !self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true) + !self.infer.expr_adjustments.get(&expr_id).map(|it| it.is_empty()).unwrap_or(true) } fn merge_blocks( @@ -1478,7 +1627,7 @@ impl<'ctx> MirLowerCtx<'ctx> { ))? .end { - Some(x) => x, + Some(it) => it, None => { let s = self.new_basic_block(); self.current_loop_blocks @@ -1602,10 +1751,10 @@ impl<'ctx> MirLowerCtx<'ctx> { pick_binding: impl Fn(BindingId) -> bool, ) -> Result { let base_param_count = self.result.param_locals.len(); - self.result.param_locals.extend(params.clone().map(|(x, ty)| { + self.result.param_locals.extend(params.clone().map(|(it, ty)| { let local_id = self.result.locals.alloc(Local { ty }); self.drop_scopes.last_mut().unwrap().locals.push(local_id); - if let Pat::Bind { id, subpat: None } = self.body[x] { + if let Pat::Bind { id, subpat: None } = self.body[it] { if matches!( self.body.bindings[id].mode, BindingAnnotation::Unannotated | BindingAnnotation::Mutable @@ -1646,7 +1795,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn binding_local(&self, b: BindingId) -> Result { match self.result.binding_locals.get(b) { - Some(x) => Ok(*x), + Some(it) => Ok(*it), None => { // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which // is a hir lowering problem IMO. @@ -1731,6 +1880,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) { + (TyKind::FnDef(..), TyKind::Function(_)) => CastKind::Pointer(PointerCast::ReifyFnPointer), (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) { (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat, (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt, @@ -1742,17 +1892,17 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { CastKind::Pointer(if a == b { PointerCast::MutToConstPointer - } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str) - && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str) + } else if matches!(b.kind(Interner), TyKind::Slice(_)) + && matches!(a.kind(Interner), TyKind::Array(_, _)) + || matches!(b.kind(Interner), TyKind::Dyn(_)) { - // slice to slice cast is no-op (metadata is not touched), so we use this - PointerCast::MutToConstPointer - } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { PointerCast::Unsize } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { PointerCast::ArrayToPointer } else { - // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant + // cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like + // slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata + // will be touched) but there is no specific variant // for it in `PointerCast` so we use `MutToConstPointer` PointerCast::MutToConstPointer }) @@ -1796,7 +1946,7 @@ pub fn mir_body_for_closure_query( implementation_error!("closure has not callable sig"); }; let current = ctx.lower_params_and_bindings( - args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())), + args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())), |_| true, )?; if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { @@ -1815,34 +1965,35 @@ pub fn mir_body_for_closure_query( FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref], }; ctx.result.walk_places(|p| { - if let Some(x) = upvar_map.get(&p.local) { - let r = x.iter().find(|x| { - if p.projection.len() < x.0.place.projections.len() { + if let Some(it) = upvar_map.get(&p.local) { + let r = it.iter().find(|it| { + if p.projection.len() < it.0.place.projections.len() { return false; } - for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) { - match (x, y) { + for (it, y) in p.projection.iter().zip(it.0.place.projections.iter()) { + match (it, y) { (ProjectionElem::Deref, ProjectionElem::Deref) => (), - (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (), + (ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (), ( - ProjectionElem::TupleOrClosureField(x), + ProjectionElem::TupleOrClosureField(it), ProjectionElem::TupleOrClosureField(y), - ) if x == y => (), + ) if it == y => (), _ => return false, } } true }); match r { - Some(x) => { + Some(it) => { p.local = closure_local; let mut next_projs = closure_projection.clone(); - next_projs.push(PlaceElem::TupleOrClosureField(x.1)); + next_projs.push(PlaceElem::TupleOrClosureField(it.1)); let prev_projs = mem::take(&mut p.projection); - if x.0.kind != CaptureKind::ByValue { + if it.0.kind != CaptureKind::ByValue { next_projs.push(ProjectionElem::Deref); } - next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len())); + next_projs + .extend(prev_projs.iter().cloned().skip(it.0.place.projections.len())); p.projection = next_projs.into(); } None => err = Some(p.clone()), @@ -1902,8 +2053,8 @@ pub fn lower_to_mir( // need to take this input explicitly. root_expr: ExprId, ) -> Result { - if let Some((_, x)) = infer.type_mismatches().next() { - return Err(MirLowerError::TypeMismatch(x.clone())); + if let Some((_, it)) = infer.type_mismatches().next() { + return Err(MirLowerError::TypeMismatch(it.clone())); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local @@ -1929,7 +2080,7 @@ pub fn lower_to_mir( body.params .iter() .zip(callable_sig.params().iter()) - .map(|(x, y)| (*x, y.clone())), + .map(|(it, y)| (*it, y.clone())), binding_picker, )?; } diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index d2c8d9a089e0..213f151ab676 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -1,12 +1,12 @@ //! MIR lowering for places use super::*; -use hir_def::{lang_item::lang_attr, FunctionId}; +use hir_def::FunctionId; use hir_expand::name; macro_rules! not_supported { - ($x: expr) => { - return Err(MirLowerError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirLowerError::NotSupported(format!($it))) }; } @@ -18,7 +18,9 @@ impl MirLowerCtx<'_> { ) -> Result> { let ty = self.expr_ty_without_adjust(expr_id); let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else { + let Some(current) = + self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? + else { return Ok(None); }; Ok(Some((place.into(), current))) @@ -32,10 +34,12 @@ impl MirLowerCtx<'_> { ) -> Result> { let ty = adjustments .last() - .map(|x| x.target.clone()) + .map(|it| it.target.clone()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else { + let Some(current) = + self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? + else { return Ok(None); }; Ok(Some((place.into(), current))) @@ -57,16 +61,17 @@ impl MirLowerCtx<'_> { if let Some((last, rest)) = adjustments.split_last() { match last.kind { Adjust::Deref(None) => { - let Some(mut x) = self.lower_expr_as_place_with_adjust( + let Some(mut it) = self.lower_expr_as_place_with_adjust( current, expr_id, upgrade_rvalue, rest, - )? else { + )? + else { return Ok(None); }; - x.0 = x.0.project(ProjectionElem::Deref); - Ok(Some(x)) + it.0 = it.0.project(ProjectionElem::Deref); + Ok(Some(it)) } Adjust::Deref(Some(od)) => { let Some((r, current)) = self.lower_expr_as_place_with_adjust( @@ -74,14 +79,15 @@ impl MirLowerCtx<'_> { expr_id, upgrade_rvalue, rest, - )? else { + )? + else { return Ok(None); }; self.lower_overloaded_deref( current, r, rest.last() - .map(|x| x.target.clone()) + .map(|it| it.target.clone()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)), last.target.clone(), expr_id.into(), @@ -156,7 +162,7 @@ impl MirLowerCtx<'_> { let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) { TyKind::Ref(..) | TyKind::Raw(..) => true, TyKind::Adt(id, _) => { - if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) { + if let Some(lang_item) = self.db.lang_attr(id.0.into()) { lang_item == LangItem::OwnedBox } else { false @@ -165,7 +171,8 @@ impl MirLowerCtx<'_> { _ => false, }; if !is_builtin { - let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? + else { return Ok(None); }; return self.lower_overloaded_deref( @@ -192,7 +199,8 @@ impl MirLowerCtx<'_> { }, ); } - let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? + else { return Ok(None); }; r = r.project(ProjectionElem::Deref); @@ -217,12 +225,18 @@ impl MirLowerCtx<'_> { ) { let Some(index_fn) = self.infer.method_resolution(expr_id) else { - return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string())); + return Err(MirLowerError::UnresolvedMethod( + "[overloaded index]".to_string(), + )); }; - let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + let Some((base_place, current)) = + self.lower_expr_as_place(current, *base, true)? + else { return Ok(None); }; - let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else { + let Some((index_operand, current)) = + self.lower_expr_to_some_operand(*index, current)? + else { return Ok(None); }; return self.lower_overloaded_index( @@ -239,8 +253,8 @@ impl MirLowerCtx<'_> { .infer .expr_adjustments .get(base) - .and_then(|x| x.split_last()) - .map(|x| x.1) + .and_then(|it| it.split_last()) + .map(|it| it.1) .unwrap_or(&[]); let Some((mut p_base, current)) = self.lower_expr_as_place_with_adjust(current, *base, true, adjusts)? @@ -249,7 +263,8 @@ impl MirLowerCtx<'_> { }; let l_index = self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?; - let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else { + let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? + else { return Ok(None); }; p_base = p_base.project(ProjectionElem::Index(l_index)); @@ -282,7 +297,15 @@ impl MirLowerCtx<'_> { ) .intern(Interner), ); - let Some(current) = self.lower_call(index_fn_op, Box::new([Operand::Copy(place), index_operand]), result.clone(), current, false, span)? else { + let Some(current) = self.lower_call( + index_fn_op, + Box::new([Operand::Copy(place), index_operand]), + result.clone(), + current, + false, + span, + )? + else { return Ok(None); }; result = result.project(ProjectionElem::Deref); @@ -329,7 +352,15 @@ impl MirLowerCtx<'_> { .intern(Interner), ); let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); - let Some(current) = self.lower_call(deref_fn_op, Box::new([Operand::Copy(ref_place)]), result.clone(), current, false, span)? else { + let Some(current) = self.lower_call( + deref_fn_op, + Box::new([Operand::Copy(ref_place)]), + result.clone(), + current, + false, + span, + )? + else { return Ok(None); }; result = result.project(ProjectionElem::Deref); diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index ff43c64a9e60..3354cbd76a0a 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -307,6 +307,11 @@ impl MirLowerCtx<'_> { mode, )?, None => { + // The path is not a variant, so it is a const + if mode != MatchingMode::Check { + // A const don't bind anything. Only needs check. + return Ok((current, current_else)); + } let unresolved_name = || MirLowerError::unresolved_path(self.db, p); let resolver = self.owner.resolver(self.db.upcast()); let pr = resolver @@ -362,8 +367,8 @@ impl MirLowerCtx<'_> { }, Pat::Lit(l) => match &self.body.exprs[*l] { Expr::Literal(l) => { - let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?; if mode == MatchingMode::Check { + let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?; self.pattern_match_const(current_else, current, c, cond_place, pattern)? } else { (current, current_else) diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index ce3f7a8e5102..c565228d91e7 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -13,15 +13,14 @@ use chalk_ir::{ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, ConstData, DebruijnIndex, }; -use hir_def::{DefWithBodyId, GeneralConstId}; +use hir_def::DefWithBodyId; use triomphe::Arc; use crate::{ - consteval::unknown_const, + consteval::{intern_const_scalar, unknown_const}, db::HirDatabase, from_placeholder_idx, infer::normalize, - method_resolution::lookup_impl_const, utils::{generics, Generics}, ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind, }; @@ -29,8 +28,8 @@ use crate::{ use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind}; macro_rules! not_supported { - ($x: expr) => { - return Err(MirLowerError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirLowerError::NotSupported(format!($it))) }; } @@ -97,16 +96,16 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result, Self::Error> { - let x = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + let it = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { not_supported!("missing idx in generics"); }; Ok(self .subst .as_slice(Interner) .get(idx) - .and_then(|x| x.constant(Interner)) - .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .and_then(|it| it.constant(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))? .clone()) } @@ -115,16 +114,16 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + let it = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { not_supported!("missing idx in generics"); }; Ok(self .subst .as_slice(Interner) .get(idx) - .and_then(|x| x.ty(Interner)) - .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .and_then(|it| it.ty(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))? .clone()) } @@ -180,7 +179,7 @@ impl Filler<'_> { MirLowerError::GenericArgNotProvided( self.generics .as_ref() - .and_then(|x| x.iter().nth(b.index)) + .and_then(|it| it.iter().nth(b.index)) .unwrap() .0, self.subst.clone(), @@ -193,25 +192,12 @@ impl Filler<'_> { | chalk_ir::ConstValue::Placeholder(_) => {} chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { crate::ConstScalar::UnevaluatedConst(const_id, subst) => { - let mut const_id = *const_id; let mut subst = subst.clone(); self.fill_subst(&mut subst)?; - if let GeneralConstId::ConstId(c) = const_id { - let (c, s) = lookup_impl_const( - self.db, - self.db.trait_environment_for_body(self.owner), - c, - subst, - ); - const_id = GeneralConstId::ConstId(c); - subst = s; - } - let result = - self.db.const_eval(const_id.into(), subst).map_err(|e| { - let name = const_id.name(self.db.upcast()); - MirLowerError::ConstEvalError(name, Box::new(e)) - })?; - *c = result; + *c = intern_const_scalar( + crate::ConstScalar::UnevaluatedConst(*const_id, subst), + c.data(Interner).ty.clone(), + ); } crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (), }, diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index ac23e77bd2bb..781ffaecad56 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -135,7 +135,7 @@ impl<'a> MirPrettyCtx<'a> { fn for_closure(&mut self, closure: ClosureId) { let body = match self.db.mir_body_for_closure(closure) { - Ok(x) => x, + Ok(it) => it, Err(e) => { wln!(self, "// error in {closure:?}: {e:?}"); return; @@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> { let indent = mem::take(&mut self.indent); let mut ctx = MirPrettyCtx { body: &body, - local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(), + local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(), result, indent, ..*self @@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> { } fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self { - let local_to_binding = body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(); + let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(); MirPrettyCtx { body, db, @@ -315,17 +315,17 @@ impl<'a> MirPrettyCtx<'a> { } } } - ProjectionElem::TupleOrClosureField(x) => { + ProjectionElem::TupleOrClosureField(it) => { f(this, local, head); - w!(this, ".{}", x); + w!(this, ".{}", it); } ProjectionElem::Index(l) => { f(this, local, head); w!(this, "[{}]", this.local_name(*l).display(this.db)); } - x => { + it => { f(this, local, head); - w!(this, ".{:?}", x); + w!(this, ".{:?}", it); } } } @@ -356,14 +356,14 @@ impl<'a> MirPrettyCtx<'a> { } self.place(p); } - Rvalue::Aggregate(AggregateKind::Tuple(_), x) => { + Rvalue::Aggregate(AggregateKind::Tuple(_), it) => { w!(self, "("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } - Rvalue::Aggregate(AggregateKind::Array(_), x) => { + Rvalue::Aggregate(AggregateKind::Array(_), it) => { w!(self, "["); - self.operand_list(x); + self.operand_list(it); w!(self, "]"); } Rvalue::Repeat(op, len) => { @@ -371,19 +371,19 @@ impl<'a> MirPrettyCtx<'a> { self.operand(op); w!(self, "; {}]", len.display(self.db)); } - Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => { + Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => { w!(self, "Adt("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } - Rvalue::Aggregate(AggregateKind::Closure(_), x) => { + Rvalue::Aggregate(AggregateKind::Closure(_), it) => { w!(self, "Closure("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } - Rvalue::Aggregate(AggregateKind::Union(_, _), x) => { + Rvalue::Aggregate(AggregateKind::Union(_, _), it) => { w!(self, "Union("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } Rvalue::Len(p) => { @@ -428,8 +428,8 @@ impl<'a> MirPrettyCtx<'a> { } } - fn operand_list(&mut self, x: &[Operand]) { - let mut it = x.iter(); + fn operand_list(&mut self, it: &[Operand]) { + let mut it = it.iter(); if let Some(first) = it.next() { self.operand(first); for op in it { diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 857141280061..d22d0d85c8e1 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -30,7 +30,7 @@ use syntax::{ ast::{self, AstNode, HasName}, SyntaxNode, }; -use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; +use tracing_subscriber::{layer::SubscriberExt, Registry}; use tracing_tree::HierarchicalLayer; use triomphe::Arc; @@ -52,7 +52,8 @@ fn setup_tracing() -> Option { return None; } - let filter = EnvFilter::from_env("CHALK_DEBUG"); + let filter: tracing_subscriber::filter::Targets = + env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); let layer = HierarchicalLayer::default() .with_indent_lines(true) .with_ansi(false) @@ -205,7 +206,9 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour let Some(node) = (match expr_or_pat { hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db), hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db), - }) else { continue; }; + }) else { + continue; + }; let range = node.as_ref().original_file_range(&db); let actual = format!( "expected {}, got {}", diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index 111ac0b618eb..b71c457f0152 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -202,11 +202,11 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': IntoIterator::IntoIter 100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter 100..119 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> - 100..119 'for _ ...!() {}': Option>> + 100..119 'for _ ...!() {}': Option> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () - 104..105 '_': Iterator::Item> + 104..105 '_': IntoIterator::Item 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': () @@ -293,11 +293,11 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': IntoIterator::IntoIter 114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter 114..133 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> - 114..133 'for _ ...!() {}': Option>> + 114..133 'for _ ...!() {}': Option> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () - 118..119 '_': Iterator::Item> + 118..119 '_': IntoIterator::Item 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': () diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index 1e57a4ae2968..a8e146b096a4 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -1215,6 +1215,52 @@ fn main() { ); } +#[test] +fn inherent_method_deref_raw() { + check_types( + r#" +struct Val; + +impl Val { + pub fn method(self: *const Val) -> u32 { + 0 + } +} + +fn main() { + let foo: *const Val; + foo.method(); + // ^^^^^^^^^^^^ u32 +} +"#, + ); +} + +#[test] +fn trait_method_deref_raw() { + check_types( + r#" +trait Trait { + fn method(self: *const Self) -> u32; +} + +struct Val; + +impl Trait for Val { + fn method(self: *const Self) -> u32 { + 0 + } +} + +fn main() { + let foo: *const Val; + foo.method(); + // ^^^^^^^^^^^^ u32 +} +"#, + ); +} + #[test] fn method_on_dyn_impl() { check_types( diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 047900a324e9..8b95110233fd 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1240,11 +1240,11 @@ fn test() { 16..66 'for _ ... }': IntoIterator::IntoIter<()> 16..66 'for _ ... }': &mut IntoIterator::IntoIter<()> 16..66 'for _ ... }': fn next>(&mut IntoIterator::IntoIter<()>) -> Option< as Iterator>::Item> - 16..66 'for _ ... }': Option>> + 16..66 'for _ ... }': Option> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () - 20..21 '_': Iterator::Item> + 20..21 '_': IntoIterator::Item<()> 25..39 '{ let x = 0; }': () 31..32 'x': i32 35..36 '0': i32 diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 97ae732a9046..5f5cd794512c 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4148,6 +4148,30 @@ where ); } +#[test] +fn gats_in_bounds_for_assoc() { + check_types( + r#" +trait Trait { + type Assoc: Another = usize>; + type Assoc2: Another = T>; +} +trait Another { + type Gat; + fn foo(&self) -> Self::Gat; + fn bar(&self) -> Self::Gat; +} + +fn test(a: T::Assoc, b: T::Assoc2) { + let v = a.foo(); + //^ usize + let v = b.bar::(); + //^ isize +} +"#, + ); +} + #[test] fn bin_op_with_scalar_fallback() { // Extra impls are significant so that chalk doesn't give us definite guidances. diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index f40b7db3a551..3c7cfbaed3a4 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -170,7 +170,7 @@ fn solve( struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase>); -impl<'a> Drop for LoggingRustIrDatabaseLoggingOnDrop<'a> { +impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> { fn drop(&mut self) { eprintln!("chalk program:\n{}", self.0); } diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index 3636580630df..0c38fe5d6ab3 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -89,7 +89,7 @@ struct SuperTraits<'a> { seen: FxHashSet, } -impl<'a> SuperTraits<'a> { +impl SuperTraits<'_> { fn elaborate(&mut self, trait_ref: &TraitRef) { direct_super_trait_refs(self.db, trait_ref, |trait_ref| { if !self.seen.contains(&trait_ref.trait_id) { @@ -99,7 +99,7 @@ impl<'a> SuperTraits<'a> { } } -impl<'a> Iterator for SuperTraits<'a> { +impl Iterator for SuperTraits<'_> { type Item = TraitRef; fn next(&mut self) -> Option { diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index a20aff93f19b..f860ee948452 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -18,7 +18,7 @@ arrayvec = "0.7.2" itertools = "0.10.5" smallvec.workspace = true triomphe.workspace = true -once_cell = "1.17.0" +once_cell = "1.17.1" # local deps base-db.workspace = true diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index b817937296d2..cf8db2a5a24a 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -141,6 +141,7 @@ fn resolve_doc_path( AttrDefId::ImplId(it) => it.resolver(db.upcast()), AttrDefId::ExternBlockId(it) => it.resolver(db.upcast()), AttrDefId::MacroId(it) => it.resolver(db.upcast()), + AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()), AttrDefId::GenericParamId(it) => match it { GenericParamId::TypeParamId(it) => it.parent(), GenericParamId::ConstParamId(it) => it.parent(), diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index e0cde689fed8..f3a0608944b6 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs @@ -5,9 +5,9 @@ //! But we need this for at least LRU caching at the query level. pub use hir_def::db::*; pub use hir_expand::db::{ - AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery, - InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, - ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, + AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage, + ExpandProcMacroQuery, HygieneFrameQuery, InternMacroCallQuery, MacroArgNodeQuery, + MacroExpandQuery, ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, }; pub use hir_ty::db::*; diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index b64d81490bb1..80c3bcdca8b7 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -3,7 +3,7 @@ //! //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. -pub use hir_ty::diagnostics::{IncoherentImpl, IncorrectCase}; +pub use hir_ty::diagnostics::{CaseType, IncoherentImpl, IncorrectCase}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 9a2090ab79a2..4de9c872ad6f 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -251,8 +251,8 @@ impl HirDisplay for GenericParam { impl HirDisplay for TypeOrConstParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self.split(f.db) { - either::Either::Left(x) => x.hir_fmt(f), - either::Either::Right(x) => x.hir_fmt(f), + either::Either::Left(it) => it.hir_fmt(f), + either::Either::Right(it) => it.hir_fmt(f), } } } @@ -303,11 +303,11 @@ fn write_generic_params( ) -> Result<(), HirDisplayError> { let params = f.db.generic_params(def); if params.lifetimes.is_empty() - && params.type_or_consts.iter().all(|x| x.1.const_param().is_none()) + && params.type_or_consts.iter().all(|it| it.1.const_param().is_none()) && params .type_or_consts .iter() - .filter_map(|x| x.1.type_param()) + .filter_map(|it| it.1.type_param()) .all(|param| !matches!(param.provenance, TypeParamProvenance::TypeParamList)) { return Ok(()); diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 9f6b5c0a9fcc..b46a3856d454 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -1,12 +1,13 @@ //! Provides set of implementation for hir's objects that allows get back location in file. +use base_db::FileId; use either::Either; use hir_def::{ nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource as _}, Lookup, MacroId, VariantId, }; -use hir_expand::InFile; +use hir_expand::{HirFileId, InFile}; use syntax::ast; use crate::{ @@ -20,6 +21,10 @@ pub trait HasSource { /// Fetches the definition's source node. /// Using [`crate::Semantics::source`] is preferred when working with [`crate::Semantics`], /// as that caches the parsed file in the semantics' cache. + /// + /// The current some implementations can return `InFile` instead of `Option`. + /// But we made this method `Option` to support rlib in the future + /// by https://github.com/rust-lang/rust-analyzer/issues/6913 fn source(self, db: &dyn HirDatabase) -> Option>; } @@ -32,6 +37,11 @@ impl Module { def_map[self.id.local_id].definition_source(db.upcast()) } + pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId { + let def_map = self.id.def_map(db.upcast()); + def_map[self.id.local_id].definition_source_file_id() + } + pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool { let def_map = self.id.def_map(db.upcast()); match def_map[self.id.local_id].origin { @@ -40,6 +50,16 @@ impl Module { } } + pub fn as_source_file_id(self, db: &dyn HirDatabase) -> Option { + let def_map = self.id.def_map(db.upcast()); + match def_map[self.id.local_id].origin { + ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition, .. } => { + Some(definition) + } + _ => None, + } + } + pub fn is_inline(self, db: &dyn HirDatabase) -> bool { let def_map = self.id.def_map(db.upcast()); def_map[self.id.local_id].origin.is_inline() diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 6df625380fff..f8d9398ae2c5 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -47,7 +47,7 @@ use hir_def::{ lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, macro_id_to_def_id, - nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, + nameres::{self, diagnostics::DefDiagnostic}, per_ns::PerNs, resolver::{HasResolver, Resolver}, src::HasSource as _, @@ -62,7 +62,6 @@ use hir_ty::{ all_super_traits, autoderef, consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, - display::HexifiedConst, layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, @@ -89,11 +88,11 @@ use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ attrs::{HasAttrs, Namespace}, diagnostics::{ - AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, - IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError, - MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, - MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, - ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, + AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, + IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, + MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields, + MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, + PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, @@ -505,15 +504,10 @@ impl Module { /// Finds nearest non-block ancestor `Module` (`self` included). pub fn nearest_non_block_module(self, db: &dyn HirDatabase) -> Module { let mut id = self.id; - loop { - let def_map = id.def_map(db.upcast()); - let origin = def_map[id.local_id].origin; - if matches!(origin, ModuleOrigin::BlockExpr { .. }) { - id = id.containing_module(db.upcast()).expect("block without parent module") - } else { - return Module { id }; - } + while id.is_block_module() { + id = id.containing_module(db.upcast()).expect("block without parent module"); } + Module { id } } pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec { @@ -619,15 +613,21 @@ impl Module { let inherent_impls = db.inherent_impls_in_crate(self.id.krate()); for impl_def in self.impl_defs(db) { + let loc = impl_def.id.lookup(db.upcast()); + let tree = loc.id.item_tree(db.upcast()); + let node = &tree[loc.id.value]; + let file_id = loc.id.file_id(); + if file_id.is_builtin_derive(db.upcast()) { + // these expansion come from us, diagnosing them is a waste of resources + // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow + continue; + } + for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() { emit_def_diagnostic(db, acc, diag); } if inherent_impls.invalid_impls().contains(&impl_def.id) { - let loc = impl_def.id.lookup(db.upcast()); - let tree = loc.id.item_tree(db.upcast()); - let node = &tree[loc.id.value]; - let file_id = loc.id.file_id(); let ast_id_map = db.ast_id_map(file_id); acc.push(IncoherentImpl { impl_: ast_id_map.get(node.ast_id()), file_id }.into()) @@ -698,16 +698,18 @@ impl Module { fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec, m: Macro) { let id = macro_id_to_def_id(db.upcast(), m.id); - if let Err(e) = db.macro_def(id) { - let Some(ast) = id.ast_id().left() else { - never!("MacroDefError for proc-macro: {:?}", e); + if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) { + if let Some(e) = expander.mac.err() { + let Some(ast) = id.ast_id().left() else { + never!("declarative expander for non decl-macro: {:?}", e); return; }; - emit_def_diagnostic_( - db, - acc, - &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, - ); + emit_def_diagnostic_( + db, + acc, + &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, + ); + } } } @@ -753,7 +755,7 @@ fn emit_def_diagnostic_( let item = ast.to_node(db.upcast()); acc.push( InactiveCode { - node: ast.with_value(AstPtr::new(&item).into()), + node: ast.with_value(SyntaxNodePtr::new(&item).into()), cfg: cfg.clone(), opts: opts.clone(), } @@ -1234,7 +1236,7 @@ impl Adt { pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { let subst = db.generic_defaults(self.into()); subst.iter().any(|ty| match ty.skip_binders().data(Interner) { - GenericArgData::Ty(x) => x.is_unknown(), + GenericArgData::Ty(it) => it.is_unknown(), _ => false, }) } @@ -1635,11 +1637,11 @@ impl DefWithBody { for moof in &borrowck_result.moved_out_of_ref { let span: InFile = match moof.span { mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { - Ok(s) => s.map(|x| x.into()), + Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) { - Ok(s) => s.map(|x| match x { + Ok(s) => s.map(|it| match it { Either::Left(e) => e.into(), Either::Right(e) => e.into(), }), @@ -1661,6 +1663,14 @@ impl DefWithBody { let Some(&local) = mir_body.binding_locals.get(binding_id) else { continue; }; + if body[binding_id] + .definitions + .iter() + .any(|&pat| source_map.pat_syntax(pat).is_err()) + { + // Skip synthetic bindings + continue; + } let need_mut = &mol[local]; let local = Local { parent: self.into(), binding_id }; match (need_mut, local.is_mut(db)) { @@ -1670,11 +1680,11 @@ impl DefWithBody { for span in spans { let span: InFile = match span { mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|x| x.into()), + Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|x| match x { + Ok(s) => s.map(|it| match it { Either::Left(e) => e.into(), Either::Right(e) => e.into(), }), @@ -1687,7 +1697,7 @@ impl DefWithBody { } (mir::MutabilityReason::Not, true) => { if !infer.mutated_bindings_in_closure.contains(&binding_id) { - let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_")); + let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with("_")); if !should_ignore { acc.push(UnusedMut { local }.into()) } @@ -1919,6 +1929,21 @@ impl Function { db.function_data(self.id).has_async_kw() } + /// Does this function have `#[test]` attribute? + pub fn is_test(self, db: &dyn HirDatabase) -> bool { + db.function_data(self.id).attrs.is_test() + } + + /// Does this function have the ignore attribute? + pub fn is_ignore(self, db: &dyn HirDatabase) -> bool { + db.function_data(self.id).attrs.is_ignore() + } + + /// Does this function have `#[bench]` attribute? + pub fn is_bench(self, db: &dyn HirDatabase) -> bool { + db.function_data(self.id).attrs.is_bench() + } + pub fn is_unsafe_to_call(self, db: &dyn HirDatabase) -> bool { hir_ty::is_fn_unsafe_to_call(db, self.id) } @@ -1962,7 +1987,7 @@ impl Function { return r; } }; - let (result, stdout, stderr) = interpret_mir(db, &body, false); + let (result, stdout, stderr) = interpret_mir(db, body, false); let mut text = match result { Ok(_) => "pass".to_string(), Err(e) => { @@ -2132,7 +2157,27 @@ impl Const { pub fn render_eval(self, db: &dyn HirDatabase) -> Result { let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?; - let r = format!("{}", HexifiedConst(c).display(db)); + let data = &c.data(Interner); + if let TyKind::Scalar(s) = data.ty.kind(Interner) { + if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) { + if let hir_ty::ConstValue::Concrete(c) = &data.value { + if let hir_ty::ConstScalar::Bytes(b, _) = &c.interned { + let value = u128::from_le_bytes(mir::pad16(b, false)); + let value_signed = + i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_)))); + if value >= 10 { + return Ok(format!("{} ({:#X})", value_signed, value)); + } else { + return Ok(format!("{}", value_signed)); + } + } + } + } + } + if let Ok(s) = mir::render_const_using_debug_impl(db, self.id, &c) { + return Ok(s); + } + let r = format!("{}", c.display(db)); return Ok(r); } } @@ -2270,7 +2315,7 @@ impl TypeAlias { pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { let subst = db.generic_defaults(self.id.into()); subst.iter().any(|ty| match ty.skip_binders().data(Interner) { - GenericArgData::Ty(x) => x.is_unknown(), + GenericArgData::Ty(it) => it.is_unknown(), _ => false, }) } @@ -2660,8 +2705,8 @@ impl GenericDef { let ty_params = generics.type_or_consts.iter().map(|(local_id, _)| { let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: self.into(), local_id } }; match toc.split(db) { - Either::Left(x) => GenericParam::ConstParam(x), - Either::Right(x) => GenericParam::TypeParam(x), + Either::Left(it) => GenericParam::ConstParam(it), + Either::Right(it) => GenericParam::TypeParam(it), } }); self.lifetime_params(db) @@ -2709,14 +2754,14 @@ pub struct LocalSource { impl LocalSource { pub fn as_ident_pat(&self) -> Option<&ast::IdentPat> { match &self.source.value { - Either::Left(x) => Some(x), + Either::Left(it) => Some(it), Either::Right(_) => None, } } pub fn into_ident_pat(self) -> Option { match self.source.value { - Either::Left(x) => Some(x), + Either::Left(it) => Some(it), Either::Right(_) => None, } } @@ -2738,7 +2783,7 @@ impl LocalSource { } pub fn syntax_ptr(self) -> InFile { - self.source.map(|x| SyntaxNodePtr::new(x.syntax())) + self.source.map(|it| SyntaxNodePtr::new(it.syntax())) } } @@ -2797,13 +2842,13 @@ impl Local { Type::new(db, def, ty) } - /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;` + /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` pub fn sources(self, db: &dyn HirDatabase) -> Vec { let (body, source_map) = db.body_with_source_map(self.parent); self.sources_(db, &body, &source_map).collect() } - /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;` + /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = it;` pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource { let (body, source_map) = db.body_with_source_map(self.parent); let src = self.sources_(db, &body, &source_map).next().unwrap(); @@ -3057,7 +3102,9 @@ impl TypeParam { let subst = TyBuilder::placeholder_subst(db, self.id.parent()); let ty = ty.substitute(Interner, &subst); match ty.data(Interner) { - GenericArgData::Ty(x) => Some(Type::new_with_resolver_inner(db, &resolver, x.clone())), + GenericArgData::Ty(it) => { + Some(Type::new_with_resolver_inner(db, &resolver, it.clone())) + } _ => None, } } @@ -3096,7 +3143,7 @@ impl ConstParam { pub fn name(self, db: &dyn HirDatabase) -> Name { let params = db.generic_params(self.id.parent()); match params.type_or_consts[self.id.local_id()].name() { - Some(x) => x.clone(), + Some(it) => it.clone(), None => { never!(); Name::missing() @@ -3153,8 +3200,8 @@ impl TypeOrConstParam { pub fn ty(self, db: &dyn HirDatabase) -> Type { match self.split(db) { - Either::Left(x) => x.ty(db), - Either::Right(x) => x.ty(db), + Either::Left(it) => it.ty(db), + Either::Right(it) => it.ty(db), } } } @@ -3260,9 +3307,9 @@ impl Impl { self.id.lookup(db.upcast()).container.into() } - pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option> { + pub fn as_builtin_derive(self, db: &dyn HirDatabase) -> Option> { let src = self.source(db)?; - src.file_id.is_builtin_derive(db.upcast()) + src.file_id.as_builtin_derive_attr_node(db.upcast()) } } @@ -3652,9 +3699,9 @@ impl Type { }; let parent_subst = TyBuilder::subst_for_def(db, trait_id, None) .push(self.ty.clone()) - .fill(|x| { + .fill(|it| { // FIXME: this code is not covered in tests. - match x { + match it { ParamKind::Type => { GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner) } @@ -3821,7 +3868,7 @@ impl Type { pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> { if let TyKind::Array(ty, len) = &self.ty.kind(Interner) { - try_const_usize(db, len).map(|x| (self.derived(ty.clone()), x as usize)) + try_const_usize(db, len).map(|it| (self.derived(ty.clone()), it as usize)) } else { None } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 5a76a9185a2a..39a3e1c4489e 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1494,7 +1494,11 @@ impl<'db> SemanticsImpl<'db> { } fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool { - let Some(enclosing_item) = expr.syntax().ancestors().find_map(Either::::cast) else { return false }; + let Some(enclosing_item) = + expr.syntax().ancestors().find_map(Either::::cast) + else { + return false; + }; let def = match &enclosing_item { Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true, @@ -1631,7 +1635,7 @@ pub struct SemanticsScope<'a> { resolver: Resolver, } -impl<'a> SemanticsScope<'a> { +impl SemanticsScope<'_> { pub fn module(&self) -> Module { Module { id: self.resolver.module() } } diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index c50ffa4f8b70..b971ca62387e 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -298,7 +298,7 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn type_param_to_def(&mut self, src: InFile) -> Option { let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into(); let dyn_map = self.cache_for(container, src.file_id); - dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(|x| TypeParamId::from_unchecked(x)) + dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(|it| TypeParamId::from_unchecked(it)) } pub(super) fn lifetime_param_to_def( @@ -316,7 +316,10 @@ impl SourceToDefCtx<'_, '_> { ) -> Option { let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into(); let dyn_map = self.cache_for(container, src.file_id); - dyn_map[keys::CONST_PARAM].get(&src.value).copied().map(|x| ConstParamId::from_unchecked(x)) + dyn_map[keys::CONST_PARAM] + .get(&src.value) + .copied() + .map(|it| ConstParamId::from_unchecked(it)) } pub(super) fn generic_param_to_def( diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index ecb1b306a668..3499daf11403 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -832,7 +832,7 @@ impl SourceAnalyzer { None => return func, }; let env = db.trait_environment_for_body(owner); - method_resolution::lookup_impl_method(db, env, func, substs).0 + db.lookup_impl_method(env, func, substs).0 } fn resolve_impl_const_or_trait_def( diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 7384390f28b5..ac0b74ee8e74 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -8,10 +8,7 @@ use itertools::Itertools; use syntax::ast::edit_in_place::Removable; use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat}; -use crate::{ - utils::{self, render_snippet, Cursor}, - AssistContext, AssistId, AssistKind, Assists, -}; +use crate::{utils, AssistContext, AssistId, AssistKind, Assists}; // Assist: add_missing_match_arms // @@ -75,14 +72,18 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .collect(); let module = ctx.sema.scope(expr.syntax())?.module(); - let (mut missing_pats, is_non_exhaustive): ( + let (mut missing_pats, is_non_exhaustive, has_hidden_variants): ( Peekable>>, bool, + bool, ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate()); let variants = enum_def.variants(ctx.db()); + let has_hidden_variants = + variants.iter().any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); + let missing_pats = variants .into_iter() .filter_map(|variant| { @@ -101,7 +102,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } else { Box::new(missing_pats) }; - (missing_pats.peekable(), is_non_exhaustive) + (missing_pats.peekable(), is_non_exhaustive, has_hidden_variants) } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { let is_non_exhaustive = enum_defs.iter().any(|enum_def| enum_def.is_non_exhaustive(ctx.db(), module.krate())); @@ -124,6 +125,12 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) if n_arms > 256 { return None; } + + let has_hidden_variants = variants_of_enums + .iter() + .flatten() + .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); + let missing_pats = variants_of_enums .into_iter() .multi_cartesian_product() @@ -139,7 +146,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); - ((Box::new(missing_pats) as Box>).peekable(), is_non_exhaustive) + ( + (Box::new(missing_pats) as Box>).peekable(), + is_non_exhaustive, + has_hidden_variants, + ) } else if let Some((enum_def, len)) = resolve_array_of_enum_def(&ctx.sema, &expr) { let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate()); let variants = enum_def.variants(ctx.db()); @@ -148,6 +159,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) return None; } + let has_hidden_variants = + variants.iter().any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); + let variants_of_enums = vec![variants; len]; let missing_pats = variants_of_enums @@ -164,14 +178,20 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); - ((Box::new(missing_pats) as Box>).peekable(), is_non_exhaustive) + ( + (Box::new(missing_pats) as Box>).peekable(), + is_non_exhaustive, + has_hidden_variants, + ) } else { return None; }; let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm; - if !needs_catch_all_arm && missing_pats.peek().is_none() { + if !needs_catch_all_arm + && ((has_hidden_variants && has_catch_all_arm) || missing_pats.peek().is_none()) + { return None; } @@ -179,13 +199,21 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) AssistId("add_missing_match_arms", AssistKind::QuickFix), "Fill match arms", target_range, - |builder| { + |edit| { let new_match_arm_list = match_arm_list.clone_for_update(); + + // having any hidden variants means that we need a catch-all arm + needs_catch_all_arm |= has_hidden_variants; + let missing_arms = missing_pats - .map(|(pat, hidden)| { - (make::match_arm(iter::once(pat), None, make::ext::expr_todo()), hidden) + .filter(|(_, hidden)| { + // filter out hidden patterns because they're handled by the catch-all arm + !hidden }) - .map(|(it, hidden)| (it.clone_for_update(), hidden)); + .map(|(pat, _)| { + make::match_arm(iter::once(pat), None, make::ext::expr_todo()) + .clone_for_update() + }); let catch_all_arm = new_match_arm_list .arms() @@ -204,15 +232,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) cov_mark::hit!(add_missing_match_arms_empty_expr); } } + let mut first_new_arm = None; - for (arm, hidden) in missing_arms { - if hidden { - needs_catch_all_arm = !has_catch_all_arm; - } else { - first_new_arm.get_or_insert_with(|| arm.clone()); - new_match_arm_list.add_arm(arm); - } + for arm in missing_arms { + first_new_arm.get_or_insert_with(|| arm.clone()); + new_match_arm_list.add_arm(arm); } + if needs_catch_all_arm && !has_catch_all_arm { cov_mark::hit!(added_wildcard_pattern); let arm = make::match_arm( @@ -225,24 +251,39 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) new_match_arm_list.add_arm(arm); } - let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; - match (first_new_arm, ctx.config.snippet_cap) { - (Some(first_new_arm), Some(cap)) => { - let extend_lifetime; - let cursor = - match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast) - { - Some(it) => { - extend_lifetime = it.syntax().clone(); - Cursor::Replace(&extend_lifetime) - } - None => Cursor::Before(first_new_arm.syntax()), - }; - let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor); - builder.replace_snippet(cap, old_range, snippet); + if let (Some(first_new_arm), Some(cap)) = (first_new_arm, ctx.config.snippet_cap) { + match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast) { + Some(it) => edit.add_placeholder_snippet(cap, it), + None => edit.add_tabstop_before(cap, first_new_arm), } - _ => builder.replace(old_range, new_match_arm_list.to_string()), } + + // FIXME: Hack for mutable syntax trees not having great support for macros + // Just replace the element that the original range came from + let old_place = { + // Find the original element + let old_file_range = ctx.sema.original_range(match_arm_list.syntax()); + let file = ctx.sema.parse(old_file_range.file_id); + let old_place = file.syntax().covering_element(old_file_range.range); + + // Make `old_place` mut + match old_place { + syntax::SyntaxElement::Node(it) => { + syntax::SyntaxElement::from(edit.make_syntax_mut(it)) + } + syntax::SyntaxElement::Token(it) => { + // Don't have a way to make tokens mut, so instead make the parent mut + // and find the token again + let parent = edit.make_syntax_mut(it.parent().unwrap()); + let mut_token = + parent.covering_element(it.text_range()).into_token().unwrap(); + + syntax::SyntaxElement::from(mut_token) + } + } + }; + + syntax::ted::replace(old_place, new_match_arm_list.syntax()); }, ) } @@ -1621,10 +1662,9 @@ pub enum E { #[doc(hidden)] A, } ); } - // FIXME: I don't think the assist should be applicable in this case #[test] fn does_not_fill_wildcard_with_wildcard() { - check_assist( + check_assist_not_applicable( add_missing_match_arms, r#" //- /main.rs crate:main deps:e @@ -1635,13 +1675,6 @@ fn foo(t: ::e::E) { } //- /e.rs crate:e pub enum E { #[doc(hidden)] A, } -"#, - r#" -fn foo(t: ::e::E) { - match t { - _ => todo!(), - } -} "#, ); } @@ -1777,7 +1810,7 @@ fn foo(t: ::e::E, b: bool) { #[test] fn does_not_fill_wildcard_with_partial_wildcard_and_wildcard() { - check_assist( + check_assist_not_applicable( add_missing_match_arms, r#" //- /main.rs crate:main deps:e @@ -1789,14 +1822,6 @@ fn foo(t: ::e::E, b: bool) { } //- /e.rs crate:e pub enum E { #[doc(hidden)] A, }"#, - r#" -fn foo(t: ::e::E, b: bool) { - match t { - _ if b => todo!(), - _ => todo!(), - } -} -"#, ); } diff --git a/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/crates/ide-assists/src/handlers/desugar_doc_comment.rs index 226a5dd9fa8b..ddc8a50ed400 100644 --- a/crates/ide-assists/src/handlers/desugar_doc_comment.rs +++ b/crates/ide-assists/src/handlers/desugar_doc_comment.rs @@ -27,7 +27,9 @@ use crate::{ pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let comment = ctx.find_token_at_offset::()?; // Only allow doc comments - let Some(placement) = comment.kind().doc else { return None; }; + let Some(placement) = comment.kind().doc else { + return None; + }; // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs index 5c435dd9c296..3aff5c9144fe 100644 --- a/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{AssocItem, HasVisibility, Module, ModuleDef, Name, PathResolution, ScopeDef}; +use hir::{AssocItem, Enum, HasVisibility, Module, ModuleDef, Name, PathResolution, ScopeDef}; use ide_db::{ defs::{Definition, NameRefClass}, search::SearchScope, @@ -45,7 +45,8 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> let use_tree = star.parent().and_then(ast::UseTree::cast)?; let (parent, mod_path) = find_parent_and_path(&star)?; let target_module = match ctx.sema.resolve_path(&mod_path)? { - PathResolution::Def(ModuleDef::Module(it)) => it, + PathResolution::Def(ModuleDef::Module(it)) => Expandable::Module(it), + PathResolution::Def(ModuleDef::Adt(hir::Adt::Enum(e))) => Expandable::Enum(e), _ => return None, }; @@ -90,6 +91,11 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } +enum Expandable { + Module(Module), + Enum(Enum), +} + fn find_parent_and_path( star: &SyntaxToken, ) -> Option<(Either, ast::Path)> { @@ -168,23 +174,59 @@ impl Refs { } } -fn find_refs_in_mod(ctx: &AssistContext<'_>, module: Module, visible_from: Module) -> Option { - if !is_mod_visible_from(ctx, module, visible_from) { +fn find_refs_in_mod( + ctx: &AssistContext<'_>, + expandable: Expandable, + visible_from: Module, +) -> Option { + if !is_expandable_visible_from(ctx, &expandable, visible_from) { return None; } - let module_scope = module.scope(ctx.db(), Some(visible_from)); - let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect(); - Some(Refs(refs)) + match expandable { + Expandable::Module(module) => { + let module_scope = module.scope(ctx.db(), Some(visible_from)); + let refs = + module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect(); + Some(Refs(refs)) + } + Expandable::Enum(enm) => Some(Refs( + enm.variants(ctx.db()) + .into_iter() + .map(|v| Ref { visible_name: v.name(ctx.db()), def: Definition::Variant(v) }) + .collect(), + )), + } } -fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool { - match module.parent(ctx.db()) { - Some(parent) => { - module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) - && is_mod_visible_from(ctx, parent, from) +fn is_expandable_visible_from( + ctx: &AssistContext<'_>, + expandable: &Expandable, + from: Module, +) -> bool { + fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool { + match module.parent(ctx.db()) { + Some(parent) => { + module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) + && is_mod_visible_from(ctx, parent, from) + } + None => true, + } + } + + match expandable { + Expandable::Module(module) => match module.parent(ctx.db()) { + Some(parent) => { + module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) + && is_mod_visible_from(ctx, parent, from) + } + None => true, + }, + Expandable::Enum(enm) => { + let module = enm.module(ctx.db()); + enm.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) + && is_mod_visible_from(ctx, module, from) } - None => true, } } @@ -897,4 +939,98 @@ struct Baz { ", ); } + + #[test] + fn test_support_for_enums() { + check_assist( + expand_glob_import, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +use foo::Foo; +use foo::Foo::*$0; + +struct Strukt { + bar: Foo, +} + +fn main() { + let s: Strukt = Strukt { bar: Bar }; +}"#, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +use foo::Foo; +use foo::Foo::Bar; + +struct Strukt { + bar: Foo, +} + +fn main() { + let s: Strukt = Strukt { bar: Bar }; +}"#, + ) + } + + #[test] + fn test_expanding_multiple_variants_at_once() { + check_assist( + expand_glob_import, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +mod abc { + use super::foo; + use super::foo::Foo::*$0; + + struct Strukt { + baz: foo::Foo, + bar: foo::Foo, + } + + fn trying_calling() { + let s: Strukt = Strukt { bar: Bar , baz : Baz }; + } + +}"#, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +mod abc { + use super::foo; + use super::foo::Foo::{Bar, Baz}; + + struct Strukt { + baz: foo::Foo, + bar: foo::Foo, + } + + fn trying_calling() { + let s: Strukt = Strukt { bar: Bar , baz : Baz }; + } + +}"#, + ) + } } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 2a67909e6371..e9db38aca0f0 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1360,14 +1360,15 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St } format_to!(buf, "{expr}"); - let insert_comma = fun - .body - .parent() - .and_then(ast::MatchArm::cast) - .map_or(false, |it| it.comma_token().is_none()); + let parent_match_arm = fun.body.parent().and_then(ast::MatchArm::cast); + let insert_comma = parent_match_arm.as_ref().is_some_and(|it| it.comma_token().is_none()); + if insert_comma { buf.push(','); - } else if fun.ret_ty.is_unit() && (!fun.outliving_locals.is_empty() || !expr.is_block_like()) { + } else if parent_match_arm.is_none() + && fun.ret_ty.is_unit() + && (!fun.outliving_locals.is_empty() || !expr.is_block_like()) + { buf.push(';'); } buf @@ -4611,6 +4612,29 @@ fn $0fun_name() -> i32 { } "#, ); + + // Makes sure no semicolon is added for unit-valued match arms + check_assist( + extract_function, + r#" +fn main() { + match () { + _ => $0()$0, + } +} +"#, + r#" +fn main() { + match () { + _ => fun_name(), + } +} + +fn $0fun_name() { + () +} +"#, + ) } #[test] diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index d6c59a9c8295..c9f272474e7e 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -1,11 +1,11 @@ use hir::{db::HirDatabase, HasSource, HasVisibility, ModuleDef, PathResolution, ScopeDef}; use ide_db::base_db::FileId; use syntax::{ - ast::{self, HasVisibility as _}, - AstNode, TextRange, TextSize, + ast::{self, edit_in_place::HasVisibilityEdit, make, HasVisibility as _}, + AstNode, TextRange, }; -use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // FIXME: this really should be a fix for diagnostic, rather than an assist. @@ -40,12 +40,16 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let qualifier = path.qualifier()?; let name_ref = path.segment()?.name_ref()?; let qualifier_res = ctx.sema.resolve_path(&qualifier)?; - let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { return None; }; + let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { + return None; + }; let (_, def) = module .scope(ctx.db(), None) .into_iter() .find(|(name, _)| name.to_smol_str() == name_ref.text().as_str())?; - let ScopeDef::ModuleDef(def) = def else { return None; }; + let ScopeDef::ModuleDef(def) = def else { + return None; + }; let current_module = ctx.sema.scope(path.syntax())?.module(); let target_module = def.module(ctx.db())?; @@ -54,11 +58,13 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) return None; }; - let (offset, current_visibility, target, target_file, target_name) = - target_data_for_def(ctx.db(), def)?; + let (vis_owner, target, target_file, target_name) = target_data_for_def(ctx.db(), def)?; - let missing_visibility = - if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; + let missing_visibility = if current_module.krate() == target_module.krate() { + make::visibility_pub_crate() + } else { + make::visibility_pub() + }; let assist_label = match target_name { None => format!("Change visibility to {missing_visibility}"), @@ -67,23 +73,14 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) } }; - acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { - builder.edit_file(target_file); - match ctx.config.snippet_cap { - Some(cap) => match current_visibility { - Some(current_visibility) => builder.replace_snippet( - cap, - current_visibility.syntax().text_range(), - format!("$0{missing_visibility}"), - ), - None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), - }, - None => match current_visibility { - Some(current_visibility) => { - builder.replace(current_visibility.syntax().text_range(), missing_visibility) - } - None => builder.insert(offset, format!("{missing_visibility} ")), - }, + acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |edit| { + edit.edit_file(target_file); + + let vis_owner = edit.make_mut(vis_owner); + vis_owner.set_visibility(missing_visibility.clone_for_update()); + + if let Some((cap, vis)) = ctx.config.snippet_cap.zip(vis_owner.visibility()) { + edit.add_tabstop_before(cap, vis); } }) } @@ -103,19 +100,22 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> let target_module = parent.module(ctx.db()); let in_file_source = record_field_def.source(ctx.db())?; - let (offset, current_visibility, target) = match in_file_source.value { + let (vis_owner, target) = match in_file_source.value { hir::FieldSource::Named(it) => { - let s = it.syntax(); - (vis_offset(s), it.visibility(), s.text_range()) + let range = it.syntax().text_range(); + (ast::AnyHasVisibility::new(it), range) } hir::FieldSource::Pos(it) => { - let s = it.syntax(); - (vis_offset(s), it.visibility(), s.text_range()) + let range = it.syntax().text_range(); + (ast::AnyHasVisibility::new(it), range) } }; - let missing_visibility = - if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; + let missing_visibility = if current_module.krate() == target_module.krate() { + make::visibility_pub_crate() + } else { + make::visibility_pub() + }; let target_file = in_file_source.file_id.original_file(ctx.db()); let target_name = record_field_def.name(ctx.db()); @@ -125,23 +125,14 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> target_name.display(ctx.db()) ); - acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { - builder.edit_file(target_file); - match ctx.config.snippet_cap { - Some(cap) => match current_visibility { - Some(current_visibility) => builder.replace_snippet( - cap, - current_visibility.syntax().text_range(), - format!("$0{missing_visibility}"), - ), - None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), - }, - None => match current_visibility { - Some(current_visibility) => { - builder.replace(current_visibility.syntax().text_range(), missing_visibility) - } - None => builder.insert(offset, format!("{missing_visibility} ")), - }, + acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |edit| { + edit.edit_file(target_file); + + let vis_owner = edit.make_mut(vis_owner); + vis_owner.set_visibility(missing_visibility.clone_for_update()); + + if let Some((cap, vis)) = ctx.config.snippet_cap.zip(vis_owner.visibility()) { + edit.add_tabstop_before(cap, vis); } }) } @@ -149,11 +140,11 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> fn target_data_for_def( db: &dyn HirDatabase, def: hir::ModuleDef, -) -> Option<(TextSize, Option, TextRange, FileId, Option)> { +) -> Option<(ast::AnyHasVisibility, TextRange, FileId, Option)> { fn offset_target_and_file_id( db: &dyn HirDatabase, x: S, - ) -> Option<(TextSize, Option, TextRange, FileId)> + ) -> Option<(ast::AnyHasVisibility, TextRange, FileId)> where S: HasSource, Ast: AstNode + ast::HasVisibility, @@ -161,18 +152,12 @@ fn target_data_for_def( let source = x.source(db)?; let in_file_syntax = source.syntax(); let file_id = in_file_syntax.file_id; - let syntax = in_file_syntax.value; - let current_visibility = source.value.visibility(); - Some(( - vis_offset(syntax), - current_visibility, - syntax.text_range(), - file_id.original_file(db.upcast()), - )) + let range = in_file_syntax.value.text_range(); + Some((ast::AnyHasVisibility::new(source.value), range, file_id.original_file(db.upcast()))) } let target_name; - let (offset, current_visibility, target, target_file) = match def { + let (offset, target, target_file) = match def { hir::ModuleDef::Function(f) => { target_name = Some(f.name(db)); offset_target_and_file_id(db, f)? @@ -209,8 +194,8 @@ fn target_data_for_def( target_name = m.name(db); let in_file_source = m.declaration_source(db)?; let file_id = in_file_source.file_id.original_file(db.upcast()); - let syntax = in_file_source.value.syntax(); - (vis_offset(syntax), in_file_source.value.visibility(), syntax.text_range(), file_id) + let range = in_file_source.value.syntax().text_range(); + (ast::AnyHasVisibility::new(in_file_source.value), range, file_id) } // FIXME hir::ModuleDef::Macro(_) => return None, @@ -218,7 +203,7 @@ fn target_data_for_def( hir::ModuleDef::Variant(_) | hir::ModuleDef::BuiltinType(_) => return None, }; - Some((offset, current_visibility, target, target_file, target_name)) + Some((offset, target, target_file, target_name)) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index b68c766e6477..31fc69562c95 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -1,13 +1,17 @@ use std::collections::HashSet; use hir::{self, HasCrate, HasSource, HasVisibility}; -use syntax::ast::{self, make, AstNode, HasGenericParams, HasName, HasVisibility as _}; +use syntax::{ + ast::{ + self, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, HasVisibility as _, + }, + ted, +}; use crate::{ - utils::{convert_param_list_to_arg_list, find_struct_impl, render_snippet, Cursor}, + utils::{convert_param_list_to_arg_list, find_struct_impl}, AssistContext, AssistId, AssistKind, Assists, GroupLabel, }; -use syntax::ast::edit::AstNodeEdit; // Assist: generate_delegate_methods // @@ -88,13 +92,15 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let adt = ast::Adt::Struct(strukt.clone()); let name = name.display(ctx.db()).to_string(); // if `find_struct_impl` returns None, that means that a function named `name` already exists. - let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; }; + let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { + continue; + }; acc.add_group( &GroupLabel("Generate delegate methods…".to_owned()), AssistId("generate_delegate_methods", AssistKind::Generate), format!("Generate delegate for `{field_name}.{name}()`",), target, - |builder| { + |edit| { // Create the function let method_source = match method.source(ctx.db()) { Some(source) => source.value, @@ -133,36 +139,12 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' is_const, is_unsafe, ) - .indent(ast::edit::IndentLevel(1)) .clone_for_update(); - let cursor = Cursor::Before(f.syntax()); - - // Create or update an impl block, attach the function to it, - // then insert into our code. - match impl_def { - Some(impl_def) => { - // Remember where in our source our `impl` block lives. - let impl_def = impl_def.clone_for_update(); - let old_range = impl_def.syntax().text_range(); - - // Attach the function to the impl block - let assoc_items = impl_def.get_or_create_assoc_item_list(); - assoc_items.add_item(f.clone().into()); - - // Update the impl block. - match ctx.config.snippet_cap { - Some(cap) => { - let snippet = render_snippet(cap, impl_def.syntax(), cursor); - builder.replace_snippet(cap, old_range, snippet); - } - None => { - builder.replace(old_range, impl_def.syntax().to_string()); - } - } - } + // Get the impl to update, or create one if we need to. + let impl_def = match impl_def { + Some(impl_def) => edit.make_mut(impl_def), None => { - // Attach the function to the impl block let name = &strukt_name.to_string(); let params = strukt.generic_param_list(); let ty_params = params.clone(); @@ -176,24 +158,34 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' None, ) .clone_for_update(); - let assoc_items = impl_def.get_or_create_assoc_item_list(); - assoc_items.add_item(f.clone().into()); + + // Fixup impl_def indentation + let indent = strukt.indent_level(); + impl_def.reindent_to(indent); // Insert the impl block. - match ctx.config.snippet_cap { - Some(cap) => { - let offset = strukt.syntax().text_range().end(); - let snippet = render_snippet(cap, impl_def.syntax(), cursor); - let snippet = format!("\n\n{snippet}"); - builder.insert_snippet(cap, offset, snippet); - } - None => { - let offset = strukt.syntax().text_range().end(); - let snippet = format!("\n\n{}", impl_def.syntax()); - builder.insert(offset, snippet); - } - } + let strukt = edit.make_mut(strukt.clone()); + ted::insert_all( + ted::Position::after(strukt.syntax()), + vec![ + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + impl_def.syntax().clone().into(), + ], + ); + + impl_def } + }; + + // Fixup function indentation. + // FIXME: Should really be handled by `AssocItemList::add_item` + f.reindent_to(impl_def.indent_level() + 1); + + let assoc_items = impl_def.get_or_create_assoc_item_list(); + assoc_items.add_item(f.clone().into()); + + if let Some(cap) = ctx.config.snippet_cap { + edit.add_tabstop_before(cap, f) } }, )?; @@ -242,6 +234,45 @@ impl Person { ); } + #[test] + fn test_generate_delegate_create_impl_block_match_indent() { + check_assist( + generate_delegate_methods, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + ag$0e: Age, + } +}"#, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + age: Age, + } + + impl Person { + $0fn age(&self) -> u8 { + self.age.age() + } + } +}"#, + ); + } + #[test] fn test_generate_delegate_update_impl_block() { check_assist( @@ -279,6 +310,47 @@ impl Person { ); } + #[test] + fn test_generate_delegate_update_impl_block_match_indent() { + check_assist( + generate_delegate_methods, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + ag$0e: Age, + } + + impl Person {} +}"#, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + age: Age, + } + + impl Person { + $0fn age(&self) -> u8 { + self.age.age() + } + } +}"#, + ); + } + #[test] fn test_generate_delegate_tuple_struct() { check_assist( diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs new file mode 100644 index 000000000000..185f47184d43 --- /dev/null +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -0,0 +1,1049 @@ +use std::ops::Not; + +use crate::{ + assist_context::{AssistContext, Assists}, + utils::convert_param_list_to_arg_list, +}; +use either::Either; +use hir::{db::HirDatabase, HasVisibility}; +use ide_db::{ + assists::{AssistId, GroupLabel}, + path_transform::PathTransform, +}; +use syntax::{ + ast::{ + self, + edit::{self, AstNodeEdit}, + make, AssocItem, HasGenericParams, HasName, HasVisibility as astHasVisibility, Path, + }, + ted::{self, Position}, + AstNode, NodeOrToken, SyntaxKind, +}; + +// Assist: generate_delegate_trait +// +// Generate delegate trait implementation for `StructField`s. +// +// ``` +// trait SomeTrait { +// type T; +// fn fn_(arg: u32) -> u32; +// fn method_(&mut self) -> bool; +// } +// struct A; +// impl SomeTrait for A { +// type T = u32; +// +// fn fn_(arg: u32) -> u32 { +// 42 +// } +// +// fn method_(&mut self) -> bool { +// false +// } +// } +// struct B { +// a$0: A, +// } +// ``` +// -> +// ``` +// trait SomeTrait { +// type T; +// fn fn_(arg: u32) -> u32; +// fn method_(&mut self) -> bool; +// } +// struct A; +// impl SomeTrait for A { +// type T = u32; +// +// fn fn_(arg: u32) -> u32 { +// 42 +// } +// +// fn method_(&mut self) -> bool { +// false +// } +// } +// struct B { +// a: A, +// } +// +// impl SomeTrait for B { +// type T = ::T; +// +// fn fn_(arg: u32) -> u32 { +// ::fn_(arg) +// } +// +// fn method_(&mut self) -> bool { +// ::method_( &mut self.a ) +// } +// } +// ``` +pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let strukt = Struct::new(ctx.find_node_at_offset::()?)?; + + let field: Field = match ctx.find_node_at_offset::() { + Some(field) => Field::new(&ctx, Either::Left(field))?, + None => { + let field = ctx.find_node_at_offset::()?; + let field_list = ctx.find_node_at_offset::()?; + Field::new(&ctx, either::Right((field, field_list)))? + } + }; + + strukt.delegate(field, acc, ctx); + Some(()) +} + +/// A utility object that represents a struct's field. +struct Field { + name: String, + ty: ast::Type, + range: syntax::TextRange, + impls: Vec, +} + +impl Field { + pub(crate) fn new( + ctx: &AssistContext<'_>, + f: Either, + ) -> Option { + let db = ctx.sema.db; + let name: String; + let range: syntax::TextRange; + let ty: ast::Type; + + let module = ctx.sema.to_module_def(ctx.file_id())?; + + match f { + Either::Left(f) => { + name = f.name()?.to_string(); + ty = f.ty()?; + range = f.syntax().text_range(); + } + Either::Right((f, l)) => { + name = l.fields().position(|it| it == f)?.to_string(); + ty = f.ty()?; + range = f.syntax().text_range(); + } + }; + + let hir_ty = ctx.sema.resolve_type(&ty)?; + let type_impls = hir::Impl::all_for_type(db, hir_ty.clone()); + let mut impls = Vec::with_capacity(type_impls.len()); + let type_param = hir_ty.as_type_param(db); + + if let Some(tp) = type_param { + for tb in tp.trait_bounds(db) { + impls.push(Delegee::Bound(BoundCase(tb))); + } + }; + + for imp in type_impls { + match imp.trait_(db) { + Some(tr) => { + if tr.is_visible_from(db, module) { + impls.push(Delegee::Impls(ImplCase(tr, imp))) + } + } + None => (), + } + } + + Some(Field { name, ty, range, impls }) + } +} + +/// A field that we want to delegate can offer the enclosing struct +/// trait to implement in two ways. The first way is when the field +/// actually implements the trait and the second way is when the field +/// has a bound type parameter. We handle these cases in different ways +/// hence the enum. +enum Delegee { + Bound(BoundCase), + Impls(ImplCase), +} + +struct BoundCase(hir::Trait); +struct ImplCase(hir::Trait, hir::Impl); + +impl Delegee { + fn signature(&self, db: &dyn HirDatabase) -> String { + let mut s = String::new(); + + let (Delegee::Bound(BoundCase(it)) | Delegee::Impls(ImplCase(it, _))) = self; + + for m in it.module(db).path_to_root(db).iter().rev() { + if let Some(name) = m.name(db) { + s.push_str(&format!("{}::", name.to_smol_str())); + } + } + + s.push_str(&it.name(db).to_smol_str()); + s + } +} + +/// A utility struct that is used for the enclosing struct. +struct Struct { + strukt: ast::Struct, + name: ast::Name, +} + +impl Struct { + pub(crate) fn new(s: ast::Struct) -> Option { + let name = s.name()?; + Some(Struct { name, strukt: s }) + } + + pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { + let db = ctx.db(); + for delegee in &field.impls { + // FIXME : We can omit already implemented impl_traits + // But we don't know what the &[hir::Type] argument should look like. + + // let trait_ = match delegee { + // Delegee::Bound(b) => b.0, + // Delegee::Impls(i) => i.1, + // }; + + // if self.hir_ty.impls_trait(db, trait_, &[]) { + // continue; + // } + let signature = delegee.signature(db); + let delegate = generate_impl(ctx, self, &field.ty, &field.name, delegee); + + acc.add_group( + &GroupLabel("Delegate trait impl for field...".to_owned()), + AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), + format!("Generate delegate impl `{}` for `{}`", signature, field.name), + field.range, + |builder| { + builder.insert( + self.strukt.syntax().text_range().end(), + format!("\n\n{}", delegate.syntax()), + ); + }, + ); + } + } +} + +fn generate_impl( + ctx: &AssistContext<'_>, + strukt: &Struct, + field_ty: &ast::Type, + field_name: &String, + delegee: &Delegee, +) -> ast::Impl { + let delegate: ast::Impl; + let source: ast::Impl; + let genpar: Option; + let db = ctx.db(); + let base_path = make::path_from_text(&field_ty.to_string().as_str()); + let s_path = make::ext::ident_path(&strukt.name.to_string()); + + match delegee { + Delegee::Bound(delegee) => { + let in_file = ctx.sema.source(delegee.0.to_owned()).unwrap(); + let source: ast::Trait = in_file.value; + + delegate = make::impl_trait( + delegee.0.is_unsafe(db), + None, + None, + strukt.strukt.generic_param_list(), + None, + delegee.0.is_auto(db), + make::ty(&delegee.0.name(db).to_smol_str()), + make::ty_path(s_path), + source.where_clause(), + strukt.strukt.where_clause(), + None, + ) + .clone_for_update(); + + genpar = source.generic_param_list(); + let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); + let gen_args: String = + genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); + + // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths + let qualified_path_type = make::path_from_text(&format!( + "<{} as {}{}>", + base_path.to_string(), + delegee.0.name(db).to_smol_str(), + gen_args.to_string() + )); + + match source.assoc_item_list() { + Some(ai) => { + ai.assoc_items() + .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) + .for_each(|item| { + let assoc = + process_assoc_item(item, qualified_path_type.clone(), &field_name); + if let Some(assoc) = assoc { + delegate_assoc_items.add_item(assoc); + } + }); + } + None => {} + }; + + let target = ctx.sema.scope(strukt.strukt.syntax()).unwrap(); + let source = ctx.sema.scope(source.syntax()).unwrap(); + + let transform = + PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); + transform.apply(&delegate.syntax()); + } + Delegee::Impls(delegee) => { + let in_file = ctx.sema.source(delegee.1.to_owned()).unwrap(); + source = in_file.value; + delegate = make::impl_trait( + delegee.0.is_unsafe(db), + source.generic_param_list(), + None, + None, + None, + delegee.0.is_auto(db), + make::ty(&delegee.0.name(db).to_smol_str()), + make::ty_path(s_path), + source.where_clause(), + strukt.strukt.where_clause(), + None, + ) + .clone_for_update(); + genpar = source.generic_param_list(); + let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); + let gen_args: String = + genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); + + // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths + let qualified_path_type = make::path_from_text(&format!( + "<{} as {}{}>", + base_path.to_string().as_str(), + delegee.0.name(db).to_smol_str(), + gen_args.to_string().as_str() + )); + + source + .get_or_create_assoc_item_list() + .assoc_items() + .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) + .for_each(|item| { + let assoc = process_assoc_item(item, qualified_path_type.clone(), &field_name); + if let Some(assoc) = assoc { + delegate_assoc_items.add_item(assoc); + } + }); + + let target = ctx.sema.scope(strukt.strukt.syntax()).unwrap(); + let source = ctx.sema.scope(source.syntax()).unwrap(); + + let transform = + PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); + transform.apply(&delegate.syntax()); + } + } + + delegate +} + +fn process_assoc_item( + item: syntax::ast::AssocItem, + qual_path_ty: ast::Path, + base_name: &str, +) -> Option { + match item { + AssocItem::Const(c) => Some(const_assoc_item(c, qual_path_ty)), + AssocItem::Fn(f) => Some(func_assoc_item(f, qual_path_ty, base_name)), + AssocItem::MacroCall(_) => { + // FIXME : Handle MacroCall case. + // return Some(macro_assoc_item(mac, qual_path_ty)); + None + } + AssocItem::TypeAlias(ta) => Some(ty_assoc_item(ta, qual_path_ty)), + } +} + +fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> AssocItem { + let path_expr_segment = make::path_from_text(item.name().unwrap().to_string().as_str()); + + // We want rhs of the const assignment to be a qualified path + // The general case for const assigment can be found [here](`https://doc.rust-lang.org/reference/items/constant-items.html`) + // The qualified will have the following generic syntax : + // >::ConstName; + // FIXME : We can't rely on `make::path_qualified` for now but it would be nice to replace the following with it. + // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap()); + let qualpath = qualpath(qual_path_ty, path_expr_segment); + let inner = make::item_const( + item.visibility(), + item.name().unwrap(), + item.ty().unwrap(), + make::expr_path(qualpath), + ) + .clone_for_update(); + + AssocItem::Const(inner) +} + +fn func_assoc_item(item: syntax::ast::Fn, qual_path_ty: Path, base_name: &str) -> AssocItem { + let path_expr_segment = make::path_from_text(item.name().unwrap().to_string().as_str()); + let qualpath = qualpath(qual_path_ty, path_expr_segment); + + let call = match item.param_list() { + // Methods and funcs should be handled separately. + // We ask if the func has a `self` param. + Some(l) => match l.self_param() { + Some(slf) => { + let mut self_kw = make::expr_path(make::path_from_text("self")); + self_kw = make::expr_field(self_kw, base_name); + + let tail_expr_self = match slf.kind() { + ast::SelfParamKind::Owned => self_kw, + ast::SelfParamKind::Ref => make::expr_ref(self_kw, false), + ast::SelfParamKind::MutRef => make::expr_ref(self_kw, true), + }; + + let param_count = l.params().count(); + let args = convert_param_list_to_arg_list(l).clone_for_update(); + + if param_count > 0 { + // Add SelfParam and a TOKEN::COMMA + ted::insert_all( + Position::after(args.l_paren_token().unwrap()), + vec![ + NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), + NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), + NodeOrToken::Token(make::token(SyntaxKind::COMMA)), + ], + ); + } else { + // Add SelfParam only + ted::insert( + Position::after(args.l_paren_token().unwrap()), + NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), + ); + } + + make::expr_call(make::expr_path(qualpath), args) + } + None => make::expr_call(make::expr_path(qualpath), convert_param_list_to_arg_list(l)), + }, + None => make::expr_call( + make::expr_path(qualpath), + convert_param_list_to_arg_list(make::param_list(None, Vec::new())), + ), + } + .clone_for_update(); + + let body = make::block_expr(vec![], Some(call)).clone_for_update(); + let func = make::fn_( + item.visibility(), + item.name().unwrap(), + item.generic_param_list(), + item.where_clause(), + item.param_list().unwrap(), + body, + item.ret_type(), + item.async_token().is_some(), + item.const_token().is_some(), + item.unsafe_token().is_some(), + ) + .clone_for_update(); + + AssocItem::Fn(func.indent(edit::IndentLevel(1)).clone_for_update()) +} + +fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> AssocItem { + let path_expr_segment = make::path_from_text(item.name().unwrap().to_string().as_str()); + let qualpath = qualpath(qual_path_ty, path_expr_segment); + let ty = make::ty_path(qualpath); + let ident = item.name().unwrap().to_string(); + + let alias = make::ty_alias( + ident.as_str(), + item.generic_param_list(), + None, + item.where_clause(), + Some((ty, None)), + ) + .clone_for_update(); + + AssocItem::TypeAlias(alias) +} + +fn qualpath(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path { + make::path_from_text(&format!("{}::{}", qual_path_ty.to_string(), path_expr_seg.to_string())) +} + +#[cfg(test)] +mod test { + + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn test_tuple_struct_basic() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S(B$0ase); +trait Trait {} +impl Trait for Base {} +"#, + r#" +struct Base; +struct S(Base); + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ); + } + + #[test] + fn test_struct_struct_basic() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S { + ba$0se : Base +} +trait Trait {} +impl Trait for Base {} +"#, + r#" +struct Base; +struct S { + base : Base +} + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ) + } + + // Structs need to be by def populated with fields + // However user can invoke this assist while still editing + // We therefore assert its non-applicability + #[test] + fn test_yet_empty_struct() { + check_assist_not_applicable( + generate_delegate_trait, + r#" +struct Base; +struct S { + $0 +} + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ) + } + + #[test] + fn test_yet_unspecified_field_type() { + check_assist_not_applicable( + generate_delegate_trait, + r#" +struct Base; +struct S { + ab$0c +} + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ); + } + + #[test] + fn test_unsafe_trait() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S { + ba$0se : Base +} +unsafe trait Trait {} +unsafe impl Trait for Base {} +"#, + r#" +struct Base; +struct S { + base : Base +} + +unsafe impl Trait for S {} +unsafe trait Trait {} +unsafe impl Trait for Base {} +"#, + ); + } + + #[test] + fn test_unsafe_trait_with_unsafe_fn() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S { + ba$0se: Base, +} + +unsafe trait Trait { + unsafe fn a_func(); + unsafe fn a_method(&self); +} +unsafe impl Trait for Base { + unsafe fn a_func() {} + unsafe fn a_method(&self) {} +} +"#, + r#" +struct Base; +struct S { + base: Base, +} + +unsafe impl Trait for S { + unsafe fn a_func() { + ::a_func() + } + + unsafe fn a_method(&self) { + ::a_method( &self.base ) + } +} + +unsafe trait Trait { + unsafe fn a_func(); + unsafe fn a_method(&self); +} +unsafe impl Trait for Base { + unsafe fn a_func() {} + unsafe fn a_method(&self) {} +} +"#, + ); + } + + #[test] + fn test_struct_with_where_clause() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b$0 : T, +}"#, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b : T, +} + +impl AnotherTrait for S +where + T: AnotherTrait, +{}"#, + ); + } + + #[test] + fn test_complex_without_where() { + check_assist( + generate_delegate_trait, + r#" +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field$0: Base +} + +impl<'a, T, const C: usize> Trait<'a, T, C> for Base { + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +} +"#, + r#" +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field: Base +} + +impl<'a, T, const C: usize> Trait<'a, T, C> for S { + type AssocType = >::AssocType; + + const AssocConst: usize = >::AssocConst; + + fn assoc_fn(p: ()) { + >::assoc_fn(p) + } + + fn assoc_method(&self, p: ()) { + >::assoc_method( &self.field , p) + } +} + +impl<'a, T, const C: usize> Trait<'a, T, C> for Base { + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +} +"#, + ); + } + + #[test] + fn test_complex_two() { + check_assist( + generate_delegate_trait, + r" +trait AnotherTrait {} + +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + fi$0eld: Base, +} + +impl<'b, C, const D: usize> Trait<'b, C, D> for Base +where + C: AnotherTrait, +{ + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +}", + r#" +trait AnotherTrait {} + +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field: Base, +} + +impl<'b, C, const D: usize> Trait<'b, C, D> for S +where + C: AnotherTrait, +{ + type AssocType = >::AssocType; + + const AssocConst: usize = >::AssocConst; + + fn assoc_fn(p: ()) { + >::assoc_fn(p) + } + + fn assoc_method(&self, p: ()) { + >::assoc_method( &self.field , p) + } +} + +impl<'b, C, const D: usize> Trait<'b, C, D> for Base +where + C: AnotherTrait, +{ + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +}"#, + ) + } + + #[test] + fn test_complex_three() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +trait YetAnotherTrait {} + +struct StructImplsAll(); +impl AnotherTrait for StructImplsAll {} +impl YetAnotherTrait for StructImplsAll {} + +trait Trait<'a, T, const C: usize> { + type A; + const ASSOC_CONST: usize = C; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + fi$0eld: Base, +} + +impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base +where + A: AnotherTrait, +{ + type A = i32; + + const ASSOC_CONST: usize = B; + + fn assoc_fn(p: ()) {} + + fn assoc_method(&self, p: ()) {} +} +"#, + r#" +trait AnotherTrait {} +trait YetAnotherTrait {} + +struct StructImplsAll(); +impl AnotherTrait for StructImplsAll {} +impl YetAnotherTrait for StructImplsAll {} + +trait Trait<'a, T, const C: usize> { + type A; + const ASSOC_CONST: usize = C; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field: Base, +} + +impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for S +where + A: AnotherTrait, +{ + type A = >::A; + + const ASSOC_CONST: usize = >::ASSOC_CONST; + + fn assoc_fn(p: ()) { + >::assoc_fn(p) + } + + fn assoc_method(&self, p: ()) { + >::assoc_method( &self.field , p) + } +} + +impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base +where + A: AnotherTrait, +{ + type A = i32; + + const ASSOC_CONST: usize = B; + + fn assoc_fn(p: ()) {} + + fn assoc_method(&self, p: ()) {} +} +"#, + ) + } + + #[test] + fn test_type_bound() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b$0: T, +}"#, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b: T, +} + +impl AnotherTrait for S +where + T: AnotherTrait, +{}"#, + ); + } + + #[test] + fn test_docstring_example() { + check_assist( + generate_delegate_trait, + r#" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + fn fn_(arg: u32) -> u32 { + 42 + } + fn method_(&mut self) -> bool { + false + } +} +struct B { + a$0: A, +} +"#, + r#" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + fn fn_(arg: u32) -> u32 { + 42 + } + fn method_(&mut self) -> bool { + false + } +} +struct B { + a: A, +} + +impl SomeTrait for B { + type T = ::T; + + fn fn_(arg: u32) -> u32 { + ::fn_(arg) + } + + fn method_(&mut self) -> bool { + ::method_( &mut self.a ) + } +} +"#, + ); + } + + #[test] + fn import_from_other_mod() { + check_assist( + generate_delegate_trait, + r#" +mod some_module { + pub trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; + } + pub struct A; + impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } + } +} + +struct B { + a$0: some_module::A, +}"#, + r#" +mod some_module { + pub trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; + } + pub struct A; + impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } + } +} + +struct B { + a: some_module::A, +} + +impl some_module::SomeTrait for B { + type T = ::T; + + fn fn_(arg: u32) -> u32 { + ::fn_(arg) + } + + fn method_(&mut self) -> bool { + ::method_( &mut self.a ) + } +}"#, + ) + } +} diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs index 78ac2eb30e59..747f70f9f6f9 100644 --- a/crates/ide-assists/src/handlers/generate_derive.rs +++ b/crates/ide-assists/src/handlers/generate_derive.rs @@ -1,7 +1,6 @@ use syntax::{ - ast::{self, edit::IndentLevel, AstNode, HasAttrs}, - SyntaxKind::{COMMENT, WHITESPACE}, - TextSize, + ast::{self, edit_in_place::AttrsOwnerEdit, make, AstNode, HasAttrs}, + T, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -27,48 +26,37 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::()?; - let node_start = derive_insertion_offset(&nominal)?; let target = nominal.syntax().text_range(); - acc.add( - AssistId("generate_derive", AssistKind::Generate), - "Add `#[derive]`", - target, - |builder| { - let derive_attr = nominal - .attrs() - .filter_map(|x| x.as_simple_call()) - .filter(|(name, _arg)| name == "derive") - .map(|(_name, arg)| arg) - .next(); - match derive_attr { - None => { - let indent_level = IndentLevel::from_node(nominal.syntax()); - builder.insert_snippet( - cap, - node_start, - format!("#[derive($0)]\n{indent_level}"), - ); - } - Some(tt) => { - // Just move the cursor. - builder.insert_snippet( - cap, - tt.syntax().text_range().end() - TextSize::of(')'), - "$0", - ) - } - }; - }, - ) -} + acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| { + let derive_attr = nominal + .attrs() + .filter_map(|x| x.as_simple_call()) + .filter(|(name, _arg)| name == "derive") + .map(|(_name, arg)| arg) + .next(); + match derive_attr { + None => { + let derive = make::attr_outer(make::meta_token_tree( + make::ext::ident_path("derive"), + make::token_tree(T!['('], vec![]).clone_for_update(), + )) + .clone_for_update(); -// Insert `derive` after doc comments. -fn derive_insertion_offset(nominal: &ast::Adt) -> Option { - let non_ws_child = nominal - .syntax() - .children_with_tokens() - .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; - Some(non_ws_child.text_range().start()) + let nominal = edit.make_mut(nominal); + nominal.add_attr(derive.clone()); + + edit.add_tabstop_before_token( + cap, + derive.meta().unwrap().token_tree().unwrap().r_paren_token().unwrap(), + ); + } + Some(tt) => { + // Just move the cursor. + let tt = edit.make_mut(tt); + edit.add_tabstop_before_token(cap, tt.right_delimiter_token().unwrap()); + } + }; + }) } #[cfg(test)] @@ -114,6 +102,38 @@ mod m { ); } + #[test] + fn add_derive_existing_with_brackets() { + check_assist( + generate_derive, + " +#[derive[Clone]] +struct Foo { a: i32$0, } +", + " +#[derive[Clone$0]] +struct Foo { a: i32, } +", + ); + } + + #[test] + fn add_derive_existing_missing_delimiter() { + // since `#[derive]` isn't a simple attr call (i.e. `#[derive()]`) + // we don't consider it as a proper derive attr and generate a new + // one instead + check_assist( + generate_derive, + " +#[derive] +struct Foo { a: i32$0, }", + " +#[derive] +#[derive($0)] +struct Foo { a: i32, }", + ); + } + #[test] fn add_derive_new_with_doc_comment() { check_assist( diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index c579f6780db8..8085572497aa 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -623,7 +623,9 @@ fn fn_generic_params( fn params_and_where_preds_in_scope( ctx: &AssistContext<'_>, ) -> (Vec, Vec) { - let Some(body) = containing_body(ctx) else { return Default::default(); }; + let Some(body) = containing_body(ctx) else { + return Default::default(); + }; let mut generic_params = Vec::new(); let mut where_clauses = Vec::new(); diff --git a/crates/ide-assists/src/handlers/generate_getter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs similarity index 61% rename from crates/ide-assists/src/handlers/generate_getter.rs rename to crates/ide-assists/src/handlers/generate_getter_or_setter.rs index dd6bbd84afcc..9c9478b040d1 100644 --- a/crates/ide-assists/src/handlers/generate_getter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -1,4 +1,4 @@ -use ide_db::famous_defs::FamousDefs; +use ide_db::{famous_defs::FamousDefs, source_change::SourceChangeBuilder}; use stdx::{format_to, to_lower_snake_case}; use syntax::{ ast::{self, AstNode, HasName, HasVisibility}, @@ -10,6 +10,66 @@ use crate::{ AssistContext, AssistId, AssistKind, Assists, GroupLabel, }; +// Assist: generate_setter +// +// Generate a setter method. +// +// ``` +// struct Person { +// nam$0e: String, +// } +// ``` +// -> +// ``` +// struct Person { +// name: String, +// } +// +// impl Person { +// fn $0set_name(&mut self, name: String) { +// self.name = name; +// } +// } +// ``` +pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + // This if condition denotes two modes this assist can work in: + // - First is acting upon selection of record fields + // - Next is acting upon a single record field + // + // This is the only part where implementation diverges a bit, + // subsequent code is generic for both of these modes + + let (strukt, info_of_record_fields, mut fn_names) = extract_and_parse(ctx, AssistType::Set)?; + + // No record fields to do work on :( + if info_of_record_fields.len() == 0 { + return None; + } + + // Prepend set_ to fn names. + fn_names.iter_mut().for_each(|name| *name = format!("set_{}", name)); + + // Return early if we've found an existing fn + let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &fn_names)?; + + // Computing collective text range of all record fields in selected region + let target: TextRange = info_of_record_fields + .iter() + .map(|record_field_info| record_field_info.target) + .reduce(|acc, target| acc.cover(target))?; + + let setter_info = AssistInfo { impl_def, strukt, assist_type: AssistType::Set }; + + acc.add_group( + &GroupLabel("Generate getter/setter".to_owned()), + AssistId("generate_setter", AssistKind::Generate), + "Generate a setter method", + target, + |builder| build_source_change(builder, ctx, info_of_record_fields, setter_info), + ); + Some(()) +} + // Assist: generate_getter // // Generate a getter method. @@ -83,10 +143,16 @@ struct RecordFieldInfo { target: TextRange, } -struct GetterInfo { +struct AssistInfo { impl_def: Option, strukt: ast::Struct, - mutable: bool, + assist_type: AssistType, +} + +enum AssistType { + Get, + MutGet, + Set, } pub(crate) fn generate_getter_impl( @@ -94,40 +160,8 @@ pub(crate) fn generate_getter_impl( ctx: &AssistContext<'_>, mutable: bool, ) -> Option<()> { - // This if condition denotes two modes this assist can work in: - // - First is acting upon selection of record fields - // - Next is acting upon a single record field - // - // This is the only part where implementation diverges a bit, - // subsequent code is generic for both of these modes - - let (strukt, info_of_record_fields, fn_names) = if !ctx.has_empty_selection() { - // Selection Mode - let node = ctx.covering_element(); - - let node = match node { - syntax::NodeOrToken::Node(n) => n, - syntax::NodeOrToken::Token(t) => t.parent()?, - }; - - let parent_struct = node.ancestors().find_map(ast::Struct::cast)?; - - let (info_of_record_fields, field_names) = - extract_and_parse_record_fields(&parent_struct, ctx.selection_trimmed(), mutable)?; - - (parent_struct, info_of_record_fields, field_names) - } else { - // Single Record Field mode - let strukt = ctx.find_node_at_offset::()?; - let field = ctx.find_node_at_offset::()?; - - let record_field_info = parse_record_field(field, mutable)?; - - let fn_name = record_field_info.fn_name.clone(); - - (strukt, vec![record_field_info], vec![fn_name]) - }; - + let (strukt, info_of_record_fields, fn_names) = + extract_and_parse(ctx, if mutable { AssistType::MutGet } else { AssistType::Get })?; // No record fields to do work on :( if info_of_record_fields.len() == 0 { return None; @@ -147,98 +181,30 @@ pub(crate) fn generate_getter_impl( .map(|record_field_info| record_field_info.target) .reduce(|acc, target| acc.cover(target))?; - let getter_info = GetterInfo { impl_def, strukt, mutable }; + let getter_info = AssistInfo { + impl_def, + strukt, + assist_type: if mutable { AssistType::MutGet } else { AssistType::Get }, + }; acc.add_group( &GroupLabel("Generate getter/setter".to_owned()), AssistId(id, AssistKind::Generate), label, target, - |builder| { - let record_fields_count = info_of_record_fields.len(); - - let mut buf = String::with_capacity(512); - - // Check if an impl exists - if let Some(impl_def) = &getter_info.impl_def { - // Check if impl is empty - if let Some(assoc_item_list) = impl_def.assoc_item_list() { - if assoc_item_list.assoc_items().next().is_some() { - // If not empty then only insert a new line - buf.push('\n'); - } - } - } - - for (i, record_field_info) in info_of_record_fields.iter().enumerate() { - // this buf inserts a newline at the end of a getter - // automatically, if one wants to add one more newline - // for separating it from other assoc items, that needs - // to be handled separately - let mut getter_buf = - generate_getter_from_info(ctx, &getter_info, record_field_info); - - // Insert `$0` only for last getter we generate - if i == record_fields_count - 1 { - if ctx.config.snippet_cap.is_some() { - getter_buf = getter_buf.replacen("fn ", "fn $0", 1); - } - } - - // For first element we do not merge with '\n', as - // that can be inserted by impl_def check defined - // above, for other cases which are: - // - // - impl exists but it empty, here we would ideally - // not want to keep newline between impl { - // and fn () { line - // - // - next if impl itself does not exist, in this - // case we ourselves generate a new impl and that - // again ends up with the same reasoning as above - // for not keeping newline - if i == 0 { - buf = buf + &getter_buf; - } else { - buf = buf + "\n" + &getter_buf; - } - - // We don't insert a new line at the end of - // last getter as it will end up in the end - // of an impl where we would not like to keep - // getter and end of impl ( i.e. `}` ) with an - // extra line for no reason - if i < record_fields_count - 1 { - buf = buf + "\n"; - } - } - - let start_offset = getter_info - .impl_def - .as_ref() - .and_then(|impl_def| find_impl_block_end(impl_def.to_owned(), &mut buf)) - .unwrap_or_else(|| { - buf = generate_impl_text(&ast::Adt::Struct(getter_info.strukt.clone()), &buf); - getter_info.strukt.syntax().text_range().end() - }); - - match ctx.config.snippet_cap { - Some(cap) => builder.insert_snippet(cap, start_offset, buf), - None => builder.insert(start_offset, buf), - } - }, + |builder| build_source_change(builder, ctx, info_of_record_fields, getter_info), ) } fn generate_getter_from_info( ctx: &AssistContext<'_>, - info: &GetterInfo, + info: &AssistInfo, record_field_info: &RecordFieldInfo, ) -> String { let mut buf = String::with_capacity(512); let vis = info.strukt.visibility().map_or(String::new(), |v| format!("{v} ")); - let (ty, body) = if info.mutable { + let (ty, body) = if matches!(info.assist_type, AssistType::MutGet) { ( format!("&mut {}", record_field_info.field_ty), format!("&mut self.{}", record_field_info.field_name), @@ -273,7 +239,7 @@ fn generate_getter_from_info( }}", vis, record_field_info.fn_name, - info.mutable.then_some("mut ").unwrap_or_default(), + matches!(info.assist_type, AssistType::MutGet).then_some("mut ").unwrap_or_default(), ty, body, ); @@ -281,10 +247,58 @@ fn generate_getter_from_info( buf } +fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldInfo) -> String { + let mut buf = String::with_capacity(512); + let strukt = &info.strukt; + let fn_name = &record_field_info.fn_name; + let field_ty = &record_field_info.field_ty; + let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} ")); + format_to!( + buf, + " {vis}fn set_{fn_name}(&mut self, {fn_name}: {field_ty}) {{ + self.{fn_name} = {fn_name}; + }}" + ); + + buf +} + +fn extract_and_parse( + ctx: &AssistContext<'_>, + assist_type: AssistType, +) -> Option<(ast::Struct, Vec, Vec)> { + // This if condition denotes two modes assists can work in: + // - First is acting upon selection of record fields + // - Next is acting upon a single record field + if !ctx.has_empty_selection() { + // Selection Mode + let node = ctx.covering_element(); + + let node = match node { + syntax::NodeOrToken::Node(n) => n, + syntax::NodeOrToken::Token(t) => t.parent()?, + }; + + let parent_struct = node.ancestors().find_map(ast::Struct::cast)?; + + let (info_of_record_fields, field_names) = + extract_and_parse_record_fields(&parent_struct, ctx.selection_trimmed(), &assist_type)?; + + return Some((parent_struct, info_of_record_fields, field_names)); + } + + // Single Record Field mode + let strukt = ctx.find_node_at_offset::()?; + let field = ctx.find_node_at_offset::()?; + let record_field_info = parse_record_field(field, &assist_type)?; + let fn_name = record_field_info.fn_name.clone(); + Some((strukt, vec![record_field_info], vec![fn_name])) +} + fn extract_and_parse_record_fields( node: &ast::Struct, selection_range: TextRange, - mutable: bool, + assist_type: &AssistType, ) -> Option<(Vec, Vec)> { let mut field_names: Vec = vec![]; let field_list = node.field_list()?; @@ -295,7 +309,7 @@ fn extract_and_parse_record_fields( .fields() .filter_map(|record_field| { if selection_range.contains_range(record_field.syntax().text_range()) { - let record_field_info = parse_record_field(record_field, mutable)?; + let record_field_info = parse_record_field(record_field, assist_type)?; field_names.push(record_field_info.fn_name.clone()); return Some(record_field_info); } @@ -316,12 +330,15 @@ fn extract_and_parse_record_fields( } } -fn parse_record_field(record_field: ast::RecordField, mutable: bool) -> Option { +fn parse_record_field( + record_field: ast::RecordField, + assist_type: &AssistType, +) -> Option { let field_name = record_field.name()?; let field_ty = record_field.ty()?; let mut fn_name = to_lower_snake_case(&field_name.to_string()); - if mutable { + if matches!(assist_type, AssistType::MutGet) { format_to!(fn_name, "_mut"); } @@ -330,8 +347,89 @@ fn parse_record_field(record_field: ast::RecordField, mutable: bool) -> Option, + info_of_record_fields: Vec, + assist_info: AssistInfo, +) { + let record_fields_count = info_of_record_fields.len(); + + let mut buf = String::with_capacity(512); + + // Check if an impl exists + if let Some(impl_def) = &assist_info.impl_def { + // Check if impl is empty + if let Some(assoc_item_list) = impl_def.assoc_item_list() { + if assoc_item_list.assoc_items().next().is_some() { + // If not empty then only insert a new line + buf.push('\n'); + } + } + } + + for (i, record_field_info) in info_of_record_fields.iter().enumerate() { + // this buf inserts a newline at the end of a getter + // automatically, if one wants to add one more newline + // for separating it from other assoc items, that needs + // to be handled separately + let mut getter_buf = match assist_info.assist_type { + AssistType::Set => generate_setter_from_info(&assist_info, record_field_info), + _ => generate_getter_from_info(ctx, &assist_info, record_field_info), + }; + + // Insert `$0` only for last getter we generate + if i == record_fields_count - 1 { + if ctx.config.snippet_cap.is_some() { + getter_buf = getter_buf.replacen("fn ", "fn $0", 1); + } + } + + // For first element we do not merge with '\n', as + // that can be inserted by impl_def check defined + // above, for other cases which are: + // + // - impl exists but it empty, here we would ideally + // not want to keep newline between impl { + // and fn () { line + // + // - next if impl itself does not exist, in this + // case we ourselves generate a new impl and that + // again ends up with the same reasoning as above + // for not keeping newline + if i == 0 { + buf = buf + &getter_buf; + } else { + buf = buf + "\n" + &getter_buf; + } + + // We don't insert a new line at the end of + // last getter as it will end up in the end + // of an impl where we would not like to keep + // getter and end of impl ( i.e. `}` ) with an + // extra line for no reason + if i < record_fields_count - 1 { + buf = buf + "\n"; + } + } + + let start_offset = assist_info + .impl_def + .as_ref() + .and_then(|impl_def| find_impl_block_end(impl_def.to_owned(), &mut buf)) + .unwrap_or_else(|| { + buf = generate_impl_text(&ast::Adt::Struct(assist_info.strukt.clone()), &buf); + assist_info.strukt.syntax().text_range().end() + }); + + match ctx.config.snippet_cap { + Some(cap) => builder.insert_snippet(cap, start_offset, buf), + None => builder.insert(start_offset, buf), + } +} + #[cfg(test)] -mod tests { +mod tests_getter { use crate::tests::{check_assist, check_assist_no_snippet_cap, check_assist_not_applicable}; use super::*; @@ -812,3 +910,105 @@ impl Context { ); } } + +#[cfg(test)] +mod tests_setter { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + fn check_not_applicable(ra_fixture: &str) { + check_assist_not_applicable(generate_setter, ra_fixture) + } + + #[test] + fn test_generate_setter_from_field() { + check_assist( + generate_setter, + r#" +struct Person { + dat$0a: T, +}"#, + r#" +struct Person { + data: T, +} + +impl Person { + fn $0set_data(&mut self, data: T) { + self.data = data; + } +}"#, + ); + } + + #[test] + fn test_generate_setter_already_implemented() { + check_not_applicable( + r#" +struct Person { + dat$0a: T, +} + +impl Person { + fn set_data(&mut self, data: T) { + self.data = data; + } +}"#, + ); + } + + #[test] + fn test_generate_setter_from_field_with_visibility_marker() { + check_assist( + generate_setter, + r#" +pub(crate) struct Person { + dat$0a: T, +}"#, + r#" +pub(crate) struct Person { + data: T, +} + +impl Person { + pub(crate) fn $0set_data(&mut self, data: T) { + self.data = data; + } +}"#, + ); + } + + #[test] + fn test_multiple_generate_setter() { + check_assist( + generate_setter, + r#" +struct Context { + data: T, + cou$0nt: usize, +} + +impl Context { + fn set_data(&mut self, data: T) { + self.data = data; + } +}"#, + r#" +struct Context { + data: T, + count: usize, +} + +impl Context { + fn set_data(&mut self, data: T) { + self.data = data; + } + + fn $0set_count(&mut self, count: usize) { + self.count = count; + } +}"#, + ); + } +} diff --git a/crates/ide-assists/src/handlers/generate_setter.rs b/crates/ide-assists/src/handlers/generate_setter.rs deleted file mode 100644 index 62f72df1c9d6..000000000000 --- a/crates/ide-assists/src/handlers/generate_setter.rs +++ /dev/null @@ -1,175 +0,0 @@ -use stdx::{format_to, to_lower_snake_case}; -use syntax::ast::{self, AstNode, HasName, HasVisibility}; - -use crate::{ - utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, - AssistContext, AssistId, AssistKind, Assists, GroupLabel, -}; - -// Assist: generate_setter -// -// Generate a setter method. -// -// ``` -// struct Person { -// nam$0e: String, -// } -// ``` -// -> -// ``` -// struct Person { -// name: String, -// } -// -// impl Person { -// fn set_name(&mut self, name: String) { -// self.name = name; -// } -// } -// ``` -pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let strukt = ctx.find_node_at_offset::()?; - let field = ctx.find_node_at_offset::()?; - - let field_name = field.name()?; - let field_ty = field.ty()?; - - // Return early if we've found an existing fn - let fn_name = to_lower_snake_case(&field_name.to_string()); - let impl_def = - find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &[format!("set_{fn_name}")])?; - - let target = field.syntax().text_range(); - acc.add_group( - &GroupLabel("Generate getter/setter".to_owned()), - AssistId("generate_setter", AssistKind::Generate), - "Generate a setter method", - target, - |builder| { - let mut buf = String::with_capacity(512); - - if impl_def.is_some() { - buf.push('\n'); - } - - let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} ")); - format_to!( - buf, - " {vis}fn set_{fn_name}(&mut self, {fn_name}: {field_ty}) {{ - self.{fn_name} = {fn_name}; - }}" - ); - - let start_offset = impl_def - .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) - .unwrap_or_else(|| { - buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); - strukt.syntax().text_range().end() - }); - - builder.insert(start_offset, buf); - }, - ) -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - - use super::*; - - fn check_not_applicable(ra_fixture: &str) { - check_assist_not_applicable(generate_setter, ra_fixture) - } - - #[test] - fn test_generate_setter_from_field() { - check_assist( - generate_setter, - r#" -struct Person { - dat$0a: T, -}"#, - r#" -struct Person { - data: T, -} - -impl Person { - fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - ); - } - - #[test] - fn test_generate_setter_already_implemented() { - check_not_applicable( - r#" -struct Person { - dat$0a: T, -} - -impl Person { - fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - ); - } - - #[test] - fn test_generate_setter_from_field_with_visibility_marker() { - check_assist( - generate_setter, - r#" -pub(crate) struct Person { - dat$0a: T, -}"#, - r#" -pub(crate) struct Person { - data: T, -} - -impl Person { - pub(crate) fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - ); - } - - #[test] - fn test_multiple_generate_setter() { - check_assist( - generate_setter, - r#" -struct Context { - data: T, - cou$0nt: usize, -} - -impl Context { - fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - r#" -struct Context { - data: T, - count: usize, -} - -impl Context { - fn set_data(&mut self, data: T) { - self.data = data; - } - - fn set_count(&mut self, count: usize) { - self.count = count; - } -}"#, - ); - } -} diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs new file mode 100644 index 000000000000..0f67380d12b6 --- /dev/null +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -0,0 +1,429 @@ +use crate::assist_context::{AssistContext, Assists}; +use ide_db::assists::AssistId; +use syntax::{ + ast::{self, edit::IndentLevel, make, HasGenericParams, HasVisibility}, + ted, AstNode, SyntaxKind, +}; + +// NOTES : +// We generate erroneous code if a function is declared const (E0379) +// This is left to the user to correct as our only option is to remove the +// function completely which we should not be doing. + +// Assist: generate_trait_from_impl +// +// Generate trait for an already defined inherent impl and convert impl to a trait impl. +// +// ``` +// struct Foo([i32; N]); +// +// macro_rules! const_maker { +// ($t:ty, $v:tt) => { +// const CONST: $t = $v; +// }; +// } +// +// impl Fo$0o { +// // Used as an associated constant. +// const CONST_ASSOC: usize = N * 4; +// +// fn create() -> Option<()> { +// Some(()) +// } +// +// const_maker! {i32, 7} +// } +// ``` +// -> +// ``` +// struct Foo([i32; N]); +// +// macro_rules! const_maker { +// ($t:ty, $v:tt) => { +// const CONST: $t = $v; +// }; +// } +// +// trait ${0:TraitName} { +// // Used as an associated constant. +// const CONST_ASSOC: usize = N * 4; +// +// fn create() -> Option<()>; +// +// const_maker! {i32, 7} +// } +// +// impl ${0:TraitName} for Foo { +// // Used as an associated constant. +// const CONST_ASSOC: usize = N * 4; +// +// fn create() -> Option<()> { +// Some(()) +// } +// +// const_maker! {i32, 7} +// } +// ``` +pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + // Get AST Node + let impl_ast = ctx.find_node_at_offset::()?; + + // Check if cursor is to the left of assoc item list's L_CURLY. + // if no L_CURLY then return. + let l_curly = impl_ast.assoc_item_list()?.l_curly_token()?; + + let cursor_offset = ctx.offset(); + let l_curly_offset = l_curly.text_range(); + if cursor_offset >= l_curly_offset.start() { + return None; + } + + // If impl is not inherent then we don't really need to go any further. + if impl_ast.for_token().is_some() { + return None; + } + + let assoc_items = impl_ast.assoc_item_list()?; + let first_element = assoc_items.assoc_items().next(); + if first_element.is_none() { + // No reason for an assist. + return None; + } + + let impl_name = impl_ast.self_ty()?; + + acc.add( + AssistId("generate_trait_from_impl", ide_db::assists::AssistKind::Generate), + "Generate trait from impl", + impl_ast.syntax().text_range(), + |builder| { + let trait_items = assoc_items.clone_for_update(); + let impl_items = assoc_items.clone_for_update(); + + trait_items.assoc_items().for_each(|item| { + strip_body(&item); + remove_items_visibility(&item); + }); + + impl_items.assoc_items().for_each(|item| { + remove_items_visibility(&item); + }); + + let trait_ast = make::trait_( + false, + "NewTrait", + impl_ast.generic_param_list(), + impl_ast.where_clause(), + trait_items, + ); + + // Change `impl Foo` to `impl NewTrait for Foo` + let arg_list = if let Some(genpars) = impl_ast.generic_param_list() { + genpars.to_generic_args().to_string() + } else { + "".to_string() + }; + + if let Some(snippet_cap) = ctx.config.snippet_cap { + builder.replace_snippet( + snippet_cap, + impl_name.syntax().text_range(), + format!("${{0:TraitName}}{} for {}", arg_list, impl_name.to_string()), + ); + + // Insert trait before TraitImpl + builder.insert_snippet( + snippet_cap, + impl_ast.syntax().text_range().start(), + format!( + "{}\n\n{}", + trait_ast.to_string().replace("NewTrait", "${0:TraitName}"), + IndentLevel::from_node(impl_ast.syntax()) + ), + ); + } else { + builder.replace( + impl_name.syntax().text_range(), + format!("NewTrait{} for {}", arg_list, impl_name.to_string()), + ); + + // Insert trait before TraitImpl + builder.insert( + impl_ast.syntax().text_range().start(), + format!( + "{}\n\n{}", + trait_ast.to_string(), + IndentLevel::from_node(impl_ast.syntax()) + ), + ); + } + + builder.replace(assoc_items.syntax().text_range(), impl_items.to_string()); + }, + ); + + Some(()) +} + +/// `E0449` Trait items always share the visibility of their trait +fn remove_items_visibility(item: &ast::AssocItem) { + match item { + ast::AssocItem::Const(c) => { + if let Some(vis) = c.visibility() { + ted::remove(vis.syntax()); + } + } + ast::AssocItem::Fn(f) => { + if let Some(vis) = f.visibility() { + ted::remove(vis.syntax()); + } + } + ast::AssocItem::TypeAlias(t) => { + if let Some(vis) = t.visibility() { + ted::remove(vis.syntax()); + } + } + _ => (), + } +} + +fn strip_body(item: &ast::AssocItem) { + match item { + ast::AssocItem::Fn(f) => { + if let Some(body) = f.body() { + // In constrast to function bodies, we want to see no ws before a semicolon. + // So let's remove them if we see any. + if let Some(prev) = body.syntax().prev_sibling_or_token() { + if prev.kind() == SyntaxKind::WHITESPACE { + ted::remove(prev); + } + } + + ted::replace(body.syntax(), make::tokens::semicolon()); + } + } + _ => (), + }; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{check_assist, check_assist_no_snippet_cap, check_assist_not_applicable}; + + #[test] + fn test_trigger_when_cursor_on_header() { + check_assist_not_applicable( + generate_trait_from_impl, + r#" +struct Foo(f64); + +impl Foo { $0 + fn add(&mut self, x: f64) { + self.0 += x; + } +}"#, + ); + } + + #[test] + fn test_assoc_item_fn() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo(f64); + +impl F$0oo { + fn add(&mut self, x: f64) { + self.0 += x; + } +}"#, + r#" +struct Foo(f64); + +trait NewTrait { + fn add(&mut self, x: f64); +} + +impl NewTrait for Foo { + fn add(&mut self, x: f64) { + self.0 += x; + } +}"#, + ) + } + + #[test] + fn test_assoc_item_macro() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo; + +macro_rules! const_maker { + ($t:ty, $v:tt) => { + const CONST: $t = $v; + }; +} + +impl F$0oo { + const_maker! {i32, 7} +}"#, + r#" +struct Foo; + +macro_rules! const_maker { + ($t:ty, $v:tt) => { + const CONST: $t = $v; + }; +} + +trait NewTrait { + const_maker! {i32, 7} +} + +impl NewTrait for Foo { + const_maker! {i32, 7} +}"#, + ) + } + + #[test] + fn test_assoc_item_const() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo; + +impl F$0oo { + const ABC: i32 = 3; +}"#, + r#" +struct Foo; + +trait NewTrait { + const ABC: i32 = 3; +} + +impl NewTrait for Foo { + const ABC: i32 = 3; +}"#, + ) + } + + #[test] + fn test_impl_with_generics() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo([i32; N]); + +impl F$0oo { + // Used as an associated constant. + const CONST: usize = N * 4; +} + "#, + r#" +struct Foo([i32; N]); + +trait NewTrait { + // Used as an associated constant. + const CONST: usize = N * 4; +} + +impl NewTrait for Foo { + // Used as an associated constant. + const CONST: usize = N * 4; +} + "#, + ) + } + + #[test] + fn test_trait_items_should_not_have_vis() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo; + +impl F$0oo { + pub fn a_func() -> Option<()> { + Some(()) + } +}"#, + r#" +struct Foo; + +trait NewTrait { + fn a_func() -> Option<()>; +} + +impl NewTrait for Foo { + fn a_func() -> Option<()> { + Some(()) + } +}"#, + ) + } + + #[test] + fn test_empty_inherent_impl() { + check_assist_not_applicable( + generate_trait_from_impl, + r#" +impl Emp$0tyImpl{} +"#, + ) + } + + #[test] + fn test_not_top_level_impl() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +mod a { + impl S$0 { + fn foo() {} + } +}"#, + r#" +mod a { + trait NewTrait { + fn foo(); + } + + impl NewTrait for S { + fn foo() {} + } +}"#, + ) + } + + #[test] + fn test_snippet_cap_is_some() { + check_assist( + generate_trait_from_impl, + r#" +struct Foo([i32; N]); + +impl F$0oo { + // Used as an associated constant. + const CONST: usize = N * 4; +} + "#, + r#" +struct Foo([i32; N]); + +trait ${0:TraitName} { + // Used as an associated constant. + const CONST: usize = N * 4; +} + +impl ${0:TraitName} for Foo { + // Used as an associated constant. + const CONST: usize = N * 4; +} + "#, + ) + } +} diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 797180fa1899..67036029f5ee 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -15,7 +15,7 @@ use ide_db::{ }; use itertools::{izip, Itertools}; use syntax::{ - ast::{self, edit_in_place::Indent, HasArgList, PathExpr}, + ast::{self, edit::IndentLevel, edit_in_place::Indent, HasArgList, PathExpr}, ted, AstNode, NodeOrToken, SyntaxKind, }; @@ -306,7 +306,7 @@ fn inline( params: &[(ast::Pat, Option, hir::Param)], CallInfo { node, arguments, generic_arg_list }: &CallInfo, ) -> ast::Expr { - let body = if sema.hir_file_for(fn_body.syntax()).is_macro() { + let mut body = if sema.hir_file_for(fn_body.syntax()).is_macro() { cov_mark::hit!(inline_call_defined_in_macro); if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) { body @@ -391,19 +391,19 @@ fn inline( } } + let mut let_stmts = Vec::new(); + // Inline parameter expressions or generate `let` statements depending on whether inlining works or not. - for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() { + for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments) { // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors let usages: &[ast::PathExpr] = &usages; let expr: &ast::Expr = expr; - let insert_let_stmt = || { + let mut insert_let_stmt = || { let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone()); - if let Some(stmt_list) = body.stmt_list() { - stmt_list.push_front( - make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(), - ) - } + let_stmts.push( + make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(), + ); }; // check if there is a local var in the function that conflicts with parameter @@ -457,6 +457,24 @@ fn inline( } } + let is_async_fn = function.is_async(sema.db); + if is_async_fn { + cov_mark::hit!(inline_call_async_fn); + body = make::async_move_block_expr(body.statements(), body.tail_expr()).clone_for_update(); + + // Arguments should be evaluated outside the async block, and then moved into it. + if !let_stmts.is_empty() { + cov_mark::hit!(inline_call_async_fn_with_let_stmts); + body.indent(IndentLevel(1)); + body = make::block_expr(let_stmts, Some(body.into())).clone_for_update(); + } + } else if let Some(stmt_list) = body.stmt_list() { + ted::insert_all( + ted::Position::after(stmt_list.l_curly_token().unwrap()), + let_stmts.into_iter().map(|stmt| stmt.syntax().clone().into()).collect(), + ); + } + let original_indentation = match node { ast::CallableExpr::Call(it) => it.indent_level(), ast::CallableExpr::MethodCall(it) => it.indent_level(), @@ -464,7 +482,7 @@ fn inline( body.reindent_to(original_indentation); match body.tail_expr() { - Some(expr) if body.statements().next().is_none() => expr, + Some(expr) if !is_async_fn && body.statements().next().is_none() => expr, _ => match node .syntax() .parent() @@ -1350,6 +1368,109 @@ fn main() { bar * b * a * 6 }; } +"#, + ); + } + + #[test] + fn async_fn_single_expression() { + cov_mark::check!(inline_call_async_fn); + check_assist( + inline_call, + r#" +async fn bar(x: u32) -> u32 { x + 1 } +async fn foo(arg: u32) -> u32 { + bar(arg).await * 2 +} +fn spawn(_: T) {} +fn main() { + spawn(foo$0(42)); +} +"#, + r#" +async fn bar(x: u32) -> u32 { x + 1 } +async fn foo(arg: u32) -> u32 { + bar(arg).await * 2 +} +fn spawn(_: T) {} +fn main() { + spawn(async move { + bar(42).await * 2 + }); +} +"#, + ); + } + + #[test] + fn async_fn_multiple_statements() { + cov_mark::check!(inline_call_async_fn); + check_assist( + inline_call, + r#" +async fn bar(x: u32) -> u32 { x + 1 } +async fn foo(arg: u32) -> u32 { + bar(arg).await; + 42 +} +fn spawn(_: T) {} +fn main() { + spawn(foo$0(42)); +} +"#, + r#" +async fn bar(x: u32) -> u32 { x + 1 } +async fn foo(arg: u32) -> u32 { + bar(arg).await; + 42 +} +fn spawn(_: T) {} +fn main() { + spawn(async move { + bar(42).await; + 42 + }); +} +"#, + ); + } + + #[test] + fn async_fn_with_let_statements() { + cov_mark::check!(inline_call_async_fn); + cov_mark::check!(inline_call_async_fn_with_let_stmts); + check_assist( + inline_call, + r#" +async fn bar(x: u32) -> u32 { x + 1 } +async fn foo(x: u32, y: u32, z: &u32) -> u32 { + bar(x).await; + y + y + *z +} +fn spawn(_: T) {} +fn main() { + let var = 42; + spawn(foo$0(var, var + 1, &var)); +} +"#, + r#" +async fn bar(x: u32) -> u32 { x + 1 } +async fn foo(x: u32, y: u32, z: &u32) -> u32 { + bar(x).await; + y + y + *z +} +fn spawn(_: T) {} +fn main() { + let var = 42; + spawn({ + let y = var + 1; + let z: &u32 = &var; + async move { + bar(var).await; + y + y + *z + } + }); +} "#, ); } diff --git a/crates/ide-assists/src/handlers/move_const_to_impl.rs b/crates/ide-assists/src/handlers/move_const_to_impl.rs index b6027eac55de..e1849eb71d57 100644 --- a/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs @@ -54,7 +54,11 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> // NOTE: We can technically provide this assist for default methods in trait definitions, but // it's somewhat complex to handle it correctly when the const's name conflicts with // supertrait's item. We may want to consider implementing it in the future. - let AssocItemContainer::Impl(impl_) = ctx.sema.to_def(&parent_fn)?.as_assoc_item(db)?.container(db) else { return None; }; + let AssocItemContainer::Impl(impl_) = + ctx.sema.to_def(&parent_fn)?.as_assoc_item(db)?.container(db) + else { + return None; + }; if impl_.trait_(db).is_some() { return None; } diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs index 23153b4c566c..5cc110cf12b1 100644 --- a/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -8,13 +8,10 @@ use ide_db::{ use stdx::to_upper_snake_case; use syntax::{ ast::{self, make, HasName}, - AstNode, WalkEvent, + ted, AstNode, WalkEvent, }; -use crate::{ - assist_context::{AssistContext, Assists}, - utils::{render_snippet, Cursor}, -}; +use crate::assist_context::{AssistContext, Assists}; // Assist: promote_local_to_const // @@ -70,29 +67,33 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) cov_mark::hit!(promote_local_non_const); return None; } - let target = let_stmt.syntax().text_range(); + acc.add( AssistId("promote_local_to_const", AssistKind::Refactor), "Promote local to constant", - target, - |builder| { + let_stmt.syntax().text_range(), + |edit| { let name = to_upper_snake_case(&name.to_string()); let usages = Definition::Local(local).usages(&ctx.sema).all(); if let Some(usages) = usages.references.get(&ctx.file_id()) { + let name = make::name_ref(&name); + for usage in usages { - builder.replace(usage.range, &name); + let Some(usage) = usage.name.as_name_ref().cloned() else { continue }; + let usage = edit.make_mut(usage); + ted::replace(usage.syntax(), name.clone_for_update().syntax()); } } - let item = make::item_const(None, make::name(&name), make::ty(&ty), initializer); - match ctx.config.snippet_cap.zip(item.name()) { - Some((cap, name)) => builder.replace_snippet( - cap, - target, - render_snippet(cap, item.syntax(), Cursor::Before(name.syntax())), - ), - None => builder.replace(target, item.to_string()), + let item = make::item_const(None, make::name(&name), make::ty(&ty), initializer) + .clone_for_update(); + let let_stmt = edit.make_mut(let_stmt); + + if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) { + edit.add_tabstop_before(cap, name); } + + ted::replace(let_stmt.syntax(), item.syntax()); }, ) } @@ -157,6 +158,27 @@ fn foo() { ); } + #[test] + fn multiple_uses() { + check_assist( + promote_local_to_const, + r" +fn foo() { + let x$0 = 0; + let y = x; + let z = (x, x, x, x); +} +", + r" +fn foo() { + const $0X: i32 = 0; + let y = X; + let z = (X, X, X, X); +} +", + ); + } + #[test] fn not_applicable_non_const_meth_call() { cov_mark::check!(promote_local_non_const); diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs index a5c7fea403d3..f222b3eb903c 100644 --- a/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -102,7 +102,7 @@ struct AssignmentsCollector<'a> { assignments: Vec<(ast::BinExpr, ast::Expr)>, } -impl<'a> AssignmentsCollector<'a> { +impl AssignmentsCollector<'_> { fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> { for arm in match_expr.match_arm_list()?.arms() { match arm.expr()? { diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 3bdd795bea8a..c03bc2f41d50 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -73,7 +73,7 @@ pub(crate) fn replace_derive_with_manual_impl( &ctx.sema, current_crate, NameToImport::exact_case_sensitive(path.segments().last()?.to_string()), - items_locator::AssocItemSearch::Exclude, + items_locator::AssocSearchMode::Exclude, Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|item| match item.as_module_def()? { diff --git a/crates/ide-assists/src/handlers/unwrap_result_return_type.rs b/crates/ide-assists/src/handlers/unwrap_result_return_type.rs index 26f3c192617e..f235b554e61f 100644 --- a/crates/ide-assists/src/handlers/unwrap_result_return_type.rs +++ b/crates/ide-assists/src/handlers/unwrap_result_return_type.rs @@ -38,14 +38,18 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<' }; let type_ref = &ret_type.ty()?; - let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { return None; }; + let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { + return None; + }; let result_enum = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; if ret_enum != result_enum { return None; } - let Some(ok_type) = unwrap_result_type(type_ref) else { return None; }; + let Some(ok_type) = unwrap_result_type(type_ref) else { + return None; + }; acc.add( AssistId("unwrap_result_return_type", AssistKind::RefactorRewrite), @@ -130,12 +134,16 @@ fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { // Tries to extract `T` from `Result`. fn unwrap_result_type(ty: &ast::Type) -> Option { - let ast::Type::PathType(path_ty) = ty else { return None; }; + let ast::Type::PathType(path_ty) = ty else { + return None; + }; let path = path_ty.path()?; let segment = path.first_segment()?; let generic_arg_list = segment.generic_arg_list()?; let generic_args: Vec<_> = generic_arg_list.generic_args().collect(); - let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { return None; }; + let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { + return None; + }; ok_type.ty() } diff --git a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index b6c489eb62ee..24c3387457a3 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -6,7 +6,7 @@ use ide_db::{ }; use syntax::{ ast::{self, make, Expr}, - match_ast, AstNode, + match_ast, ted, AstNode, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -52,8 +52,8 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), "Wrap return type in Result", type_ref.syntax().text_range(), - |builder| { - let body = ast::Expr::BlockExpr(body); + |edit| { + let body = edit.make_mut(ast::Expr::BlockExpr(body)); let mut exprs_to_wrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); @@ -70,17 +70,24 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< let ok_wrapped = make::expr_call( make::expr_path(make::ext::ident_path("Ok")), make::arg_list(iter::once(ret_expr_arg.clone())), - ); - builder.replace_ast(ret_expr_arg, ok_wrapped); + ) + .clone_for_update(); + ted::replace(ret_expr_arg.syntax(), ok_wrapped.syntax()); } - match ctx.config.snippet_cap { - Some(cap) => { - let snippet = format!("Result<{type_ref}, ${{0:_}}>"); - builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) - } - None => builder - .replace(type_ref.syntax().text_range(), format!("Result<{type_ref}, _>")), + let new_result_ty = + make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); + let old_result_ty = edit.make_mut(type_ref.clone()); + + ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); + + if let Some(cap) = ctx.config.snippet_cap { + let generic_args = new_result_ty + .syntax() + .descendants() + .find_map(ast::GenericArgList::cast) + .unwrap(); + edit.add_placeholder_snippet(cap, generic_args.generic_args().last().unwrap()); } }, ) diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 111753bf3097..a82f1f9dd8be 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -145,6 +145,7 @@ mod handlers { mod generate_constant; mod generate_default_from_enum_variant; mod generate_default_from_new; + mod generate_delegate_trait; mod generate_deref; mod generate_derive; mod generate_documentation_template; @@ -153,12 +154,12 @@ mod handlers { mod generate_enum_variant; mod generate_from_impl_for_enum; mod generate_function; - mod generate_getter; + mod generate_getter_or_setter; mod generate_impl; mod generate_is_empty_from_len; mod generate_new; - mod generate_setter; mod generate_delegate_methods; + mod generate_trait_from_impl; mod add_return_type; mod inline_call; mod inline_const_as_literal; @@ -251,6 +252,7 @@ mod handlers { generate_constant::generate_constant, generate_default_from_enum_variant::generate_default_from_enum_variant, generate_default_from_new::generate_default_from_new, + generate_delegate_trait::generate_delegate_trait, generate_derive::generate_derive, generate_documentation_template::generate_documentation_template, generate_documentation_template::generate_doc_example, @@ -264,6 +266,7 @@ mod handlers { generate_impl::generate_trait_impl, generate_is_empty_from_len::generate_is_empty_from_len, generate_new::generate_new, + generate_trait_from_impl::generate_trait_from_impl, inline_call::inline_call, inline_call::inline_into_callers, inline_const_as_literal::inline_const_as_literal, @@ -334,9 +337,9 @@ mod handlers { extract_function::extract_function, extract_module::extract_module, // - generate_getter::generate_getter, - generate_getter::generate_getter_mut, - generate_setter::generate_setter, + generate_getter_or_setter::generate_getter, + generate_getter_or_setter::generate_getter_mut, + generate_getter_or_setter::generate_setter, generate_delegate_methods::generate_delegate_methods, generate_deref::generate_deref, // diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index c097e073980c..4d47a199b7c2 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1015,6 +1015,69 @@ impl Person { ) } +#[test] +fn doctest_generate_delegate_trait() { + check_doc_test( + "generate_delegate_trait", + r#####" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } +} +struct B { + a$0: A, +} +"#####, + r#####" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } +} +struct B { + a: A, +} + +impl SomeTrait for B { + type T = ::T; + + fn fn_(arg: u32) -> u32 { + ::fn_(arg) + } + + fn method_(&mut self) -> bool { + ::method_( &mut self.a ) + } +} +"#####, + ) +} + #[test] fn doctest_generate_deref() { check_doc_test( @@ -1429,7 +1492,7 @@ struct Person { } impl Person { - fn set_name(&mut self, name: String) { + fn $0set_name(&mut self, name: String) { self.name = name; } } @@ -1437,6 +1500,62 @@ impl Person { ) } +#[test] +fn doctest_generate_trait_from_impl() { + check_doc_test( + "generate_trait_from_impl", + r#####" +struct Foo([i32; N]); + +macro_rules! const_maker { + ($t:ty, $v:tt) => { + const CONST: $t = $v; + }; +} + +impl Fo$0o { + // Used as an associated constant. + const CONST_ASSOC: usize = N * 4; + + fn create() -> Option<()> { + Some(()) + } + + const_maker! {i32, 7} +} +"#####, + r#####" +struct Foo([i32; N]); + +macro_rules! const_maker { + ($t:ty, $v:tt) => { + const CONST: $t = $v; + }; +} + +trait ${0:TraitName} { + // Used as an associated constant. + const CONST_ASSOC: usize = N * 4; + + fn create() -> Option<()>; + + const_maker! {i32, 7} +} + +impl ${0:TraitName} for Foo { + // Used as an associated constant. + const CONST_ASSOC: usize = N * 4; + + fn create() -> Option<()> { + Some(()) + } + + const_maker! {i32, 7} +} +"#####, + ) +} + #[test] fn doctest_generate_trait_impl() { check_doc_test( diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index d3e75c6da476..1e09894059d5 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -42,7 +42,7 @@ pub(crate) fn complete_mod( } let module_definition_file = - current_module.definition_source(ctx.db).file_id.original_file(ctx.db); + current_module.definition_source_file_id(ctx.db).original_file(ctx.db); let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file)); let directory_to_look_for_submodules = directory_to_look_for_submodules( current_module, diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 7b145f3c14e3..3cb65b2729ac 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -463,7 +463,9 @@ impl CompletionContext<'_> { /// Checks whether this item should be listed in regards to stability. Returns `true` if we should. pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool { - let Some(attrs) = attrs else { return true; }; + let Some(attrs) = attrs else { + return true; + }; !attrs.is_unstable() || self.is_nightly } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index cc5221cfccbd..3ea50659030c 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -243,10 +243,7 @@ fn analyze( let Some(name_like) = find_node_at_offset(&speculative_file, offset) else { let analysis = if let Some(original) = ast::String::cast(original_token.clone()) { - CompletionAnalysis::String { - original, - expanded: ast::String::cast(self_token.clone()), - } + CompletionAnalysis::String { original, expanded: ast::String::cast(self_token.clone()) } } else { // Fix up trailing whitespace problem // #[attr(foo = $0 @@ -736,7 +733,7 @@ fn classify_name_ref( return None; } let parent = match ast::Fn::cast(parent.parent()?) { - Some(x) => x.param_list(), + Some(it) => it.param_list(), None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), }; diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 106d4e1e52f9..2eaa42040a01 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -231,7 +231,7 @@ pub fn resolve_completion_edits( &sema, current_crate, NameToImport::exact_case_sensitive(imported_name), - items_locator::AssocItemSearch::Include, + items_locator::AssocSearchMode::Include, Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()), ); let import = items_with_name diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index c97144b61b6d..1aaf39587265 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/crates/ide-completion/src/tests/attribute.rs @@ -300,6 +300,7 @@ struct Foo; at deprecated at derive macro derive at derive(…) + at derive_const macro derive_const at doc = "…" at doc(alias = "…") at doc(hidden) diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml index 4e75dc4dba54..faec74206811 100644 --- a/crates/ide-db/Cargo.toml +++ b/crates/ide-db/Cargo.toml @@ -21,7 +21,7 @@ once_cell = "1.17.0" either = "1.7.0" itertools = "0.10.5" arrayvec = "0.7.2" -indexmap = "1.9.1" +indexmap = "2.0.0" memchr = "2.5.0" triomphe.workspace = true nohash-hasher.workspace = true diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index 0dd544d0ae23..a0b05c87ae73 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -99,8 +99,8 @@ impl RootDatabase { hir::db::AstIdMapQuery hir::db::ParseMacroExpansionQuery hir::db::InternMacroCallQuery - hir::db::MacroArgTextQuery - hir::db::MacroDefQuery + hir::db::MacroArgNodeQuery + hir::db::DeclMacroExpanderQuery hir::db::MacroExpandQuery hir::db::ExpandProcMacroQuery hir::db::HygieneFrameQuery diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index eba9d8afc40a..1eb8f00020b1 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -9,7 +9,10 @@ use syntax::{ AstToken, SyntaxKind, SyntaxToken, TokenAtOffset, }; -use crate::{defs::Definition, generated, RootDatabase}; +use crate::{ + defs::{Definition, IdentClass}, + generated, RootDatabase, +}; pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option { match item { @@ -109,3 +112,16 @@ pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool { let source_root_id = db.file_source_root(root_file); !db.source_root(source_root_id).is_library } + +pub fn get_definition( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> Option { + for token in sema.descend_into_macros(token) { + let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); + if let Some(&[x]) = def.as_deref() { + return Some(x); + } + } + None +} diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 901d592c6910..e52dc3567754 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -13,7 +13,7 @@ use syntax::{ use crate::{ helpers::item_name, - items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, + items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, RootDatabase, }; @@ -317,7 +317,7 @@ fn path_applicable_imports( // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear // // see also an ignored test under FIXME comment in the qualify_path.rs module - AssocItemSearch::Exclude, + AssocSearchMode::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|item| { @@ -334,7 +334,7 @@ fn path_applicable_imports( sema, current_crate, path_candidate.name.clone(), - AssocItemSearch::Include, + AssocSearchMode::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|item| { @@ -483,7 +483,7 @@ fn trait_applicable_items( sema, current_crate, trait_candidate.assoc_item_name.clone(), - AssocItemSearch::AssocItemsOnly, + AssocSearchMode::AssocItemsOnly, Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|input| item_as_assoc(db, input)) diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 46f1353e2e1b..3f7a3ec2d0fd 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -3,10 +3,7 @@ //! The main reason for this module to exist is the fact that project's items and dependencies' items //! are located in different caches, with different APIs. use either::Either; -use hir::{ - import_map::{self, ImportKind}, - AsAssocItem, Crate, ItemInNs, Semantics, -}; +use hir::{import_map, AsAssocItem, Crate, ItemInNs, Semantics}; use limit::Limit; use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; @@ -14,23 +11,14 @@ use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; /// A value to use, when uncertain which limit to pick. pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40); -/// Three possible ways to search for the name in associated and/or other items. -#[derive(Debug, Clone, Copy)] -pub enum AssocItemSearch { - /// Search for the name in both associated and other items. - Include, - /// Search for the name in other items only. - Exclude, - /// Search for the name in the associated items only. - AssocItemsOnly, -} +pub use import_map::AssocSearchMode; /// Searches for importable items with the given name in the crate and its dependencies. pub fn items_with_name<'a>( sema: &'a Semantics<'_, RootDatabase>, krate: Crate, name: NameToImport, - assoc_item_search: AssocItemSearch, + assoc_item_search: AssocSearchMode, limit: Option, ) -> impl Iterator + 'a { let _p = profile::span("items_with_name").detail(|| { @@ -48,9 +36,7 @@ pub fn items_with_name<'a>( let mut local_query = symbol_index::Query::new(exact_name.clone()); local_query.exact(); - let external_query = import_map::Query::new(exact_name) - .name_only() - .search_mode(import_map::SearchMode::Equals); + let external_query = import_map::Query::new(exact_name); ( local_query, @@ -61,17 +47,8 @@ pub fn items_with_name<'a>( let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone()); let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) - .search_mode(import_map::SearchMode::Fuzzy) - .name_only(); - match assoc_item_search { - AssocItemSearch::Include => {} - AssocItemSearch::Exclude => { - external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem); - } - AssocItemSearch::AssocItemsOnly => { - external_query = external_query.assoc_items_only(); - } - } + .fuzzy() + .assoc_search_mode(assoc_item_search); if fuzzy_search_string.to_lowercase() != fuzzy_search_string { local_query.case_sensitive(); @@ -93,13 +70,15 @@ pub fn items_with_name<'a>( fn find_items<'a>( sema: &'a Semantics<'_, RootDatabase>, krate: Crate, - assoc_item_search: AssocItemSearch, + assoc_item_search: AssocSearchMode, local_query: symbol_index::Query, external_query: import_map::Query, ) -> impl Iterator + 'a { let _p = profile::span("find_items"); let db = sema.db; + // NOTE: `external_query` includes `assoc_item_search`, so we don't need to + // filter on our own. let external_importables = krate.query_external_importables(db, external_query).map(|external_importable| { match external_importable { @@ -112,18 +91,15 @@ fn find_items<'a>( let local_results = local_query .search(&symbol_index::crate_symbols(db, krate)) .into_iter() - .filter_map(|local_candidate| match local_candidate.def { - hir::ModuleDef::Macro(macro_def) => Some(ItemInNs::Macros(macro_def)), - def => Some(ItemInNs::from(def)), + .filter(move |candidate| match assoc_item_search { + AssocSearchMode::Include => true, + AssocSearchMode::Exclude => candidate.def.as_assoc_item(db).is_none(), + AssocSearchMode::AssocItemsOnly => candidate.def.as_assoc_item(db).is_some(), + }) + .map(|local_candidate| match local_candidate.def { + hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), + def => ItemInNs::from(def), }); - external_importables.chain(local_results).filter(move |&item| match assoc_item_search { - AssocItemSearch::Include => true, - AssocItemSearch::Exclude => !is_assoc_item(item, sema.db), - AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db), - }) -} - -fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { - item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some() + external_importables.chain(local_results) } diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index ff1a20f03f44..f27ed485d814 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -200,8 +200,8 @@ impl RootDatabase { hir_db::AstIdMapQuery // hir_db::ParseMacroExpansionQuery // hir_db::InternMacroCallQuery - hir_db::MacroArgTextQuery - hir_db::MacroDefQuery + hir_db::MacroArgNodeQuery + hir_db::DeclMacroExpanderQuery // hir_db::MacroExpandQuery hir_db::ExpandProcMacroQuery hir_db::HygieneFrameQuery diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 73e6a920ee4c..1d0cb426a57c 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -195,7 +195,7 @@ fn postorder(item: &SyntaxNode) -> impl Iterator { }) } -impl<'a> Ctx<'a> { +impl Ctx<'_> { fn apply(&self, item: &SyntaxNode) { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index e8ff107bd495..f3c0f79c5896 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -149,10 +149,8 @@ impl SearchScope { let mut to_visit: Vec<_> = module.children(db).collect(); while let Some(module) = to_visit.pop() { - if let InFile { file_id, value: ModuleSource::SourceFile(_) } = - module.definition_source(db) - { - entries.insert(file_id.original_file(db), None); + if let Some(file_id) = module.as_source_file_id(db) { + entries.insert(file_id, None); } to_visit.extend(module.children(db)); } @@ -340,21 +338,21 @@ pub struct FindUsages<'a> { search_self_mod: bool, } -impl<'a> FindUsages<'a> { +impl FindUsages<'_> { /// Enable searching for `Self` when the definition is a type or `self` for modules. - pub fn include_self_refs(mut self) -> FindUsages<'a> { + pub fn include_self_refs(mut self) -> Self { self.include_self_kw_refs = def_to_ty(self.sema, &self.def); self.search_self_mod = true; self } /// Limit the search to a given [`SearchScope`]. - pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> { + pub fn in_scope(self, scope: SearchScope) -> Self { self.set_scope(Some(scope)) } /// Limit the search to a given [`SearchScope`]. - pub fn set_scope(mut self, scope: Option) -> FindUsages<'a> { + pub fn set_scope(mut self, scope: Option) -> Self { assert!(self.scope.is_none()); self.scope = scope; self diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index 061fb0f05cfb..fad0ca51a025 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs @@ -9,7 +9,10 @@ use crate::SnippetCap; use base_db::{AnchoredPathBuf, FileId}; use nohash_hasher::IntMap; use stdx::never; -use syntax::{algo, ast, ted, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; +use syntax::{ + algo, ast, ted, AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, + TextSize, +}; use text_edit::{TextEdit, TextEditBuilder}; #[derive(Default, Debug, Clone)] @@ -237,19 +240,31 @@ impl SourceChangeBuilder { /// Adds a tabstop snippet to place the cursor before `node` pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Before(node.syntax().clone())); + self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into())); } /// Adds a tabstop snippet to place the cursor after `node` pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::After(node.syntax().clone())); + self.add_snippet(PlaceSnippet::After(node.syntax().clone().into())); + } + + /// Adds a tabstop snippet to place the cursor before `token` + pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { + assert!(token.parent().is_some()); + self.add_snippet(PlaceSnippet::Before(token.clone().into())); + } + + /// Adds a tabstop snippet to place the cursor after `token` + pub fn add_tabstop_after_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { + assert!(token.parent().is_some()); + self.add_snippet(PlaceSnippet::After(token.clone().into())); } /// Adds a snippet to move the cursor selected over `node` pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Over(node.syntax().clone())) + self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into())) } fn add_snippet(&mut self, snippet: PlaceSnippet) { @@ -282,38 +297,40 @@ impl From for SourceChange { } enum PlaceSnippet { - /// Place a tabstop before a node - Before(SyntaxNode), - /// Place a tabstop before a node - After(SyntaxNode), - /// Place a placeholder snippet in place of the node - Over(SyntaxNode), + /// Place a tabstop before an element + Before(SyntaxElement), + /// Place a tabstop before an element + After(SyntaxElement), + /// Place a placeholder snippet in place of the element + Over(SyntaxElement), } impl PlaceSnippet { - /// Places the snippet before or over a node with the given tab stop index + /// Places the snippet before or over an element with the given tab stop index fn place(self, order: usize) { - // ensure the target node is still attached + // ensure the target element is still attached match &self { - PlaceSnippet::Before(node) | PlaceSnippet::After(node) | PlaceSnippet::Over(node) => { - // node should still be in the tree, but if it isn't + PlaceSnippet::Before(element) + | PlaceSnippet::After(element) + | PlaceSnippet::Over(element) => { + // element should still be in the tree, but if it isn't // then it's okay to just ignore this place - if stdx::never!(node.parent().is_none()) { + if stdx::never!(element.parent().is_none()) { return; } } } match self { - PlaceSnippet::Before(node) => { - ted::insert_raw(ted::Position::before(&node), Self::make_tab_stop(order)); + PlaceSnippet::Before(element) => { + ted::insert_raw(ted::Position::before(&element), Self::make_tab_stop(order)); } - PlaceSnippet::After(node) => { - ted::insert_raw(ted::Position::after(&node), Self::make_tab_stop(order)); + PlaceSnippet::After(element) => { + ted::insert_raw(ted::Position::after(&element), Self::make_tab_stop(order)); } - PlaceSnippet::Over(node) => { - let position = ted::Position::before(&node); - node.detach(); + PlaceSnippet::Over(element) => { + let position = ted::Position::before(&element); + element.detach(); let snippet = ast::SourceFile::parse(&format!("${{{order}:_}}")) .syntax_node() @@ -321,7 +338,7 @@ impl PlaceSnippet { let placeholder = snippet.descendants().find_map(ast::UnderscoreExpr::cast).unwrap(); - ted::replace(placeholder.syntax(), node); + ted::replace(placeholder.syntax(), element); ted::insert_raw(position, snippet); } diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index e18624fcc267..14aa3940199a 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -16,6 +16,7 @@ cov-mark = "2.0.0-pre.1" either = "1.7.0" itertools = "0.10.5" serde_json = "1.0.86" +once_cell = "1.17.0" # local deps profile.workspace = true diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index 30576c71fb7c..491005403882 100644 --- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: break-outside-of-loop // @@ -13,10 +13,11 @@ pub(crate) fn break_outside_of_loop( let construct = if d.is_break { "break" } else { "continue" }; format!("{construct} outside of loop") }; - Diagnostic::new( - "break-outside-of-loop", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0268"), message, - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs index d2f27664d6f9..e1e5db91c54c 100644 --- a/crates/ide-diagnostics/src/handlers/expected_function.rs +++ b/crates/ide-diagnostics/src/handlers/expected_function.rs @@ -1,6 +1,6 @@ use hir::HirDisplay; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: expected-function // @@ -9,10 +9,11 @@ pub(crate) fn expected_function( ctx: &DiagnosticsContext<'_>, d: &hir::ExpectedFunction, ) -> Diagnostic { - Diagnostic::new( - "expected-function", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0618"), format!("expected function, found {}", d.found.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.call.clone().map(|it| it.into())).range, + d.call.clone().map(|it| it.into()), ) .experimental() } diff --git a/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/crates/ide-diagnostics/src/handlers/field_shorthand.rs index 2b7105362503..3b69640af9b8 100644 --- a/crates/ide-diagnostics/src/handlers/field_shorthand.rs +++ b/crates/ide-diagnostics/src/handlers/field_shorthand.rs @@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; use syntax::{ast, match_ast, AstNode, SyntaxNode}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode}; pub(crate) fn field_shorthand(acc: &mut Vec, file_id: FileId, node: &SyntaxNode) { match_ast! { @@ -46,14 +46,17 @@ fn check_expr_field_shorthand( let field_range = record_field.syntax().text_range(); acc.push( - Diagnostic::new("use-field-shorthand", "Shorthand struct initialization", field_range) - .severity(Severity::WeakWarning) - .with_fixes(Some(vec![fix( - "use_expr_field_shorthand", - "Use struct shorthand initialization", - SourceChange::from_text_edit(file_id, edit), - field_range, - )])), + Diagnostic::new( + DiagnosticCode::Clippy("redundant_field_names"), + "Shorthand struct initialization", + field_range, + ) + .with_fixes(Some(vec![fix( + "use_expr_field_shorthand", + "Use struct shorthand initialization", + SourceChange::from_text_edit(file_id, edit), + field_range, + )])), ); } } @@ -87,14 +90,17 @@ fn check_pat_field_shorthand( let field_range = record_pat_field.syntax().text_range(); acc.push( - Diagnostic::new("use-field-shorthand", "Shorthand struct pattern", field_range) - .severity(Severity::WeakWarning) - .with_fixes(Some(vec![fix( - "use_pat_field_shorthand", - "Use struct field shorthand", - SourceChange::from_text_edit(file_id, edit), - field_range, - )])), + Diagnostic::new( + DiagnosticCode::Clippy("redundant_field_names"), + "Shorthand struct pattern", + field_range, + ) + .with_fixes(Some(vec![fix( + "use_pat_field_shorthand", + "Use struct field shorthand", + SourceChange::from_text_edit(file_id, edit), + field_range, + )])), ); } } diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs index f558b7256a4c..9eb763d3e2c2 100644 --- a/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -1,7 +1,7 @@ use cfg::DnfExpr; use stdx::format_to; -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: inactive-code // @@ -27,13 +27,12 @@ pub(crate) fn inactive_code( format_to!(message, ": {}", inactive); } } - + // FIXME: This shouldn't be a diagnostic let res = Diagnostic::new( - "inactive-code", + DiagnosticCode::Ra("inactive-code", Severity::WeakWarning), message, ctx.sema.diagnostics_display_range(d.node.clone()).range, ) - .severity(Severity::WeakWarning) .with_unused(true); Some(res) } diff --git a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs index 72af9ebfcbb6..4afb4db03bda 100644 --- a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs +++ b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs @@ -1,17 +1,17 @@ use hir::InFile; -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: incoherent-impl // // This diagnostic is triggered if the targe type of an impl is from a foreign crate. pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic { - Diagnostic::new( - "incoherent-impl", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0210"), format!("cannot define inherent `impl` for foreign type"), - ctx.sema.diagnostics_display_range(InFile::new(d.file_id, d.impl_.clone().into())).range, + InFile::new(d.file_id, d.impl_.clone().into()), ) - .severity(Severity::Error) } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 90279e14536b..92fd4f71ca5d 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -1,4 +1,4 @@ -use hir::{db::ExpandDatabase, InFile}; +use hir::{db::ExpandDatabase, CaseType, InFile}; use ide_db::{assists::Assist, defs::NameClass}; use syntax::AstNode; @@ -6,23 +6,29 @@ use crate::{ // references::rename::rename_with_semantics, unresolved_fix, Diagnostic, + DiagnosticCode, DiagnosticsContext, - Severity, }; // Diagnostic: incorrect-ident-case // // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { - Diagnostic::new( - "incorrect-ident-case", + let code = match d.expected_case { + CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"), + CaseType::UpperSnakeCase => DiagnosticCode::RustcLint("non_upper_case_globals"), + // The name is lying. It also covers variants, traits, ... + CaseType::UpperCamelCase => DiagnosticCode::RustcLint("non_camel_case_types"), + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + code, format!( "{} `{}` should have {} name, e.g. `{}`", d.ident_type, d.ident_text, d.expected_case, d.suggested_text ), - ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range, + InFile::new(d.file, d.ident.clone().into()), ) - .severity(Severity::WeakWarning) .with_fixes(fixes(ctx, d)) } @@ -149,7 +155,7 @@ impl TestStruct { check_diagnostics( r#" fn FOO() {} -// ^^^ 💡 weak: Function `FOO` should have snake_case name, e.g. `foo` +// ^^^ 💡 warn: Function `FOO` should have snake_case name, e.g. `foo` "#, ); check_fix(r#"fn FOO$0() {}"#, r#"fn foo() {}"#); @@ -160,7 +166,7 @@ fn FOO() {} check_diagnostics( r#" fn NonSnakeCaseName() {} -// ^^^^^^^^^^^^^^^^ 💡 weak: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` +// ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` "#, ); } @@ -170,10 +176,10 @@ fn NonSnakeCaseName() {} check_diagnostics( r#" fn foo(SomeParam: u8) {} - // ^^^^^^^^^ 💡 weak: Parameter `SomeParam` should have snake_case name, e.g. `some_param` + // ^^^^^^^^^ 💡 warn: Parameter `SomeParam` should have snake_case name, e.g. `some_param` fn foo2(ok_param: &str, CAPS_PARAM: u8) {} - // ^^^^^^^^^^ 💡 weak: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` + // ^^^^^^^^^^ 💡 warn: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` "#, ); } @@ -184,9 +190,9 @@ fn foo2(ok_param: &str, CAPS_PARAM: u8) {} r#" fn foo() { let SOME_VALUE = 10; - // ^^^^^^^^^^ 💡 weak: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` + // ^^^^^^^^^^ 💡 warn: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` let AnotherValue = 20; - // ^^^^^^^^^^^^ 💡 weak: Variable `AnotherValue` should have snake_case name, e.g. `another_value` + // ^^^^^^^^^^^^ 💡 warn: Variable `AnotherValue` should have snake_case name, e.g. `another_value` } "#, ); @@ -197,10 +203,10 @@ fn foo() { check_diagnostics( r#" struct non_camel_case_name {} - // ^^^^^^^^^^^^^^^^^^^ 💡 weak: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` + // ^^^^^^^^^^^^^^^^^^^ 💡 warn: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` struct SCREAMING_CASE {} - // ^^^^^^^^^^^^^^ 💡 weak: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` + // ^^^^^^^^^^^^^^ 💡 warn: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` "#, ); } @@ -219,7 +225,7 @@ struct AABB {} check_diagnostics( r#" struct SomeStruct { SomeField: u8 } - // ^^^^^^^^^ 💡 weak: Field `SomeField` should have snake_case name, e.g. `some_field` + // ^^^^^^^^^ 💡 warn: Field `SomeField` should have snake_case name, e.g. `some_field` "#, ); } @@ -229,10 +235,10 @@ struct SomeStruct { SomeField: u8 } check_diagnostics( r#" enum some_enum { Val(u8) } - // ^^^^^^^^^ 💡 weak: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` + // ^^^^^^^^^ 💡 warn: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` enum SOME_ENUM {} - // ^^^^^^^^^ 💡 weak: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` + // ^^^^^^^^^ 💡 warn: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` "#, ); } @@ -251,7 +257,7 @@ enum AABB {} check_diagnostics( r#" enum SomeEnum { SOME_VARIANT(u8) } - // ^^^^^^^^^^^^ 💡 weak: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` + // ^^^^^^^^^^^^ 💡 warn: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` "#, ); } @@ -261,7 +267,7 @@ enum SomeEnum { SOME_VARIANT(u8) } check_diagnostics( r#" const some_weird_const: u8 = 10; - // ^^^^^^^^^^^^^^^^ 💡 weak: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` + // ^^^^^^^^^^^^^^^^ 💡 warn: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` "#, ); } @@ -271,7 +277,7 @@ const some_weird_const: u8 = 10; check_diagnostics( r#" static some_weird_const: u8 = 10; - // ^^^^^^^^^^^^^^^^ 💡 weak: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` + // ^^^^^^^^^^^^^^^^ 💡 warn: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` "#, ); } @@ -281,13 +287,13 @@ static some_weird_const: u8 = 10; check_diagnostics( r#" struct someStruct; - // ^^^^^^^^^^ 💡 weak: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` + // ^^^^^^^^^^ 💡 warn: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` impl someStruct { fn SomeFunc(&self) { - // ^^^^^^^^ 💡 weak: Function `SomeFunc` should have snake_case name, e.g. `some_func` + // ^^^^^^^^ 💡 warn: Function `SomeFunc` should have snake_case name, e.g. `some_func` let WHY_VAR_IS_CAPS = 10; - // ^^^^^^^^^^^^^^^ 💡 weak: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` + // ^^^^^^^^^^^^^^^ 💡 warn: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` } } "#, @@ -319,7 +325,7 @@ enum Option { Some, None } fn main() { match Option::None { SOME_VAR @ None => (), - // ^^^^^^^^ 💡 weak: Variable `SOME_VAR` should have snake_case name, e.g. `some_var` + // ^^^^^^^^ 💡 warn: Variable `SOME_VAR` should have snake_case name, e.g. `some_var` Some => (), } } @@ -461,7 +467,7 @@ mod CheckNonstandardStyle { #[allow(bad_style)] mod CheckBadStyle { - fn HiImABadFnName() {} + struct fooo; } mod F { @@ -483,4 +489,60 @@ pub static SomeStatic: u8 = 10; "#, ); } + + #[test] + fn deny_attributes() { + check_diagnostics( + r#" +#[deny(non_snake_case)] +fn NonSnakeCaseName(some_var: u8) -> u8 { + //^^^^^^^^^^^^^^^^ 💡 error: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + // cov_flags generated output from elsewhere in this file + extern "C" { + #[no_mangle] + static lower_case: u8; + } + + let OtherVar = some_var + 1; + //^^^^^^^^ 💡 error: Variable `OtherVar` should have snake_case name, e.g. `other_var` + OtherVar +} + +#[deny(nonstandard_style)] +mod CheckNonstandardStyle { + fn HiImABadFnName() {} + //^^^^^^^^^^^^^^ 💡 error: Function `HiImABadFnName` should have snake_case name, e.g. `hi_im_abad_fn_name` +} + +#[deny(warnings)] +mod CheckBadStyle { + struct fooo; + //^^^^ 💡 error: Structure `fooo` should have CamelCase name, e.g. `Fooo` +} + +mod F { + #![deny(non_snake_case)] + fn CheckItWorksWithModAttr() {} + //^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Function `CheckItWorksWithModAttr` should have snake_case name, e.g. `check_it_works_with_mod_attr` +} + +#[deny(non_snake_case, non_camel_case_types)] +pub struct some_type { + //^^^^^^^^^ 💡 error: Structure `some_type` should have CamelCase name, e.g. `SomeType` + SOME_FIELD: u8, + //^^^^^^^^^^ 💡 error: Field `SOME_FIELD` should have snake_case name, e.g. `some_field` + SomeField: u16, + //^^^^^^^^^ 💡 error: Field `SomeField` should have snake_case name, e.g. `some_field` +} + +#[deny(non_upper_case_globals)] +pub const some_const: u8 = 10; + //^^^^^^^^^^ 💡 error: Constant `some_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST` + +#[deny(non_upper_case_globals)] +pub static SomeStatic: u8 = 10; + //^^^^^^^^^^ 💡 error: Static variable `SomeStatic` should have UPPER_SNAKE_CASE name, e.g. `SOME_STATIC` + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs index c779266bc97b..1ec17952b238 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: invalid-derive-target // @@ -11,11 +11,10 @@ pub(crate) fn invalid_derive_target( let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range; Diagnostic::new( - "invalid-derive-target", + DiagnosticCode::RustcHardError("E0774"), "`derive` may only be applied to `struct`s, `enum`s and `union`s", display_range, ) - .severity(Severity::Error) } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 04ce1e0feeed..a337e2660d6a 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -17,7 +17,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsConfig, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsConfig, Severity}; #[derive(Default)] struct State { @@ -117,11 +117,10 @@ pub(crate) fn json_in_items( edit.insert(range.start(), state.result); acc.push( Diagnostic::new( - "json-is-not-rust", + DiagnosticCode::Ra("json-is-not-rust", Severity::WeakWarning), "JSON syntax is not valid as a Rust item", range, ) - .severity(Severity::WeakWarning) .with_fixes(Some(vec![{ let mut scb = SourceChangeBuilder::new(file_id); let scope = match import_scope { diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index 7547779a95c7..937e2f96642e 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: macro-error // @@ -6,7 +6,12 @@ use crate::{Diagnostic, DiagnosticsContext}; pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); - Diagnostic::new("macro-error", d.message.clone(), display_range).experimental() + Diagnostic::new( + DiagnosticCode::Ra("macro-error", Severity::Error), + d.message.clone(), + display_range, + ) + .experimental() } // Diagnostic: macro-error @@ -16,7 +21,12 @@ pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefErr // Use more accurate position if available. let display_range = ctx.resolve_precise_location(&d.node.clone().map(|it| it.syntax_node_ptr()), d.name); - Diagnostic::new("macro-def-error", d.message.clone(), display_range).experimental() + Diagnostic::new( + DiagnosticCode::Ra("macro-def-error", Severity::Error), + d.message.clone(), + display_range, + ) + .experimental() } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/crates/ide-diagnostics/src/handlers/malformed_derive.rs index cd48bdba07e5..fc57dde69f2a 100644 --- a/crates/ide-diagnostics/src/handlers/malformed_derive.rs +++ b/crates/ide-diagnostics/src/handlers/malformed_derive.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: malformed-derive // @@ -10,11 +10,10 @@ pub(crate) fn malformed_derive( let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range; Diagnostic::new( - "malformed-derive", + DiagnosticCode::RustcHardError("E0777"), "malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`", display_range, ) - .severity(Severity::Error) } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index c5db8c3741b8..6238c7e09e99 100644 --- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -3,7 +3,7 @@ use syntax::{ AstNode, TextRange, }; -use crate::{adjusted_display_range, Diagnostic, DiagnosticsContext}; +use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: mismatched-arg-count // @@ -14,7 +14,7 @@ pub(crate) fn mismatched_arg_count( ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; let message = format!("expected {} argument{s}, found {}", d.expected, d.found); - Diagnostic::new("mismatched-arg-count", message, invalid_args_range(ctx, d)) + Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d)) } fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange { diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 60ccc41df01c..bb0e36ff3a1e 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -15,7 +15,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsContext}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: missing-fields // @@ -42,7 +42,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())), ); - Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range) + Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr) .with_fixes(fixes(ctx, d)) } diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 3f13b97a4473..82a9a3bd5595 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: missing-match-arm // @@ -7,10 +7,11 @@ pub(crate) fn missing_match_arms( ctx: &DiagnosticsContext<'_>, d: &hir::MissingMatchArms, ) -> Diagnostic { - Diagnostic::new( - "missing-match-arm", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0004"), format!("missing match arm: {}", d.uncovered_patterns), - ctx.sema.diagnostics_display_range(d.scrutinee_expr.clone().map(Into::into)).range, + d.scrutinee_expr.clone().map(Into::into), ) } diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 2026b6fcef53..70b26009bae0 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -4,16 +4,17 @@ use syntax::{ast, SyntaxNode}; use syntax::{match_ast, AstNode}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsContext}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: missing-unsafe // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { - Diagnostic::new( - "missing-unsafe", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0133"), "this operation is unsafe and requires an unsafe function or block", - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) } diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 32e321107e6f..3aa4aa97026a 100644 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -1,14 +1,15 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; use hir::HirDisplay; // Diagnostic: moved-out-of-ref // // This diagnostic is triggered on moving non copy things out of references. pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic { - Diagnostic::new( - "moved-out-of-ref", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0507"), format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.span.clone()).range, + d.span.clone(), ) .experimental() // spans are broken, and I'm not sure how precise we can detect copy types } diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index f61460e317f7..e0c3bedce46f 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -2,7 +2,7 @@ use ide_db::source_change::SourceChange; use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsContext, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: need-mut // @@ -29,13 +29,15 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno use_range, )]) })(); - Diagnostic::new( - "need-mut", + Diagnostic::new_with_syntax_node_ptr( + ctx, + // FIXME: `E0384` is not the only error that this diagnostic handles + DiagnosticCode::RustcHardError("E0384"), format!( "cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db).display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.span.clone()).range, + d.span.clone(), ) .with_fixes(fixes) } @@ -68,12 +70,12 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - Diagnostic::new( - "unused-mut", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_mut"), "variable does not need to be mutable", - ctx.sema.diagnostics_display_range(ast).range, + ast, ) - .severity(Severity::WeakWarning) .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive. .with_fixes(fixes) } @@ -93,7 +95,7 @@ mod tests { fn f(_: i32) {} fn main() { let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, @@ -268,7 +270,7 @@ fn main() { fn f(_: i32) {} fn main() { let mut x = (2, 7); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x.1); } "#, @@ -302,7 +304,7 @@ fn main() { r#" fn main() { let mut x = &mut 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable *x = 5; } "#, @@ -346,7 +348,7 @@ fn main() { r#" //- minicore: copy, builtin_impls fn clone(mut i: &!) -> ! { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable *i } "#, @@ -360,7 +362,7 @@ fn main() { //- minicore: option fn main() { let mut v = &mut Some(2); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let _ = || match v { Some(k) => { *k = 5; @@ -386,7 +388,7 @@ fn main() { fn main() { match (2, 3) { (x, mut y) => { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x = 7; //^^^^^ 💡 error: cannot mutate immutable variable `x` } @@ -407,7 +409,7 @@ fn main() { fn main() { return; let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -417,7 +419,7 @@ fn main() { fn main() { loop {} let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -438,7 +440,7 @@ fn main(b: bool) { g(); } let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -452,7 +454,7 @@ fn main(b: bool) { return; } let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -466,7 +468,7 @@ fn main(b: bool) { fn f(_: i32) {} fn main() { let mut x; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x = 5; f(x); } @@ -477,7 +479,7 @@ fn main() { fn f(_: i32) {} fn main(b: bool) { let mut x; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable if b { x = 1; } else { @@ -552,15 +554,15 @@ fn f(_: i32) {} fn main() { loop { let mut x = 1; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x); if let mut y = 2 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(y); } match 3 { mut z => f(z), - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable } } } @@ -577,9 +579,9 @@ fn main() { loop { let c @ ( mut b, - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable mut d - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable ); a = 1; //^^^^^ 💡 error: cannot mutate immutable variable `a` @@ -597,7 +599,7 @@ fn main() { check_diagnostics( r#" fn f(mut x: i32) { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable } "#, ); @@ -640,7 +642,7 @@ fn f() { //- minicore: iterators, copy fn f(x: [(i32, u8); 10]) { for (a, mut b) in x { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` } @@ -657,9 +659,9 @@ fn f(x: [(i32, u8); 10]) { fn f(x: [(i32, u8); 10]) { let mut it = x.into_iter(); while let Some((a, mut b)) = it.next() { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable while let Some((c, mut d)) = it.next() { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` c = 2; @@ -683,7 +685,7 @@ fn f() { let x = &mut x; //^^^^^^ 💡 error: cannot mutate immutable variable `x` let mut x = x; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x[2] = 5; } "#, @@ -711,13 +713,13 @@ impl IndexMut for Foo { } fn f() { let mut x = Foo; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y = &x[2]; let x = Foo; let y = &mut x[2]; //^💡 error: cannot mutate immutable variable `x` let mut x = &mut Foo; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y: &mut (i32, u8) = &mut x[2]; let x = Foo; let ref mut y = x[7]; @@ -731,7 +733,7 @@ fn f() { } let mut x = Foo; let mut i = 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y = &mut x[i]; } "#, @@ -759,7 +761,7 @@ impl DerefMut for Foo { } fn f() { let mut x = Foo; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y = &*x; let x = Foo; let y = &mut *x; @@ -790,9 +792,25 @@ fn f() { fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x); } +"#, + ); + check_diagnostics( + r#" +struct Foo(i32); + +const X: Foo = Foo(5); +const Y: Foo = Foo(12); + +const fn f(mut a: Foo) -> bool { + //^^^^^ 💡 warn: variable does not need to be mutable + match a { + X | Y => true, + _ => false, + } +} "#, ); } @@ -842,7 +860,7 @@ pub struct TreeLeaf { pub fn test() { let mut tree = Tree::Leaf( - //^^^^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^^^^ 💡 warn: variable does not need to be mutable TreeLeaf { depth: 0, data: 0 @@ -859,7 +877,7 @@ pub fn test() { r#" //- minicore: fn fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x(2) } fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { @@ -867,11 +885,11 @@ fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { //^ 💡 error: cannot mutate immutable variable `x` } fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x(2) } fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x(2) } "#, @@ -915,14 +933,14 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { //- minicore: copy, fn fn f() { let mut x = 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let mut y = 2; y = 7; let closure = || { let mut z = 8; z = 3; let mut k = z; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable }; } "#, @@ -949,7 +967,7 @@ fn f() { fn f() { struct X; let mut x = X; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let c1 = || x; let mut x = X; let c2 = || { x = X; x }; @@ -965,12 +983,12 @@ fn f() { fn f() { let mut x = &mut 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { *x = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { *x = 2; &x; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` @@ -979,12 +997,12 @@ fn f() { let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = move || { *x = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut X(1, 2); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { x.0 = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` @@ -1001,7 +1019,7 @@ fn f() { fn x(t: &[u8]) { match t { &[a, mut b] | &[a, _, mut b] => { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` @@ -1055,7 +1073,7 @@ fn f() { *x = 7; //^^^^^^ 💡 error: cannot mutate immutable variable `x` let mut y = Box::new(5); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable *x = *y; //^^^^^^^ 💡 error: cannot mutate immutable variable `x` let x = Box::new(5); @@ -1066,6 +1084,33 @@ fn f() { ); } + #[test] + fn regression_15143() { + check_diagnostics( + r#" + trait Tr { + type Ty; + } + + struct A; + + impl Tr for A { + type Ty = (u32, i64); + } + + struct B { + f: ::Ty, + } + + fn main(b: B) { + let f = b.f.0; + f = 5; + //^^^^^ 💡 error: cannot mutate immutable variable `f` + } + "#, + ); + } + #[test] fn allow_unused_mut_for_identifiers_starting_with_underline() { check_diagnostics( @@ -1080,17 +1125,51 @@ fn main() { } #[test] - fn respect_allow_unused_mut() { - // FIXME: respect + fn respect_lint_attributes_for_unused_mut() { check_diagnostics( r#" fn f(_: i32) {} fn main() { #[allow(unused_mut)] let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable f(x); } + +fn main2() { + #[deny(unused_mut)] + let mut x = 2; + //^^^^^ 💡 error: variable does not need to be mutable + f(x); +} +"#, + ); + check_diagnostics( + r#" +macro_rules! mac { + ($($x:expr),*$(,)*) => ({ + #[allow(unused_mut)] + let mut vec = 2; + vec + }); +} + +fn main2() { + let mut x = mac![]; + //^^^^^ 💡 warn: variable does not need to be mutable +} + "#, + ); + } + + #[test] + fn regression_15099() { + check_diagnostics( + r#" +//- minicore: iterator, range +fn f() { + loop {} + for _ in 0..2 {} +} "#, ); } diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index a39eceab2437..a34a5824f296 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -6,16 +6,17 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Assist, Diagnostic, DiagnosticsContext}; +use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: no-such-field // // This diagnostic is triggered if created structure does not have field provided in record. pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { - Diagnostic::new( - "no-such-field", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0559"), "no such field", - ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range, + d.field.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) } diff --git a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 4cd85a479a06..c44d28e77f6d 100644 --- a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -1,6 +1,6 @@ use either::Either; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: private-assoc-item // @@ -16,8 +16,9 @@ pub(crate) fn private_assoc_item( .name(ctx.sema.db) .map(|name| format!("`{}` ", name.display(ctx.sema.db))) .unwrap_or_default(); - Diagnostic::new( - "private-assoc-item", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0624"), format!( "{} {}is private", match d.item { @@ -27,15 +28,13 @@ pub(crate) fn private_assoc_item( }, name, ), - ctx.sema - .diagnostics_display_range(d.expr_or_pat.clone().map(|it| match it { + d.expr_or_pat.clone().map(|it| match it { + Either::Left(it) => it.into(), + Either::Right(it) => match it { Either::Left(it) => it.into(), - Either::Right(it) => match it { - Either::Left(it) => it.into(), - Either::Right(it) => it.into(), - }, - })) - .range, + Either::Right(it) => it.into(), + }, + }), ) } diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs index de7f51f693c4..553defcf9905 100644 --- a/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/crates/ide-diagnostics/src/handlers/private_field.rs @@ -1,18 +1,19 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: private-field // // This diagnostic is triggered if the accessed field is not visible from the current module. pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic { // FIXME: add quickfix - Diagnostic::new( - "private-field", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0616"), format!( "field `{}` of `{}` is private", d.field.name(ctx.sema.db).display(ctx.sema.db), d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index d3eda3c5ebce..083ef3e8dc16 100644 --- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -6,7 +6,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity}; +use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: replace-filter-map-next-with-find-map // @@ -15,12 +15,12 @@ pub(crate) fn replace_filter_map_next_with_find_map( ctx: &DiagnosticsContext<'_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Diagnostic { - Diagnostic::new( - "replace-filter-map-next-with-find-map", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::Clippy("filter_map_next"), "replace filter_map(..).next() with find_map(..)", - ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range, + InFile::new(d.file, d.next_expr.clone().into()), ) - .severity(Severity::WeakWarning) .with_fixes(fixes(ctx, d)) } @@ -64,7 +64,7 @@ mod tests { pub(crate) fn check_diagnostics(ra_fixture: &str) { let mut config = DiagnosticsConfig::test_sample(); config.disabled.insert("inactive-code".to_string()); - config.disabled.insert("unresolved-method".to_string()); + config.disabled.insert("E0599".to_string()); check_diagnostics_with_config(config, ra_fixture) } @@ -139,4 +139,33 @@ fn foo() { "#, ) } + + #[test] + fn respect_lint_attributes_for_clippy_equivalent() { + check_diagnostics( + r#" +//- minicore: iterators + +fn foo() { + #[allow(clippy::filter_map_next)] + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} + +#[deny(clippy::filter_map_next)] +fn foo() { + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: replace filter_map(..).next() with find_map(..) + +fn foo() { + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) + +#[warn(clippy::filter_map_next)] +fn foo() { + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warn: replace filter_map(..).next() with find_map(..) + +"#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index c28f98d8333e..15bd28c00dfd 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -7,7 +7,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext}; +use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: type-mismatch // @@ -39,7 +39,7 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) } }; let mut diag = Diagnostic::new( - "type-mismatch", + DiagnosticCode::RustcHardError("E0308"), format!( "expected {}, found {}", d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId), diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index e12bbcf68206..4af67227115d 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -7,7 +7,7 @@ use ide_db::{ use syntax::AstNode; use text_edit::TextEdit; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: typed-hole // @@ -26,7 +26,8 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di ) }; - Diagnostic::new("typed-hole", message, display_range.range).with_fixes(fixes) + Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range.range) + .with_fixes(fixes) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option> { diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs index 034e4fcfb85b..7de9a9a323ec 100644 --- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: undeclared-label pub(crate) fn undeclared_label( @@ -6,10 +6,11 @@ pub(crate) fn undeclared_label( d: &hir::UndeclaredLabel, ) -> Diagnostic { let name = &d.name; - Diagnostic::new( - "undeclared-label", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("undeclared-label"), format!("use of undeclared label `{}`", name.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, + d.node.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs index e879de75cd84..bcce72a7d010 100644 --- a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs +++ b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: unimplemented-builtin-macro // @@ -7,10 +7,10 @@ pub(crate) fn unimplemented_builtin_macro( ctx: &DiagnosticsContext<'_>, d: &hir::UnimplementedBuiltinMacro, ) -> Diagnostic { - Diagnostic::new( - "unimplemented-builtin-macro", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::Ra("unimplemented-builtin-macro", Severity::WeakWarning), "unimplemented built-in macro".to_string(), - ctx.sema.diagnostics_display_range(d.node.clone()).range, + d.node.clone(), ) - .severity(Severity::WeakWarning) } diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 271e7ce73bcd..e04f27c27fdf 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -14,7 +14,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity}; +use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: unlinked-file // @@ -46,8 +46,7 @@ pub(crate) fn unlinked_file( .unwrap_or(range); acc.push( - Diagnostic::new("unlinked-file", message, range) - .severity(Severity::WeakWarning) + Diagnostic::new(DiagnosticCode::Ra("unlinked-file", Severity::WeakWarning), message, range) .with_fixes(fixes), ); } @@ -119,10 +118,11 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { stack.pop(); 'crates: for &krate in ctx.sema.db.relevant_crates(parent_id).iter() { let crate_def_map = ctx.sema.db.crate_def_map(krate); - let Some((_, module)) = - crate_def_map.modules() - .find(|(_, module)| module.origin.file_id() == Some(parent_id) && !module.origin.is_inline()) - else { continue }; + let Some((_, module)) = crate_def_map.modules().find(|(_, module)| { + module.origin.file_id() == Some(parent_id) && !module.origin.is_inline() + }) else { + continue; + }; if stack.is_empty() { return make_fixes( diff --git a/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/crates/ide-diagnostics/src/handlers/unreachable_label.rs index 9fedadeae035..1c5d6cd0909f 100644 --- a/crates/ide-diagnostics/src/handlers/unreachable_label.rs +++ b/crates/ide-diagnostics/src/handlers/unreachable_label.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unreachable-label pub(crate) fn unreachable_label( @@ -6,10 +6,11 @@ pub(crate) fn unreachable_label( d: &hir::UnreachableLabel, ) -> Diagnostic { let name = &d.name; - Diagnostic::new( - "unreachable-label", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0767"), format!("use of unreachable label `{}`", name.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, + d.node.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs index 74e4a69c64b7..f8265b63275f 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-extern-crate // @@ -7,10 +7,11 @@ pub(crate) fn unresolved_extern_crate( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedExternCrate, ) -> Diagnostic { - Diagnostic::new( - "unresolved-extern-crate", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("unresolved-extern-crate"), "unresolved extern crate", - ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, + d.decl.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 5e4efa41fd9c..0758706e45a2 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ast, AstNode, AstPtr}; use text_edit::TextEdit; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-field // @@ -22,14 +22,15 @@ pub(crate) fn unresolved_field( } else { "" }; - Diagnostic::new( - "unresolved-field", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0559"), format!( "no field `{}` on type `{}`{method_suffix}", d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) .experimental() @@ -67,7 +68,10 @@ fn method_fix( } #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::{ + tests::{check_diagnostics, check_diagnostics_with_config}, + DiagnosticsConfig, + }; #[test] fn smoke_test() { @@ -145,4 +149,11 @@ fn foo() { "#, ); } + + #[test] + fn no_diagnostic_for_missing_name() { + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("syntax-error".to_owned()); + check_diagnostics_with_config(config, "fn foo() { (). }"); + } } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/crates/ide-diagnostics/src/handlers/unresolved_import.rs index e52a88459d55..6b8026c03419 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_import.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_import.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-import // @@ -8,10 +8,11 @@ pub(crate) fn unresolved_import( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedImport, ) -> Diagnostic { - Diagnostic::new( - "unresolved-import", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0432"), "unresolved import", - ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, + d.decl.clone().map(|it| it.into()), ) // This currently results in false positives in the following cases: // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) diff --git a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 3943b51ab422..33e7c2e37c3b 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-macro-call // @@ -12,7 +12,7 @@ pub(crate) fn unresolved_macro_call( let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location); let bang = if d.is_bang { "!" } else { "" }; Diagnostic::new( - "unresolved-macro-call", + DiagnosticCode::RustcHardError("unresolved-macro-call"), format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)), display_range, ) diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 8bbb837e6709..ae9f6744c40f 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ast, AstNode, TextRange}; use text_edit::TextEdit; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-method // @@ -22,14 +22,15 @@ pub(crate) fn unresolved_method( } else { "" }; - Diagnostic::new( - "unresolved-method", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0599"), format!( "no method `{}` on type `{}`{field_suffix}", d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) .experimental() diff --git a/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 6e3fd3b42b0c..be24e50c9871 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -3,7 +3,7 @@ use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSyste use itertools::Itertools; use syntax::AstNode; -use crate::{fix, Diagnostic, DiagnosticsContext}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-module // @@ -12,8 +12,9 @@ pub(crate) fn unresolved_module( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule, ) -> Diagnostic { - Diagnostic::new( - "unresolved-module", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0583"), match &*d.candidates { [] => "unresolved module".to_string(), [candidate] => format!("unresolved module, can't find module file: {candidate}"), @@ -25,7 +26,7 @@ pub(crate) fn unresolved_module( ) } }, - ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, + d.decl.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) } @@ -82,8 +83,8 @@ mod baz {} expect![[r#" [ Diagnostic { - code: DiagnosticCode( - "unresolved-module", + code: RustcHardError( + "E0583", ), message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs", range: 0..8, @@ -148,6 +149,22 @@ mod baz {} }, ], ), + main_node: Some( + InFile { + file_id: FileId( + FileId( + 0, + ), + ), + value: MODULE@0..8 + MOD_KW@0..3 "mod" + WHITESPACE@3..4 " " + NAME@4..7 + IDENT@4..7 "foo" + SEMICOLON@7..8 ";" + , + }, + ), }, ] "#]], diff --git a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs index ae5cf135839f..015a3d6b2ce7 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs @@ -1,6 +1,6 @@ use hir::db::DefDatabase; -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: unresolved-proc-macro // @@ -41,5 +41,5 @@ pub(crate) fn unresolved_proc_macro( }; let message = format!("{not_expanded_message}: {message}"); - Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity) + Diagnostic::new(DiagnosticCode::Ra("unresolved-proc-macro", severity), message, display_range) } diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index 289ed0458c67..0aa439f797a1 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,9 +1,9 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, TextRange}; +use syntax::{ast, AstNode, SyntaxNode}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode}; // Diagnostic: unnecessary-braces // @@ -15,6 +15,11 @@ pub(crate) fn useless_braces( ) -> Option<()> { let use_tree_list = ast::UseTreeList::cast(node.clone())?; if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { + // If there is a `self` inside the bracketed `use`, don't show diagnostic. + if single_use_tree.path()?.segment()?.self_token().is_some() { + return Some(()); + } + // If there is a comment inside the bracketed `use`, // assume it is a commented out module path and don't show diagnostic. if use_tree_list.has_inner_comment() { @@ -22,21 +27,18 @@ pub(crate) fn useless_braces( } let use_range = use_tree_list.syntax().text_range(); - let edit = remove_braces(&single_use_tree).unwrap_or_else(|| { - let to_replace = single_use_tree.syntax().text().to_string(); - let mut edit_builder = TextEdit::builder(); - edit_builder.delete(use_range); - edit_builder.insert(use_range.start(), to_replace); - edit_builder.finish() - }); + let to_replace = single_use_tree.syntax().text().to_string(); + let mut edit_builder = TextEdit::builder(); + edit_builder.delete(use_range); + edit_builder.insert(use_range.start(), to_replace); + let edit = edit_builder.finish(); acc.push( Diagnostic::new( - "unnecessary-braces", + DiagnosticCode::RustcLint("unused_braces"), "Unnecessary braces in use statement".to_string(), use_range, ) - .severity(Severity::WeakWarning) .with_fixes(Some(vec![fix( "remove_braces", "Remove unnecessary braces", @@ -49,19 +51,12 @@ pub(crate) fn useless_braces( Some(()) } -fn remove_braces(single_use_tree: &ast::UseTree) -> Option { - let use_tree_list_node = single_use_tree.syntax().parent()?; - if single_use_tree.path()?.segment()?.self_token().is_some() { - let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); - let end = use_tree_list_node.text_range().end(); - return Some(TextEdit::delete(TextRange::new(start, end))); - } - None -} - #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix}; + use crate::{ + tests::{check_diagnostics, check_diagnostics_with_config, check_fix}, + DiagnosticsConfig, + }; #[test] fn test_check_unnecessary_braces_in_use_statement() { @@ -92,6 +87,32 @@ mod a { pub mod e {} } } +"#, + ); + check_diagnostics( + r#" +use a::{self}; + +mod a { +} +"#, + ); + check_diagnostics( + r#" +use a::{self as cool_name}; + +mod a { +} +"#, + ); + + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("syntax-error".to_string()); + check_diagnostics_with_config( + config, + r#" +mod a { pub mod b {} } +use a::{b::self}; "#, ); check_fix( @@ -122,16 +143,6 @@ use a::{c$0}; r#" mod a { pub mod c {} } use a::c; -"#, - ); - check_fix( - r#" -mod a {} -use a::{self$0}; -"#, - r#" -mod a {} -use a; "#, ); check_fix( diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 55a4a482d3b2..b1b9b4b8ea5d 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -67,24 +67,61 @@ mod handlers { #[cfg(test)] mod tests; +use std::collections::HashMap; + use hir::{diagnostics::AnyDiagnostic, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, base_db::{FileId, FileRange, SourceDatabase}, + generated::lints::{LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, source_change::SourceChange, - FxHashSet, RootDatabase, + syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, + FxHashMap, FxHashSet, RootDatabase, +}; +use once_cell::sync::Lazy; +use stdx::never; +use syntax::{ + algo::find_node_at_range, + ast::{self, AstNode}, + SyntaxNode, SyntaxNodePtr, TextRange, }; -use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange}; // FIXME: Make this an enum -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct DiagnosticCode(pub &'static str); +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DiagnosticCode { + RustcHardError(&'static str), + RustcLint(&'static str), + Clippy(&'static str), + Ra(&'static str, Severity), +} impl DiagnosticCode { - pub fn as_str(&self) -> &str { - self.0 + pub fn url(&self) -> String { + match self { + DiagnosticCode::RustcHardError(e) => { + format!("https://doc.rust-lang.org/stable/error_codes/{e}.html") + } + DiagnosticCode::RustcLint(e) => { + format!("https://doc.rust-lang.org/rustc/?search={e}") + } + DiagnosticCode::Clippy(e) => { + format!("https://rust-lang.github.io/rust-clippy/master/#/{e}") + } + DiagnosticCode::Ra(e, _) => { + format!("https://rust-analyzer.github.io/manual.html#{e}") + } + } + } + + pub fn as_str(&self) -> &'static str { + match self { + DiagnosticCode::RustcHardError(r) + | DiagnosticCode::RustcLint(r) + | DiagnosticCode::Clippy(r) + | DiagnosticCode::Ra(r, _) => r, + } } } @@ -97,29 +134,51 @@ pub struct Diagnostic { pub unused: bool, pub experimental: bool, pub fixes: Option>, + // The node that will be affected by `#[allow]` and similar attributes. + pub main_node: Option>, } impl Diagnostic { - fn new(code: &'static str, message: impl Into, range: TextRange) -> Diagnostic { + fn new(code: DiagnosticCode, message: impl Into, range: TextRange) -> Diagnostic { let message = message.into(); Diagnostic { - code: DiagnosticCode(code), + code, message, range, - severity: Severity::Error, + severity: match code { + DiagnosticCode::RustcHardError(_) => Severity::Error, + // FIXME: Rustc lints are not always warning, but the ones that are currently implemented are all warnings. + DiagnosticCode::RustcLint(_) => Severity::Warning, + // FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can + // make it normal warning. + DiagnosticCode::Clippy(_) => Severity::WeakWarning, + DiagnosticCode::Ra(_, s) => s, + }, unused: false, experimental: false, fixes: None, + main_node: None, } } + fn new_with_syntax_node_ptr( + ctx: &DiagnosticsContext<'_>, + code: DiagnosticCode, + message: impl Into, + node: InFile, + ) -> Diagnostic { + let file_id = node.file_id; + Diagnostic::new(code, message, ctx.sema.diagnostics_display_range(node.clone()).range) + .with_main_node(node.map(|x| x.to_node(&ctx.sema.parse_or_expand(file_id)))) + } + fn experimental(mut self) -> Diagnostic { self.experimental = true; self } - fn severity(mut self, severity: Severity) -> Diagnostic { - self.severity = severity; + fn with_main_node(mut self, main_node: InFile) -> Diagnostic { + self.main_node = Some(main_node); self } @@ -134,12 +193,12 @@ impl Diagnostic { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Severity { Error, - // We don't actually emit this one yet, but we should at some point. - // Warning, + Warning, WeakWarning, + Allow, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -155,6 +214,8 @@ impl Default for ExprFillDefaultMode { #[derive(Debug, Clone)] pub struct DiagnosticsConfig { + /// Whether native diagnostics are enabled. + pub enabled: bool, pub proc_macros_enabled: bool, pub proc_attr_macros_enabled: bool, pub disable_experimental: bool, @@ -171,6 +232,7 @@ impl DiagnosticsConfig { use ide_db::imports::insert_use::ImportGranularity; Self { + enabled: true, proc_macros_enabled: Default::default(), proc_attr_macros_enabled: Default::default(), disable_experimental: Default::default(), @@ -194,7 +256,7 @@ struct DiagnosticsContext<'a> { resolve: &'a AssistResolveStrategy, } -impl<'a> DiagnosticsContext<'a> { +impl DiagnosticsContext<'_> { fn resolve_precise_location( &self, node: &InFile, @@ -228,11 +290,13 @@ pub fn diagnostics( let mut res = Vec::new(); // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. - res.extend( - parse.errors().iter().take(128).map(|err| { - Diagnostic::new("syntax-error", format!("Syntax Error: {err}"), err.range()) - }), - ); + res.extend(parse.errors().iter().take(128).map(|err| { + Diagnostic::new( + DiagnosticCode::RustcHardError("syntax-error"), + format!("Syntax Error: {err}"), + err.range(), + ) + })); let parse = sema.parse(file_id); @@ -271,7 +335,7 @@ pub fn diagnostics( res.extend(d.errors.iter().take(32).map(|err| { { Diagnostic::new( - "syntax-error", + DiagnosticCode::RustcHardError("syntax-error"), format!("Syntax Error in Expansion: {err}"), ctx.resolve_precise_location(&d.node.clone(), d.precise_location), ) @@ -309,14 +373,168 @@ pub fn diagnostics( res.push(d) } + let mut diagnostics_of_range = + res.iter_mut().filter_map(|x| Some((x.main_node.clone()?, x))).collect::>(); + + let mut rustc_stack: FxHashMap> = FxHashMap::default(); + let mut clippy_stack: FxHashMap> = FxHashMap::default(); + + handle_lint_attributes( + &ctx.sema, + parse.syntax(), + &mut rustc_stack, + &mut clippy_stack, + &mut diagnostics_of_range, + ); + res.retain(|d| { - !ctx.config.disabled.contains(d.code.as_str()) + d.severity != Severity::Allow + && !ctx.config.disabled.contains(d.code.as_str()) && !(ctx.config.disable_experimental && d.experimental) }); res } +// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros + +static RUSTC_LINT_GROUPS_DICT: Lazy>> = + Lazy::new(|| build_group_dict(DEFAULT_LINT_GROUPS, &["warnings", "__RA_EVERY_LINT"], "")); + +static CLIPPY_LINT_GROUPS_DICT: Lazy>> = + Lazy::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::")); + +fn build_group_dict( + lint_group: &'static [LintGroup], + all_groups: &'static [&'static str], + prefix: &'static str, +) -> HashMap<&'static str, Vec<&'static str>> { + let mut r: HashMap<&str, Vec<&str>> = HashMap::new(); + for g in lint_group { + for child in g.children { + r.entry(child.strip_prefix(prefix).unwrap()) + .or_default() + .push(g.lint.label.strip_prefix(prefix).unwrap()); + } + } + for (lint, groups) in r.iter_mut() { + groups.push(lint); + groups.extend_from_slice(all_groups); + } + r +} + +fn handle_lint_attributes( + sema: &Semantics<'_, RootDatabase>, + root: &SyntaxNode, + rustc_stack: &mut FxHashMap>, + clippy_stack: &mut FxHashMap>, + diagnostics_of_range: &mut FxHashMap, &mut Diagnostic>, +) { + let file_id = sema.hir_file_for(root); + for ev in root.preorder() { + match ev { + syntax::WalkEvent::Enter(node) => { + for attr in node.children().filter_map(ast::Attr::cast) { + parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| { + stack.push(severity); + }); + } + if let Some(x) = + diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() }) + { + const EMPTY_LINTS: &[&str] = &[]; + let (names, stack) = match x.code { + DiagnosticCode::RustcLint(name) => ( + RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x), + &mut *rustc_stack, + ), + DiagnosticCode::Clippy(name) => ( + CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x), + &mut *clippy_stack, + ), + _ => continue, + }; + for &name in names { + if let Some(s) = stack.get(name).and_then(|x| x.last()) { + x.severity = *s; + } + } + } + if let Some(item) = ast::Item::cast(node.clone()) { + if let Some(me) = sema.expand_attr_macro(&item) { + for stack in [&mut *rustc_stack, &mut *clippy_stack] { + stack + .entry("__RA_EVERY_LINT".to_owned()) + .or_default() + .push(Severity::Allow); + } + handle_lint_attributes( + sema, + &me, + rustc_stack, + clippy_stack, + diagnostics_of_range, + ); + for stack in [&mut *rustc_stack, &mut *clippy_stack] { + stack.entry("__RA_EVERY_LINT".to_owned()).or_default().pop(); + } + } + } + if let Some(mc) = ast::MacroCall::cast(node) { + if let Some(me) = sema.expand(&mc) { + handle_lint_attributes( + sema, + &me, + rustc_stack, + clippy_stack, + diagnostics_of_range, + ); + } + } + } + syntax::WalkEvent::Leave(node) => { + for attr in node.children().filter_map(ast::Attr::cast) { + parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| { + if stack.pop() != Some(severity) { + never!("Mismatched serevity in walking lint attributes"); + } + }); + } + } + } + } +} + +fn parse_lint_attribute( + attr: ast::Attr, + rustc_stack: &mut FxHashMap>, + clippy_stack: &mut FxHashMap>, + job: impl Fn(&mut Vec, Severity), +) { + let Some((tag, args_tt)) = attr.as_simple_call() else { + return; + }; + let serevity = match tag.as_str() { + "allow" => Severity::Allow, + "warn" => Severity::Warning, + "forbid" | "deny" => Severity::Error, + _ => return, + }; + for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() { + if let Some(lint) = lint.as_single_name_ref() { + job(rustc_stack.entry(lint.to_string()).or_default(), serevity); + } + if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) { + if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) { + if tool.to_string() == "clippy" { + job(clippy_stack.entry(name_ref.to_string()).or_default(), serevity); + } + } + } + } +} + fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { let mut res = unresolved_fix(id, label, target); res.source_change = Some(source_change); diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index b5cd4e0d6891..4ac9d0a9fb73 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -114,6 +114,8 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur annotation.push_str(match d.severity { Severity::Error => "error", Severity::WeakWarning => "weak", + Severity::Warning => "warn", + Severity::Allow => "allow", }); annotation.push_str(": "); annotation.push_str(&d.message); @@ -130,14 +132,19 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur ) } } - assert_eq!(expected, actual); + if expected != actual { + let fneg = expected.iter().filter(|x| !actual.contains(x)).collect::>(); + let fpos = actual.iter().filter(|x| !expected.contains(x)).collect::>(); + + panic!("Diagnostic test failed.\nFalse negatives: {fneg:?}\nFalse positives: {fpos:?}"); + } } } #[test] fn test_disabled_diagnostics() { let mut config = DiagnosticsConfig::test_sample(); - config.disabled.insert("unresolved-module".into()); + config.disabled.insert("E0583".into()); let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); @@ -159,7 +166,7 @@ fn minicore_smoke_test() { let source = minicore.source_code(); let mut config = DiagnosticsConfig::test_sample(); // This should be ignored since we conditionaly remove code which creates single item use with braces - config.disabled.insert("unnecessary-braces".to_string()); + config.disabled.insert("unused_braces".to_string()); check_diagnostics_with_config(config, &source); } diff --git a/crates/ide-ssr/src/search.rs b/crates/ide-ssr/src/search.rs index 0a85569b6008..96c193bd5390 100644 --- a/crates/ide-ssr/src/search.rs +++ b/crates/ide-ssr/src/search.rs @@ -22,7 +22,7 @@ pub(crate) struct UsageCache { usages: Vec<(Definition, UsageSearchResult)>, } -impl<'db> MatchFinder<'db> { +impl MatchFinder<'_> { /// Adds all matches for `rule` to `matches_out`. Matches may overlap in ways that make /// replacement impossible, so further processing is required in order to properly nest matches /// and remove overlapping matches. This is done in the `nesting` module. diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 8112c4f7259c..c90ba2125353 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -330,7 +330,9 @@ fn get_doc_links( base_url.and_then(|url| url.join(path).ok()) }; - let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { return Default::default(); }; + let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { + return Default::default(); + }; let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot); diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 1362146413e0..ef33386a7e91 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -422,10 +422,10 @@ pub(super) fn definition( |&it| { if !it.parent_enum(db).is_data_carrying(db) { match it.eval(db) { - Ok(x) => { - Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }) + Ok(it) => { + Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") }) } - Err(_) => it.value(db).map(|x| format!("{x:?}")), + Err(_) => it.value(db).map(|it| format!("{it:?}")), } } else { None @@ -437,7 +437,7 @@ pub(super) fn definition( Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.render_eval(db); match body { - Ok(x) => Some(x), + Ok(it) => Some(it), Err(_) => { let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f75ebfa12eb2..00e21433daae 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -674,7 +674,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - field_a: u8 // size = 1, align = 1, offset = 4 + field_a: u8 // size = 1, align = 1, offset = 6 ``` "#]], ); @@ -779,6 +779,39 @@ const foo$0: u32 = { ``` "#]], ); + + check( + r#"const FOO$0: i32 = -2147483648;"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: i32 = -2147483648 (0x80000000) + ``` + "#]], + ); + + check( + r#" + const FOO: i32 = -2147483648; + const BAR$0: bool = FOO > 0; + "#, + expect![[r#" + *BAR* + + ```rust + test + ``` + + ```rust + const BAR: bool = false + ``` + "#]], + ); } #[test] diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 10bee2a6accf..6d6bd315ebb4 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -259,7 +259,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, } })() else { never!("broken syntax tree?\n{:?}\n{:?}", expr, dummy_expr); - return (true, true) + return (true, true); }; // At this point diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 84eac16b9f93..b621a8dda7ee 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -474,7 +474,7 @@ fn main() { file_id: FileId( 1, ), - range: 9287..9295, + range: 9289..9297, }, ), tooltip: "", @@ -487,7 +487,7 @@ fn main() { file_id: FileId( 1, ), - range: 9319..9323, + range: 9321..9325, }, ), tooltip: "", @@ -511,7 +511,7 @@ fn main() { file_id: FileId( 1, ), - range: 9287..9295, + range: 9289..9297, }, ), tooltip: "", @@ -524,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 9319..9323, + range: 9321..9325, }, ), tooltip: "", @@ -548,7 +548,7 @@ fn main() { file_id: FileId( 1, ), - range: 9287..9295, + range: 9289..9297, }, ), tooltip: "", @@ -561,7 +561,7 @@ fn main() { file_id: FileId( 1, ), - range: 9319..9323, + range: 9321..9325, }, ), tooltip: "", diff --git a/crates/ide/src/interpret_function.rs b/crates/ide/src/interpret_function.rs index cbcbb4b09db4..d06ffd535758 100644 --- a/crates/ide/src/interpret_function.rs +++ b/crates/ide/src/interpret_function.rs @@ -34,13 +34,15 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option return None, }; let span_formatter = |file_id, text_range: TextRange| { - let line_col = db.line_index(file_id).line_col(text_range.start()); let path = &db .source_root(db.file_source_root(file_id)) .path_for_file(&file_id) .map(|x| x.to_string()); let path = path.as_deref().unwrap_or(""); - format!("file://{path}#{}:{}", line_col.line + 1, line_col.col) + match db.line_index(file_id).try_line_col(text_range.start()) { + Some(line_col) => format!("file://{path}#{}:{}", line_col.line + 1, line_col.col), + None => format!("file://{path} range {:?}", text_range), + } }; Some(def.eval(db, span_formatter)) } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index f195f78b3ab8..0ad4c6c47e62 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -60,6 +60,7 @@ mod interpret_function; mod view_item_tree; mod shuffle_crate_graph; mod fetch_crates; +mod view_memory_layout; use std::ffi::OsStr; @@ -74,6 +75,7 @@ use ide_db::{ }; use syntax::SourceFile; use triomphe::Arc; +use view_memory_layout::{view_memory_layout, RecursiveMemoryLayout}; use crate::navigation_target::{ToNav, TryToNav}; @@ -642,7 +644,7 @@ impl Analysis { }; self.with_db(|db| { - let diagnostic_assists = if include_fixes { + let diagnostic_assists = if diagnostics_config.enabled && include_fixes { ide_diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id) .into_iter() .flat_map(|it| it.fixes.unwrap_or_default()) @@ -724,6 +726,13 @@ impl Analysis { self.with_db(|db| move_item::move_item(db, range, direction)) } + pub fn get_recursive_memory_layout( + &self, + position: FilePosition, + ) -> Cancellable> { + self.with_db(|db| view_memory_layout(db, position)) + } + /// Performs an operation on the database that may be canceled. /// /// rust-analyzer needs to be able to answer semantic questions about the diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 0d57e63d29c9..d486a794e13b 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -320,7 +320,7 @@ use foo::module::func; fn main() { func$0(); } -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub fn func() {} } @@ -336,7 +336,7 @@ use foo::module::func; fn main() { func(); } -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub fn func$0() {} } @@ -351,7 +351,7 @@ pub mod module { fn moniker_for_trait() { check_moniker( r#" -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { pub fn func$0() {} @@ -368,7 +368,7 @@ pub mod module { fn moniker_for_trait_constant() { check_moniker( r#" -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { const MY_CONST$0: u8; @@ -385,7 +385,7 @@ pub mod module { fn moniker_for_trait_type() { check_moniker( r#" -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { type MyType$0; @@ -402,7 +402,7 @@ pub mod module { fn moniker_for_trait_impl_function() { check_moniker( r#" -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { pub fn func() {} @@ -430,7 +430,7 @@ use foo::St; fn main() { let x = St { a$0: 2 }; } -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub struct St { pub a: i32, } @@ -450,7 +450,7 @@ use foo::module::func; fn main() { func(); } -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub fn func() { let x$0 = 2; diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 385c1b0c0081..c7abecb4f1e3 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -357,13 +357,11 @@ impl ToNav for hir::Module { impl TryToNav for hir::Impl { fn try_to_nav(&self, db: &RootDatabase) -> Option { let InFile { file_id, value } = self.source(db)?; - let derive_attr = self.is_builtin_derive(db); + let derive_attr = self.as_builtin_derive(db); - let focus = if derive_attr.is_some() { None } else { value.self_ty() }; - - let syntax = match &derive_attr { - Some(attr) => attr.value.syntax(), - None => value.syntax(), + let (focus, syntax) = match &derive_attr { + Some(attr) => (None, attr.value.syntax()), + None => (value.self_ty(), value.syntax()), }; let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 27ad63d820d6..9fa0e6449b83 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -2,7 +2,7 @@ use std::fmt; use ast::HasName; use cfg::CfgExpr; -use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; +use hir::{db::HirDatabase, AsAssocItem, HasAttrs, HasSource, Semantics}; use ide_assists::utils::test_related_attribute; use ide_db::{ base_db::{FilePosition, FileRange}, @@ -14,7 +14,7 @@ use ide_db::{ use itertools::Itertools; use stdx::{always, format_to}; use syntax::{ - ast::{self, AstNode, HasAttrs as _}, + ast::{self, AstNode}, SmolStr, SyntaxNode, }; @@ -307,7 +307,6 @@ pub(crate) fn runnable_fn( sema: &Semantics<'_, RootDatabase>, def: hir::Function, ) -> Option { - let func = def.source(sema.db)?; let name = def.name(sema.db).to_smol_str(); let root = def.module(sema.db).krate().root_module(sema.db); @@ -323,10 +322,10 @@ pub(crate) fn runnable_fn( canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name)) }; - if test_related_attribute(&func.value).is_some() { - let attr = TestAttr::from_fn(&func.value); + if def.is_test(sema.db) { + let attr = TestAttr::from_fn(sema.db, def); RunnableKind::Test { test_id: test_id(), attr } - } else if func.value.has_atom_attr("bench") { + } else if def.is_bench(sema.db) { RunnableKind::Bench { test_id: test_id() } } else { return None; @@ -335,7 +334,7 @@ pub(crate) fn runnable_fn( let nav = NavigationTarget::from_named( sema.db, - func.as_ref().map(|it| it as &dyn ast::HasName), + def.source(sema.db)?.as_ref().map(|it| it as &dyn ast::HasName), SymbolKind::Function, ); let cfg = def.attrs(sema.db).cfg(); @@ -487,12 +486,8 @@ pub struct TestAttr { } impl TestAttr { - fn from_fn(fn_def: &ast::Fn) -> TestAttr { - let ignore = fn_def - .attrs() - .filter_map(|attr| attr.simple_name()) - .any(|attribute_text| attribute_text == "ignore"); - TestAttr { ignore } + fn from_fn(db: &dyn HirDatabase, fn_def: hir::Function) -> TestAttr { + TestAttr { ignore: fn_def.is_ignore(db) } } } @@ -594,6 +589,9 @@ fn main() {} #[test] fn test_foo() {} +#[::core::prelude::v1::test] +fn test_full_path() {} + #[test] #[ignore] fn test_foo() {} @@ -605,7 +603,7 @@ mod not_a_root { fn main() {} } "#, - &[TestMod, Bin, Test, Test, Bench], + &[TestMod, Bin, Test, Test, Test, Bench], expect![[r#" [ Runnable { @@ -614,7 +612,7 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 0..137, + full_range: 0..190, name: "", kind: Module, }, @@ -664,8 +662,29 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 41..75, - focus_range: 62..70, + full_range: 41..92, + focus_range: 73..87, + name: "test_full_path", + kind: Function, + }, + kind: Test { + test_id: Path( + "test_full_path", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 94..128, + focus_range: 115..123, name: "test_foo", kind: Function, }, @@ -685,8 +704,8 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 77..99, - focus_range: 89..94, + full_range: 130..152, + focus_range: 142..147, name: "bench", kind: Function, }, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 3e3d9f8f85c4..59e8300dcdbe 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -3,13 +3,14 @@ use std::collections::HashMap; -use hir::{db::HirDatabase, Crate, Module, Semantics}; +use hir::{db::HirDatabase, Crate, Module}; +use ide_db::helpers::get_definition; use ide_db::{ base_db::{FileId, FileRange, SourceDatabaseExt}, - defs::{Definition, IdentClass}, + defs::Definition, FxHashSet, RootDatabase, }; -use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T}; +use syntax::{AstNode, SyntaxKind::*, TextRange, T}; use crate::{ hover::hover_for_definition, @@ -73,7 +74,7 @@ impl TokenStore { } pub fn iter(self) -> impl Iterator { - self.0.into_iter().enumerate().map(|(i, x)| (TokenId(i), x)) + self.0.into_iter().enumerate().map(|(id, data)| (TokenId(id), data)) } } @@ -132,9 +133,9 @@ impl StaticIndex<'_> { // hovers let sema = hir::Semantics::new(self.db); let tokens_or_nodes = sema.parse(file_id).syntax().clone(); - let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|x| match x { + let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it { syntax::NodeOrToken::Node(_) => None, - syntax::NodeOrToken::Token(x) => Some(x), + syntax::NodeOrToken::Token(it) => Some(it), }); let hover_config = HoverConfig { links_in_hover: true, @@ -154,28 +155,29 @@ impl StaticIndex<'_> { let range = token.text_range(); let node = token.parent().unwrap(); let def = match get_definition(&sema, token.clone()) { - Some(x) => x, + Some(it) => it, None => continue, }; - let id = if let Some(x) = self.def_map.get(&def) { - *x + let id = if let Some(it) = self.def_map.get(&def) { + *it } else { - let x = self.tokens.insert(TokenStaticData { + let it = self.tokens.insert(TokenStaticData { hover: hover_for_definition(&sema, file_id, def, &node, &hover_config), - definition: def - .try_to_nav(self.db) - .map(|x| FileRange { file_id: x.file_id, range: x.focus_or_full_range() }), + definition: def.try_to_nav(self.db).map(|it| FileRange { + file_id: it.file_id, + range: it.focus_or_full_range(), + }), references: vec![], moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), }); - self.def_map.insert(def, x); - x + self.def_map.insert(def, it); + it }; let token = self.tokens.get_mut(id).unwrap(); token.references.push(ReferenceData { range: FileRange { range, file_id }, is_definition: match def.try_to_nav(self.db) { - Some(x) => x.file_id == file_id && x.focus_or_full_range() == range, + Some(it) => it.file_id == file_id && it.focus_or_full_range() == range, None => false, }, }); @@ -187,7 +189,7 @@ impl StaticIndex<'_> { pub fn compute(analysis: &Analysis) -> StaticIndex<'_> { let db = &*analysis.db; let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source(db).file_id.original_file(db); + let file_id = module.definition_source_file_id(db).original_file(db); let source_root = db.file_source_root(file_id); let source_root = db.source_root(source_root); !source_root.is_library @@ -201,7 +203,7 @@ impl StaticIndex<'_> { }; let mut visited_files = FxHashSet::default(); for module in work { - let file_id = module.definition_source(db).file_id.original_file(db); + let file_id = module.definition_source_file_id(db).original_file(db); if visited_files.contains(&file_id) { continue; } @@ -213,16 +215,6 @@ impl StaticIndex<'_> { } } -fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { - for token in sema.descend_into_macros(token) { - let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); - if let Some(&[x]) = def.as_deref() { - return Some(x); - } - } - None -} - #[cfg(test)] mod tests { use crate::{fixture, StaticIndex}; @@ -233,14 +225,14 @@ mod tests { fn check_all_ranges(ra_fixture: &str) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); let s = StaticIndex::compute(&analysis); - let mut range_set: HashSet<_> = ranges.iter().map(|x| x.0).collect(); + let mut range_set: HashSet<_> = ranges.iter().map(|it| it.0).collect(); for f in s.files { for (range, _) in f.tokens { - let x = FileRange { file_id: f.file_id, range }; - if !range_set.contains(&x) { - panic!("additional range {x:?}"); + let it = FileRange { file_id: f.file_id, range }; + if !range_set.contains(&it) { + panic!("additional range {it:?}"); } - range_set.remove(&x); + range_set.remove(&it); } } if !range_set.is_empty() { @@ -251,17 +243,17 @@ mod tests { fn check_definitions(ra_fixture: &str) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); let s = StaticIndex::compute(&analysis); - let mut range_set: HashSet<_> = ranges.iter().map(|x| x.0).collect(); + let mut range_set: HashSet<_> = ranges.iter().map(|it| it.0).collect(); for (_, t) in s.tokens.iter() { - if let Some(x) = t.definition { - if x.range.start() == TextSize::from(0) { + if let Some(t) = t.definition { + if t.range.start() == TextSize::from(0) { // ignore definitions that are whole of file continue; } - if !range_set.contains(&x) { - panic!("additional definition {x:?}"); + if !range_set.contains(&t) { + panic!("additional definition {t:?}"); } - range_set.remove(&x); + range_set.remove(&t); } } if !range_set.is_empty() { diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index dc06591ffea5..577bd2bc1f89 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -265,10 +265,14 @@ fn traverse( // set macro and attribute highlighting states match event.clone() { - Enter(NodeOrToken::Node(node)) if ast::TokenTree::can_cast(node.kind()) => { + Enter(NodeOrToken::Node(node)) + if current_macro.is_none() && ast::TokenTree::can_cast(node.kind()) => + { tt_level += 1; } - Leave(NodeOrToken::Node(node)) if ast::TokenTree::can_cast(node.kind()) => { + Leave(NodeOrToken::Node(node)) + if current_macro.is_none() && ast::TokenTree::can_cast(node.kind()) => + { tt_level -= 1; } Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { @@ -387,7 +391,7 @@ fn traverse( }; let descended_element = if in_macro { // Attempt to descend tokens into macro-calls. - match element { + let res = match element { NodeOrToken::Token(token) if token.kind() != COMMENT => { let token = match attr_or_derive_item { Some(AttrOrDerive::Attr(_)) => { @@ -412,7 +416,8 @@ fn traverse( } } e => e, - } + }; + res } else { element }; diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index fa374b04f195..f4f164aa1de6 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -161,7 +161,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let _ = "\x28\x28\x00\x63\n"; let _ = b"\x28\x28\x00\x63\n"; - let _ = r"\\"; + let backslash = r"\\"; println!("{\x41}", A = 92); println!("{ничоси}", ничоси = 92); @@ -173,6 +173,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); asm!("mov eax, {0}"); - format_args!(concat!("{}"), "{}"); - format_args!("{}", format_args!("{}", 0)); + format_args!(concat!("{}"), "{}"); + format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 497992f684ce..1ee451a06d04 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -507,7 +507,7 @@ fn main() { let _ = "\x28\x28\x00\x63\n"; let _ = b"\x28\x28\x00\x63\n"; - let _ = r"\\"; + let backslash = r"\\"; println!("{\x41}", A = 92); println!("{ничоси}", ничоси = 92); @@ -520,7 +520,7 @@ fn main() { toho!("{}fmt", 0); asm!("mov eax, {0}"); format_args!(concat!("{}"), "{}"); - format_args!("{}", format_args!("{}", 0)); + format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); }"#, expect_file!["./test_data/highlight_strings.html"], false, diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs new file mode 100644 index 000000000000..2f6332abd259 --- /dev/null +++ b/crates/ide/src/view_memory_layout.rs @@ -0,0 +1,409 @@ +use std::fmt; + +use hir::{Field, HirDisplay, Layout, Semantics, Type}; +use ide_db::{ + defs::Definition, + helpers::{get_definition, pick_best_token}, + RootDatabase, +}; +use syntax::{AstNode, SyntaxKind}; + +use crate::FilePosition; + +pub struct MemoryLayoutNode { + pub item_name: String, + pub typename: String, + pub size: u64, + pub alignment: u64, + pub offset: u64, + pub parent_idx: i64, + pub children_start: i64, + pub children_len: u64, +} + +pub struct RecursiveMemoryLayout { + pub nodes: Vec, +} + +// NOTE: this is currently strictly for testing and so isn't super useful as a visualization tool, however it could be adapted to become one? +impl fmt::Display for RecursiveMemoryLayout { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn process( + fmt: &mut fmt::Formatter<'_>, + nodes: &Vec, + idx: usize, + depth: usize, + ) -> fmt::Result { + let mut out = "\t".repeat(depth); + let node = &nodes[idx]; + out += &format!( + "{}: {} (size: {}, align: {}, field offset: {})\n", + node.item_name, node.typename, node.size, node.alignment, node.offset + ); + write!(fmt, "{}", out)?; + if node.children_start != -1 { + for j in nodes[idx].children_start + ..(nodes[idx].children_start + nodes[idx].children_len as i64) + { + process(fmt, nodes, j as usize, depth + 1)?; + } + } + Ok(()) + } + + process(fmt, &self.nodes, 0, 0) + } +} + +enum FieldOrTupleIdx { + Field(Field), + TupleIdx(usize), +} + +impl FieldOrTupleIdx { + fn name(&self, db: &RootDatabase) -> String { + match *self { + FieldOrTupleIdx::Field(f) => f + .name(db) + .as_str() + .map(|s| s.to_owned()) + .unwrap_or_else(|| format!(".{}", f.name(db).as_tuple_index().unwrap())), + FieldOrTupleIdx::TupleIdx(i) => format!(".{i}").to_owned(), + } + } + + fn index(&self) -> usize { + match *self { + FieldOrTupleIdx::Field(f) => f.index(), + FieldOrTupleIdx::TupleIdx(i) => i, + } + } +} + +// Feature: View Memory Layout +// +// Displays the recursive memory layout of a datatype. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **rust-analyzer: View Memory Layout** +// |=== +pub(crate) fn view_memory_layout( + db: &RootDatabase, + position: FilePosition, +) -> Option { + let sema = Semantics::new(db); + let file = sema.parse(position.file_id); + let token = + pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { + SyntaxKind::IDENT => 3, + _ => 0, + })?; + + let def = get_definition(&sema, token)?; + + let ty = match def { + Definition::Adt(it) => it.ty(db), + Definition::TypeAlias(it) => it.ty(db), + Definition::BuiltinType(it) => it.ty(db), + Definition::SelfType(it) => it.self_ty(db), + Definition::Local(it) => it.ty(db), + Definition::Field(it) => it.ty(db), + Definition::Const(it) => it.ty(db), + Definition::Static(it) => it.ty(db), + _ => return None, + }; + + fn read_layout( + nodes: &mut Vec, + db: &RootDatabase, + ty: &Type, + layout: &Layout, + parent_idx: usize, + ) { + let mut fields = ty + .fields(db) + .into_iter() + .map(|(f, ty)| (FieldOrTupleIdx::Field(f), ty)) + .chain( + ty.tuple_fields(db) + .into_iter() + .enumerate() + .map(|(i, ty)| (FieldOrTupleIdx::TupleIdx(i), ty)), + ) + .collect::>(); + + if fields.len() == 0 { + return; + } + + fields.sort_by_key(|(f, _)| layout.field_offset(f.index()).unwrap()); + + let children_start = nodes.len(); + nodes[parent_idx].children_start = children_start as i64; + nodes[parent_idx].children_len = fields.len() as u64; + + for (field, child_ty) in fields.iter() { + if let Ok(child_layout) = child_ty.layout(db) { + nodes.push(MemoryLayoutNode { + item_name: field.name(db), + typename: child_ty.display(db).to_string(), + size: child_layout.size(), + alignment: child_layout.align(), + offset: layout.field_offset(field.index()).unwrap_or(0), + parent_idx: parent_idx as i64, + children_start: -1, + children_len: 0, + }); + } else { + nodes.push(MemoryLayoutNode { + item_name: field.name(db) + + format!("(no layout data: {:?})", child_ty.layout(db).unwrap_err()) + .as_ref(), + typename: child_ty.display(db).to_string(), + size: 0, + offset: 0, + alignment: 0, + parent_idx: parent_idx as i64, + children_start: -1, + children_len: 0, + }); + } + } + + for (i, (_, child_ty)) in fields.iter().enumerate() { + if let Ok(child_layout) = child_ty.layout(db) { + read_layout(nodes, db, &child_ty, &child_layout, children_start + i); + } + } + } + + ty.layout(db) + .map(|layout| { + let item_name = match def { + // def is a datatype + Definition::Adt(_) + | Definition::TypeAlias(_) + | Definition::BuiltinType(_) + | Definition::SelfType(_) => "[ROOT]".to_owned(), + + // def is an item + def => def + .name(db) + .map(|n| { + n.as_str() + .map(|s| s.to_owned()) + .unwrap_or_else(|| format!(".{}", n.as_tuple_index().unwrap())) + }) + .unwrap_or("[ROOT]".to_owned()), + }; + + let typename = ty.display(db).to_string(); + + let mut nodes = vec![MemoryLayoutNode { + item_name, + typename: typename.clone(), + size: layout.size(), + offset: 0, + alignment: layout.align(), + parent_idx: -1, + children_start: -1, + children_len: 0, + }]; + read_layout(&mut nodes, db, &ty, &layout, 0); + + RecursiveMemoryLayout { nodes } + }) + .ok() +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::fixture; + use expect_test::expect; + + fn make_memory_layout(ra_fixture: &str) -> Option { + let (analysis, position, _) = fixture::annotations(ra_fixture); + + view_memory_layout(&analysis.db, position) + } + + #[test] + fn view_memory_layout_none() { + assert!(make_memory_layout(r#"$0"#).is_none()); + assert!(make_memory_layout(r#"stru$0ct Blah {}"#).is_none()); + } + + #[test] + fn view_memory_layout_primitive() { + expect![[r#" + foo: i32 (size: 4, align: 4, field offset: 0) + "#]] + .assert_eq( + &make_memory_layout( + r#" +fn main() { + let foo$0 = 109; // default i32 +} +"#, + ) + .unwrap() + .to_string(), + ); + } + + #[test] + fn view_memory_layout_constant() { + expect![[r#" + BLAH: bool (size: 1, align: 1, field offset: 0) + "#]] + .assert_eq( + &make_memory_layout( + r#" +const BLAH$0: bool = 0; +"#, + ) + .unwrap() + .to_string(), + ); + } + + #[test] + fn view_memory_layout_static() { + expect![[r#" + BLAH: bool (size: 1, align: 1, field offset: 0) + "#]] + .assert_eq( + &make_memory_layout( + r#" +static BLAH$0: bool = 0; +"#, + ) + .unwrap() + .to_string(), + ); + } + + #[test] + fn view_memory_layout_tuple() { + expect![[r#" + x: (f64, u8, i64) (size: 24, align: 8, field offset: 0) + .0: f64 (size: 8, align: 8, field offset: 0) + .1: u8 (size: 1, align: 1, field offset: 8) + .2: i64 (size: 8, align: 8, field offset: 16) + "#]] + .assert_eq( + &make_memory_layout( + r#" +fn main() { + let x$0 = (101.0, 111u8, 119i64); +} +"#, + ) + .unwrap() + .to_string(), + ); + } + + #[test] + fn view_memory_layout_c_struct() { + expect![[r#" + [ROOT]: Blah (size: 16, align: 4, field offset: 0) + a: u32 (size: 4, align: 4, field offset: 0) + b: (i32, u8) (size: 8, align: 4, field offset: 4) + .0: i32 (size: 4, align: 4, field offset: 0) + .1: u8 (size: 1, align: 1, field offset: 4) + c: i8 (size: 1, align: 1, field offset: 12) + "#]] + .assert_eq( + &make_memory_layout( + r#" +#[repr(C)] +struct Blah$0 { + a: u32, + b: (i32, u8), + c: i8, +} +"#, + ) + .unwrap() + .to_string(), + ); + } + + #[test] + fn view_memory_layout_struct() { + expect![[r#" + [ROOT]: Blah (size: 16, align: 4, field offset: 0) + b: (i32, u8) (size: 8, align: 4, field offset: 0) + .0: i32 (size: 4, align: 4, field offset: 0) + .1: u8 (size: 1, align: 1, field offset: 4) + a: u32 (size: 4, align: 4, field offset: 8) + c: i8 (size: 1, align: 1, field offset: 12) + "#]] + .assert_eq( + &make_memory_layout( + r#" +struct Blah$0 { + a: u32, + b: (i32, u8), + c: i8, +} +"#, + ) + .unwrap() + .to_string(), + ); + } + + #[test] + fn view_memory_layout_member() { + expect![[r#" + a: bool (size: 1, align: 1, field offset: 0) + "#]] + .assert_eq( + &make_memory_layout( + r#" +#[repr(C)] +struct Oof { + a$0: bool, +} +"#, + ) + .unwrap() + .to_string(), + ); + } + + #[test] + fn view_memory_layout_alias() { + let ml_a = make_memory_layout( + r#" +struct X { + a: u32, + b: i8, + c: (f32, f32), +} + +type Foo$0 = X; +"#, + ) + .unwrap(); + + let ml_b = make_memory_layout( + r#" +struct X$0 { + a: u32, + b: i8, + c: (f32, f32), +} +"#, + ) + .unwrap(); + + assert_eq!(ml_a.to_string(), ml_b.to_string()); + } +} diff --git a/crates/intern/Cargo.toml b/crates/intern/Cargo.toml index dcd0d7881255..4d56c7719603 100644 --- a/crates/intern/Cargo.toml +++ b/crates/intern/Cargo.toml @@ -15,7 +15,7 @@ doctest = false [dependencies] # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.4.0", features = ["raw-api"] } -hashbrown = { version = "0.12.1", default-features = false } +hashbrown.workspace = true once_cell = "1.17.0" rustc-hash = "1.1.0" triomphe.workspace = true diff --git a/crates/load-cargo/Cargo.toml b/crates/load-cargo/Cargo.toml new file mode 100644 index 000000000000..f041ca88ac08 --- /dev/null +++ b/crates/load-cargo/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "load-cargo" +version = "0.0.0" +description = "TBD" + +rust-version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.62" +crossbeam-channel = "0.5.5" +itertools = "0.10.5" +tracing = "0.1.35" + +ide.workspace = true +ide-db.workspace =true +proc-macro-api.workspace = true +project-model.workspace = true +tt.workspace = true +vfs.workspace = true +vfs-notify.workspace = true diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs new file mode 100644 index 000000000000..7a795dd62ab7 --- /dev/null +++ b/crates/load-cargo/src/lib.rs @@ -0,0 +1,441 @@ +//! Loads a Cargo project into a static instance of analysis, without support +//! for incorporating changes. +// Note, don't remove any public api from this. This API is consumed by external tools +// to run rust-analyzer as a library. +use std::{collections::hash_map::Entry, mem, path::Path, sync}; + +use ::tt::token_id as tt; +use crossbeam_channel::{unbounded, Receiver}; +use ide::{AnalysisHost, Change, SourceRoot}; +use ide_db::{ + base_db::{ + CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, + ProcMacroLoadResult, ProcMacros, + }, + FxHashMap, +}; +use itertools::Itertools; +use proc_macro_api::{MacroDylib, ProcMacroServer}; +use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; +use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath}; + +pub struct LoadCargoConfig { + pub load_out_dirs_from_check: bool, + pub with_proc_macro_server: ProcMacroServerChoice, + pub prefill_caches: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ProcMacroServerChoice { + Sysroot, + Explicit(AbsPathBuf), + None, +} + +pub fn load_workspace_at( + root: &Path, + cargo_config: &CargoConfig, + load_config: &LoadCargoConfig, + progress: &dyn Fn(String), +) -> anyhow::Result<(AnalysisHost, vfs::Vfs, Option)> { + let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); + let root = ProjectManifest::discover_single(&root)?; + let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?; + + if load_config.load_out_dirs_from_check { + let build_scripts = workspace.run_build_scripts(cargo_config, progress)?; + workspace.set_build_scripts(build_scripts) + } + + load_workspace(workspace, &cargo_config.extra_env, load_config) +} + +pub fn load_workspace( + ws: ProjectWorkspace, + extra_env: &FxHashMap, + load_config: &LoadCargoConfig, +) -> anyhow::Result<(AnalysisHost, vfs::Vfs, Option)> { + let (sender, receiver) = unbounded(); + let mut vfs = vfs::Vfs::default(); + let mut loader = { + let loader = + vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap())); + Box::new(loader) + }; + + let proc_macro_server = match &load_config.with_proc_macro_server { + ProcMacroServerChoice::Sysroot => ws + .find_sysroot_proc_macro_srv() + .and_then(|it| ProcMacroServer::spawn(it).map_err(Into::into)), + ProcMacroServerChoice::Explicit(path) => { + ProcMacroServer::spawn(path.clone()).map_err(Into::into) + } + ProcMacroServerChoice::None => Err(anyhow::format_err!("proc macro server disabled")), + }; + + let (crate_graph, proc_macros) = ws.to_crate_graph( + &mut |path: &AbsPath| { + let contents = loader.load_sync(path); + let path = vfs::VfsPath::from(path.to_path_buf()); + vfs.set_file_contents(path.clone(), contents); + vfs.file_id(&path) + }, + extra_env, + ); + let proc_macros = { + let proc_macro_server = match &proc_macro_server { + Ok(it) => Ok(it), + Err(e) => Err(e.to_string()), + }; + proc_macros + .into_iter() + .map(|(crate_id, path)| { + ( + crate_id, + path.map_or_else( + |_| Err("proc macro crate is missing dylib".to_owned()), + |(_, path)| { + proc_macro_server.as_ref().map_err(Clone::clone).and_then( + |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]), + ) + }, + ), + ) + }) + .collect() + }; + + let project_folders = ProjectFolders::new(&[ws], &[]); + loader.set_config(vfs::loader::Config { + load: project_folders.load, + watch: vec![], + version: 0, + }); + + let host = load_crate_graph( + crate_graph, + proc_macros, + project_folders.source_root_config, + &mut vfs, + &receiver, + ); + + if load_config.prefill_caches { + host.analysis().parallel_prime_caches(1, |_| {})?; + } + Ok((host, vfs, proc_macro_server.ok())) +} + +#[derive(Default)] +pub struct ProjectFolders { + pub load: Vec, + pub watch: Vec, + pub source_root_config: SourceRootConfig, +} + +impl ProjectFolders { + pub fn new(workspaces: &[ProjectWorkspace], global_excludes: &[AbsPathBuf]) -> ProjectFolders { + let mut res = ProjectFolders::default(); + let mut fsc = FileSetConfig::builder(); + let mut local_filesets = vec![]; + + // Dedup source roots + // Depending on the project setup, we can have duplicated source roots, or for example in + // the case of the rustc workspace, we can end up with two source roots that are almost the + // same but not quite, like: + // PackageRoot { is_local: false, include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri")], exclude: [] } + // PackageRoot { + // is_local: true, + // include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri"), AbsPathBuf(".../rust/build/x86_64-pc-windows-msvc/stage0-tools/x86_64-pc-windows-msvc/release/build/cargo-miri-85801cd3d2d1dae4/out")], + // exclude: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri/.git"), AbsPathBuf(".../rust/src/tools/miri/cargo-miri/target")] + // } + // + // The first one comes from the explicit rustc workspace which points to the rustc workspace itself + // The second comes from the rustc workspace that we load as the actual project workspace + // These `is_local` differing in this kind of way gives us problems, especially when trying to filter diagnostics as we don't report diagnostics for external libraries. + // So we need to deduplicate these, usually it would be enough to deduplicate by `include`, but as the rustc example shows here that doesn't work, + // so we need to also coalesce the includes if they overlap. + + let mut roots: Vec<_> = workspaces + .iter() + .flat_map(|ws| ws.to_roots()) + .update(|root| root.include.sort()) + .sorted_by(|a, b| a.include.cmp(&b.include)) + .collect(); + + // map that tracks indices of overlapping roots + let mut overlap_map = FxHashMap::<_, Vec<_>>::default(); + let mut done = false; + + while !mem::replace(&mut done, true) { + // maps include paths to indices of the corresponding root + let mut include_to_idx = FxHashMap::default(); + // Find and note down the indices of overlapping roots + for (idx, root) in roots.iter().enumerate().filter(|(_, it)| !it.include.is_empty()) { + for include in &root.include { + match include_to_idx.entry(include) { + Entry::Occupied(e) => { + overlap_map.entry(*e.get()).or_default().push(idx); + } + Entry::Vacant(e) => { + e.insert(idx); + } + } + } + } + for (k, v) in overlap_map.drain() { + done = false; + for v in v { + let r = mem::replace( + &mut roots[v], + PackageRoot { is_local: false, include: vec![], exclude: vec![] }, + ); + roots[k].is_local |= r.is_local; + roots[k].include.extend(r.include); + roots[k].exclude.extend(r.exclude); + } + roots[k].include.sort(); + roots[k].exclude.sort(); + roots[k].include.dedup(); + roots[k].exclude.dedup(); + } + } + + for root in roots.into_iter().filter(|it| !it.include.is_empty()) { + let file_set_roots: Vec = + root.include.iter().cloned().map(VfsPath::from).collect(); + + let entry = { + let mut dirs = vfs::loader::Directories::default(); + dirs.extensions.push("rs".into()); + dirs.include.extend(root.include); + dirs.exclude.extend(root.exclude); + for excl in global_excludes { + if dirs + .include + .iter() + .any(|incl| incl.starts_with(excl) || excl.starts_with(incl)) + { + dirs.exclude.push(excl.clone()); + } + } + + vfs::loader::Entry::Directories(dirs) + }; + + if root.is_local { + res.watch.push(res.load.len()); + } + res.load.push(entry); + + if root.is_local { + local_filesets.push(fsc.len()); + } + fsc.add_file_set(file_set_roots) + } + + let fsc = fsc.build(); + res.source_root_config = SourceRootConfig { fsc, local_filesets }; + + res + } +} + +#[derive(Default, Debug)] +pub struct SourceRootConfig { + pub fsc: FileSetConfig, + pub local_filesets: Vec, +} + +impl SourceRootConfig { + pub fn partition(&self, vfs: &vfs::Vfs) -> Vec { + self.fsc + .partition(vfs) + .into_iter() + .enumerate() + .map(|(idx, file_set)| { + let is_local = self.local_filesets.contains(&idx); + if is_local { + SourceRoot::new_local(file_set) + } else { + SourceRoot::new_library(file_set) + } + }) + .collect() + } +} + +/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace` +/// with an identity dummy expander. +pub fn load_proc_macro( + server: &ProcMacroServer, + path: &AbsPath, + dummy_replace: &[Box], +) -> ProcMacroLoadResult { + let res: Result, String> = (|| { + let dylib = MacroDylib::new(path.to_path_buf()); + let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?; + if vec.is_empty() { + return Err("proc macro library returned no proc macros".to_string()); + } + Ok(vec + .into_iter() + .map(|expander| expander_to_proc_macro(expander, dummy_replace)) + .collect()) + })(); + match res { + Ok(proc_macros) => { + tracing::info!( + "Loaded proc-macros for {path}: {:?}", + proc_macros.iter().map(|it| it.name.clone()).collect::>() + ); + Ok(proc_macros) + } + Err(e) => { + tracing::warn!("proc-macro loading for {path} failed: {e}"); + Err(e) + } + } +} + +fn load_crate_graph( + crate_graph: CrateGraph, + proc_macros: ProcMacros, + source_root_config: SourceRootConfig, + vfs: &mut vfs::Vfs, + receiver: &Receiver, +) -> AnalysisHost { + let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok()); + let mut host = AnalysisHost::new(lru_cap); + let mut analysis_change = Change::new(); + + host.raw_database_mut().enable_proc_attr_macros(); + + // wait until Vfs has loaded all roots + for task in receiver { + match task { + vfs::loader::Message::Progress { n_done, n_total, config_version: _ } => { + if n_done == n_total { + break; + } + } + vfs::loader::Message::Loaded { files } => { + for (path, contents) in files { + vfs.set_file_contents(path.into(), contents); + } + } + } + } + let changes = vfs.take_changes(); + for file in changes { + if file.exists() { + let contents = vfs.file_contents(file.file_id); + if let Ok(text) = std::str::from_utf8(contents) { + analysis_change.change_file(file.file_id, Some(text.into())) + } + } + } + let source_roots = source_root_config.partition(vfs); + analysis_change.set_roots(source_roots); + + analysis_change.set_crate_graph(crate_graph); + analysis_change.set_proc_macros(proc_macros); + + host.apply_change(analysis_change); + host +} + +fn expander_to_proc_macro( + expander: proc_macro_api::ProcMacro, + dummy_replace: &[Box], +) -> ProcMacro { + let name = From::from(expander.name()); + let kind = match expander.kind() { + proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive, + proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike, + proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr, + }; + let expander: sync::Arc = + if dummy_replace.iter().any(|replace| &**replace == name) { + match kind { + ProcMacroKind::Attr => sync::Arc::new(IdentityExpander), + _ => sync::Arc::new(EmptyExpander), + } + } else { + sync::Arc::new(Expander(expander)) + }; + ProcMacro { name, kind, expander } +} + +#[derive(Debug)] +struct Expander(proc_macro_api::ProcMacro); + +impl ProcMacroExpander for Expander { + fn expand( + &self, + subtree: &tt::Subtree, + attrs: Option<&tt::Subtree>, + env: &Env, + ) -> Result { + let env = env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(); + match self.0.expand(subtree, attrs, env) { + Ok(Ok(subtree)) => Ok(subtree), + Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)), + Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), + } + } +} + +/// Dummy identity expander, used for attribute proc-macros that are deliberately ignored by the user. +#[derive(Debug)] +struct IdentityExpander; + +impl ProcMacroExpander for IdentityExpander { + fn expand( + &self, + subtree: &tt::Subtree, + _: Option<&tt::Subtree>, + _: &Env, + ) -> Result { + Ok(subtree.clone()) + } +} + +/// Empty expander, used for proc-macros that are deliberately ignored by the user. +#[derive(Debug)] +struct EmptyExpander; + +impl ProcMacroExpander for EmptyExpander { + fn expand( + &self, + _: &tt::Subtree, + _: Option<&tt::Subtree>, + _: &Env, + ) -> Result { + Ok(tt::Subtree::empty()) + } +} + +#[cfg(test)] +mod tests { + use ide_db::base_db::SourceDatabase; + + use super::*; + + #[test] + fn test_loading_rust_analyzer() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); + let cargo_config = CargoConfig::default(); + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: false, + with_proc_macro_server: ProcMacroServerChoice::None, + prefill_caches: false, + }; + let (host, _vfs, _proc_macro) = + load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap(); + + let n_crates = host.raw_database().crate_graph().iter().count(); + // RA has quite a few crates, but the exact count doesn't matter + assert!(n_crates > 20); + } +} diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index d28dd17def37..9d43e130457d 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -20,10 +20,7 @@ fn benchmark_parse_macro_rules() { let rules = macro_rules_fixtures_tt(); let hash: usize = { let _pt = bench("mbe parse macro rules"); - rules - .values() - .map(|it| DeclarativeMacro::parse_macro_rules(it, true).unwrap().rules.len()) - .sum() + rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it, true).rules.len()).sum() }; assert_eq!(hash, 1144); } @@ -41,7 +38,7 @@ fn benchmark_expand_macro_rules() { invocations .into_iter() .map(|(id, tt)| { - let res = rules[&id].expand(&tt); + let res = rules[&id].expand(tt); assert!(res.err.is_none()); res.value.token_trees.len() }) @@ -53,7 +50,7 @@ fn benchmark_expand_macro_rules() { fn macro_rules_fixtures() -> FxHashMap { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true).unwrap())) + .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true))) .collect() } @@ -105,7 +102,7 @@ fn invocation_fixtures(rules: &FxHashMap) -> Vec<(Stri for op in rule.lhs.iter() { collect_from_op(op, &mut subtree, &mut seed); } - if it.expand(&subtree).err.is_none() { + if it.expand(subtree.clone()).err.is_none() { res.push((name.clone(), subtree)); break; } diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 474826079d73..1a7b7eed2953 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -884,7 +884,7 @@ impl<'a> Iterator for OpDelimitedIter<'a> { } } -impl<'a> TtIter<'a> { +impl TtIter<'_> { fn expect_separator(&mut self, separator: &Separator) -> bool { let mut fork = self.clone(); let ok = match separator { diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 5ef20ff8a9b0..665bce474a69 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -34,7 +34,8 @@ pub use ::parser::TopEntryPoint; pub use crate::{ syntax_bridge::{ - parse_exprs_with_sep, parse_to_token_tree, syntax_node_to_token_tree, + parse_exprs_with_sep, parse_to_token_tree, syntax_node_to_token_map, + syntax_node_to_token_map_with_modifications, syntax_node_to_token_tree, syntax_node_to_token_tree_with_modifications, token_tree_to_syntax_node, SyntheticToken, SyntheticTokenId, }, @@ -131,6 +132,7 @@ pub struct DeclarativeMacro { // This is used for correctly determining the behavior of the pat fragment // FIXME: This should be tracked by hygiene of the fragment identifier! is_2021: bool, + err: Option>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -206,81 +208,118 @@ impl Shift { } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Origin { Def, Call, } impl DeclarativeMacro { + pub fn from_err(err: ParseError, is_2021: bool) -> DeclarativeMacro { + DeclarativeMacro { + rules: Box::default(), + shift: Shift(0), + is_2021, + err: Some(Box::new(err)), + } + } + /// The old, `macro_rules! m {}` flavor. - pub fn parse_macro_rules( - tt: &tt::Subtree, - is_2021: bool, - ) -> Result { + pub fn parse_macro_rules(tt: &tt::Subtree, is_2021: bool) -> DeclarativeMacro { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. let mut src = TtIter::new(tt); let mut rules = Vec::new(); + let mut err = None; + while src.len() > 0 { - let rule = Rule::parse(&mut src, true)?; + let rule = match Rule::parse(&mut src, true) { + Ok(it) => it, + Err(e) => { + err = Some(Box::new(e)); + break; + } + }; rules.push(rule); if let Err(()) = src.expect_char(';') { if src.len() > 0 { - return Err(ParseError::expected("expected `;`")); + err = Some(Box::new(ParseError::expected("expected `;`"))); } break; } } for Rule { lhs, .. } in &rules { - validate(lhs)?; + if let Err(e) = validate(lhs) { + err = Some(Box::new(e)); + break; + } } - Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 }) + DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021, err } } /// The new, unstable `macro m {}` flavor. - pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> Result { + pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> DeclarativeMacro { let mut src = TtIter::new(tt); let mut rules = Vec::new(); + let mut err = None; if tt::DelimiterKind::Brace == tt.delimiter.kind { cov_mark::hit!(parse_macro_def_rules); while src.len() > 0 { - let rule = Rule::parse(&mut src, true)?; + let rule = match Rule::parse(&mut src, true) { + Ok(it) => it, + Err(e) => { + err = Some(Box::new(e)); + break; + } + }; rules.push(rule); if let Err(()) = src.expect_any_char(&[';', ',']) { if src.len() > 0 { - return Err(ParseError::expected("expected `;` or `,` to delimit rules")); + err = Some(Box::new(ParseError::expected( + "expected `;` or `,` to delimit rules", + ))); } break; } } } else { cov_mark::hit!(parse_macro_def_simple); - let rule = Rule::parse(&mut src, false)?; - if src.len() != 0 { - return Err(ParseError::expected("remaining tokens in macro def")); + match Rule::parse(&mut src, false) { + Ok(rule) => { + if src.len() != 0 { + err = Some(Box::new(ParseError::expected("remaining tokens in macro def"))); + } + rules.push(rule); + } + Err(e) => { + err = Some(Box::new(e)); + } } - rules.push(rule); } for Rule { lhs, .. } in &rules { - validate(lhs)?; + if let Err(e) = validate(lhs) { + err = Some(Box::new(e)); + break; + } } - Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 }) + DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021, err } } - pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult { - // apply shift - let mut tt = tt.clone(); + pub fn expand(&self, mut tt: tt::Subtree) -> ExpandResult { self.shift.shift_all(&mut tt); expander::expand_rules(&self.rules, &tt, self.is_2021) } + pub fn err(&self) -> Option<&ParseError> { + self.err.as_deref() + } + pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { self.shift.shift(id) } diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 8cbf0f8fc0bd..62b2accf5cda 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -53,6 +53,37 @@ pub fn syntax_node_to_token_tree_with_modifications( (subtree, c.id_alloc.map, c.id_alloc.next_id) } +/// Convert the syntax node to a `TokenTree` (what macro +/// will consume). +pub fn syntax_node_to_token_map(node: &SyntaxNode) -> TokenMap { + syntax_node_to_token_map_with_modifications( + node, + Default::default(), + 0, + Default::default(), + Default::default(), + ) + .0 +} + +/// Convert the syntax node to a `TokenTree` (what macro will consume) +/// with the censored range excluded. +pub fn syntax_node_to_token_map_with_modifications( + node: &SyntaxNode, + existing_token_map: TokenMap, + next_id: u32, + replace: FxHashMap>, + append: FxHashMap>, +) -> (TokenMap, u32) { + let global_offset = node.text_range().start(); + let mut c = Converter::new(node, global_offset, existing_token_map, next_id, replace, append); + collect_tokens(&mut c); + c.id_alloc.map.shrink_to_fit(); + always!(c.replace.is_empty(), "replace: {:?}", c.replace); + always!(c.append.is_empty(), "append: {:?}", c.append); + (c.id_alloc.map, c.id_alloc.next_id) +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct SyntheticTokenId(pub u32); @@ -327,6 +358,111 @@ fn convert_tokens(conv: &mut C) -> tt::Subtree { } } +fn collect_tokens(conv: &mut C) { + struct StackEntry { + idx: usize, + open_range: TextRange, + delimiter: tt::DelimiterKind, + } + + let entry = StackEntry { + delimiter: tt::DelimiterKind::Invisible, + // never used (delimiter is `None`) + idx: !0, + open_range: TextRange::empty(TextSize::of('.')), + }; + let mut stack = NonEmptyVec::new(entry); + + loop { + let StackEntry { delimiter, .. } = stack.last_mut(); + let (token, range) = match conv.bump() { + Some(it) => it, + None => break, + }; + let synth_id = token.synthetic_id(conv); + + let kind = token.kind(conv); + if kind == COMMENT { + // Since `convert_doc_comment` can fail, we need to peek the next id, so that we can + // figure out which token id to use for the doc comment, if it is converted successfully. + let next_id = conv.id_alloc().peek_next_id(); + if let Some(_tokens) = conv.convert_doc_comment(&token, next_id) { + let id = conv.id_alloc().alloc(range, synth_id); + debug_assert_eq!(id, next_id); + } + continue; + } + if kind.is_punct() && kind != UNDERSCORE { + if synth_id.is_none() { + assert_eq!(range.len(), TextSize::of('.')); + } + + let expected = match delimiter { + tt::DelimiterKind::Parenthesis => Some(T![')']), + tt::DelimiterKind::Brace => Some(T!['}']), + tt::DelimiterKind::Bracket => Some(T![']']), + tt::DelimiterKind::Invisible => None, + }; + + if let Some(expected) = expected { + if kind == expected { + if let Some(entry) = stack.pop() { + conv.id_alloc().close_delim(entry.idx, Some(range)); + } + continue; + } + } + + let delim = match kind { + T!['('] => Some(tt::DelimiterKind::Parenthesis), + T!['{'] => Some(tt::DelimiterKind::Brace), + T!['['] => Some(tt::DelimiterKind::Bracket), + _ => None, + }; + + if let Some(kind) = delim { + let (_id, idx) = conv.id_alloc().open_delim(range, synth_id); + + stack.push(StackEntry { idx, open_range: range, delimiter: kind }); + continue; + } + + conv.id_alloc().alloc(range, synth_id); + } else { + macro_rules! make_leaf { + ($i:ident) => {{ + conv.id_alloc().alloc(range, synth_id); + }}; + } + match kind { + T![true] | T![false] => make_leaf!(Ident), + IDENT => make_leaf!(Ident), + UNDERSCORE => make_leaf!(Ident), + k if k.is_keyword() => make_leaf!(Ident), + k if k.is_literal() => make_leaf!(Literal), + LIFETIME_IDENT => { + let char_unit = TextSize::of('\''); + let r = TextRange::at(range.start(), char_unit); + conv.id_alloc().alloc(r, synth_id); + + let r = TextRange::at(range.start() + char_unit, range.len() - char_unit); + conv.id_alloc().alloc(r, synth_id); + continue; + } + _ => continue, + }; + }; + + // If we get here, we've consumed all input tokens. + // We might have more than one subtree in the stack, if the delimiters are improperly balanced. + // Merge them so we're left with one. + while let Some(entry) = stack.pop() { + conv.id_alloc().close_delim(entry.idx, None); + conv.id_alloc().alloc(entry.open_range, None); + } + } +} + fn is_single_token_op(kind: SyntaxKind) -> bool { matches!( kind, @@ -509,12 +645,12 @@ trait TokenConverter: Sized { fn id_alloc(&mut self) -> &mut TokenIdAlloc; } -impl<'a> SrcToken> for usize { - fn kind(&self, ctx: &RawConverter<'a>) -> SyntaxKind { +impl SrcToken> for usize { + fn kind(&self, ctx: &RawConverter<'_>) -> SyntaxKind { ctx.lexed.kind(*self) } - fn to_char(&self, ctx: &RawConverter<'a>) -> Option { + fn to_char(&self, ctx: &RawConverter<'_>) -> Option { ctx.lexed.text(*self).chars().next() } @@ -522,12 +658,12 @@ impl<'a> SrcToken> for usize { ctx.lexed.text(*self).into() } - fn synthetic_id(&self, _ctx: &RawConverter<'a>) -> Option { + fn synthetic_id(&self, _ctx: &RawConverter<'_>) -> Option { None } } -impl<'a> TokenConverter for RawConverter<'a> { +impl TokenConverter for RawConverter<'_> { type Token = usize; fn convert_doc_comment(&self, &token: &usize, span: tt::TokenId) -> Option> { @@ -800,7 +936,7 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> { Some(&texts[idx..texts.len() - (1 - idx)]) } -impl<'a> TtTreeSink<'a> { +impl TtTreeSink<'_> { /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween. /// This occurs when a float literal is used as a field access. fn float_split(&mut self, has_pseudo_dot: bool) { diff --git a/crates/mbe/src/token_map.rs b/crates/mbe/src/token_map.rs index c923e7a69a1b..9b2df89f9c71 100644 --- a/crates/mbe/src/token_map.rs +++ b/crates/mbe/src/token_map.rs @@ -110,4 +110,11 @@ impl TokenMap { // FIXME: This could be accidentally quadratic self.entries.remove(idx); } + + pub fn entries(&self) -> impl Iterator + '_ { + self.entries.iter().filter_map(|&(tid, tr)| match tr { + TokenTextRange::Token(range) => Some((tid, range)), + TokenTextRange::Delimiter(_) => None, + }) + } } diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs index 59dbf156800d..79ff8ca28e86 100644 --- a/crates/mbe/src/tt_iter.rs +++ b/crates/mbe/src/tt_iter.rs @@ -197,4 +197,4 @@ impl<'a> Iterator for TtIter<'a> { } } -impl<'a> std::iter::ExactSizeIterator for TtIter<'a> {} +impl std::iter::ExactSizeIterator for TtIter<'_> {} diff --git a/crates/parser/src/grammar/generic_args.rs b/crates/parser/src/grammar/generic_args.rs index e589b69934d2..211af98e6ef0 100644 --- a/crates/parser/src/grammar/generic_args.rs +++ b/crates/parser/src/grammar/generic_args.rs @@ -32,6 +32,9 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[ ]) .union(types::TYPE_FIRST); +// Despite its name, it can also be used for generic param list. +const GENERIC_ARG_RECOVERY_SET: TokenSet = TokenSet::new(&[T![>], T![,]]); + // test generic_arg // type T = S; fn generic_arg(p: &mut Parser<'_>) -> bool { @@ -55,6 +58,15 @@ fn generic_arg(p: &mut Parser<'_>) -> bool { // test assoc_type_eq // type T = StreamingIterator = &'a T>; types::type_(p); + } else if p.at_ts(GENERIC_ARG_RECOVERY_SET) { + // Although `const_arg()` recovers as expected, we want to + // handle those here to give the following message because + // we don't know whether this associated item is a type or + // const at this point. + + // test_err recover_from_missing_assoc_item_binding + // fn f() -> impl Iterator {} + p.error("missing associated item binding"); } else { // test assoc_const_eq // fn foo>() {} @@ -141,12 +153,17 @@ pub(super) fn const_arg_expr(p: &mut Parser<'_>) { expressions::literal(p); lm.complete(p, PREFIX_EXPR); } - _ => { + _ if paths::is_use_path_start(p) => { // This shouldn't be hit by `const_arg` let lm = p.start(); paths::use_path(p); lm.complete(p, PATH_EXPR); } + _ => { + // test_err recover_from_missing_const_default + // struct A; + p.err_recover("expected a generic const argument", GENERIC_ARG_RECOVERY_SET); + } } } diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs index 7fcf938babdb..8ed1c84c4c64 100644 --- a/crates/parser/src/grammar/generic_params.rs +++ b/crates/parser/src/grammar/generic_params.rs @@ -79,10 +79,9 @@ fn const_param(p: &mut Parser<'_>, m: Marker) { p.error("missing type for const parameter"); } - if p.at(T![=]) { + if p.eat(T![=]) { // test const_param_default_literal // struct A; - p.bump(T![=]); // test const_param_default_expression // struct A; diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs index 5cdb39700dda..6e3ae656b025 100644 --- a/crates/parser/src/shortcuts.rs +++ b/crates/parser/src/shortcuts.rs @@ -24,7 +24,7 @@ pub enum StrStep<'a> { Error { msg: &'a str, pos: usize }, } -impl<'a> LexedStr<'a> { +impl LexedStr<'_> { pub fn to_input(&self) -> crate::Input { let mut res = crate::Input::default(); let mut was_joint = false; diff --git a/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast new file mode 100644 index 000000000000..fc59db84e77d --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast @@ -0,0 +1,48 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + THIN_ARROW "->" + WHITESPACE " " + IMPL_TRAIT_TYPE + IMPL_KW "impl" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + GENERIC_ARG_LIST + L_ANGLE "<" + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + WHITESPACE " " + EQ "=" + WHITESPACE " " + COMMA "," + WHITESPACE " " + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + WHITESPACE " " + EQ "=" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 30: missing associated item binding +error 39: missing associated item binding diff --git a/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs new file mode 100644 index 000000000000..e484e433a09f --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs @@ -0,0 +1 @@ +fn f() -> impl Iterator {} diff --git a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast new file mode 100644 index 000000000000..809ad1b8d5b9 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast @@ -0,0 +1,44 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "A" + GENERIC_PARAM_LIST + L_ANGLE "<" + CONST_PARAM + CONST_KW "const" + WHITESPACE " " + NAME + IDENT "N" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + EQ "=" + WHITESPACE " " + COMMA "," + WHITESPACE " " + CONST_PARAM + CONST_KW "const" + WHITESPACE " " + NAME + IDENT "M" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + EQ "=" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" +error 23: expected a generic const argument +error 40: expected a generic const argument diff --git a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs new file mode 100644 index 000000000000..5bab13da92b6 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs @@ -0,0 +1 @@ +struct A; diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index e0c20a4143bd..88b8d0aee3a4 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -6,7 +6,7 @@ use std::{ borrow::Borrow, ffi::OsStr, - ops, + fmt, ops, path::{Component, Path, PathBuf}, }; @@ -95,6 +95,12 @@ impl AbsPathBuf { } } +impl fmt::Display for AbsPathBuf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0.display(), f) + } +} + /// Wrapper around an absolute [`Path`]. #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] #[repr(transparent)] @@ -217,6 +223,7 @@ impl AbsPath { pub fn as_os_str(&self) -> &OsStr { self.0.as_os_str() } + #[deprecated(note = "use Display instead")] pub fn display(&self) -> std::path::Display<'_> { self.0.display() } @@ -227,6 +234,12 @@ impl AbsPath { // endregion:delegate-methods } +impl fmt::Display for AbsPath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0.display(), f) + } +} + /// Wrapper around a relative [`PathBuf`]. #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub struct RelPathBuf(PathBuf); diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index d3486e75575b..4e39167136b4 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [dependencies] -object = { version = "0.30.2", default-features = false, features = [ +object = { version = "0.31.0", default-features = false, features = [ "std", "read_core", "elf", diff --git a/crates/proc-macro-api/src/version.rs b/crates/proc-macro-api/src/version.rs index 13f67a0128a7..48efbf589c6a 100644 --- a/crates/proc-macro-api/src/version.rs +++ b/crates/proc-macro-api/src/version.rs @@ -135,7 +135,12 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result { } }; - let mut snappy_decoder = SnapDecoder::new(snappy_portion); + let mut uncompressed: Box = if &snappy_portion[0..4] == b"rust" { + // Not compressed. + Box::new(snappy_portion) + } else { + Box::new(SnapDecoder::new(snappy_portion)) + }; // the bytes before version string bytes, so this basically is: // 8 bytes for [b'r',b'u',b's',b't',0,0,0,5] @@ -144,11 +149,11 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result { // so 13 bytes in total, and we should check the 13th byte // to know the length let mut bytes_before_version = [0u8; 13]; - snappy_decoder.read_exact(&mut bytes_before_version)?; + uncompressed.read_exact(&mut bytes_before_version)?; let length = bytes_before_version[12]; let mut version_string_utf8 = vec![0u8; length as usize]; - snappy_decoder.read_exact(&mut version_string_utf8)?; + uncompressed.read_exact(&mut version_string_utf8)?; let version_string = String::from_utf8(version_string_utf8); version_string.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index d5eb157bfef9..ecc6aaa0ac73 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -12,14 +12,14 @@ rust-version.workspace = true doctest = false [dependencies] -object = { version = "0.30.2", default-features = false, features = [ +object = { version = "0.31.0", default-features = false, features = [ "std", "read_core", "elf", "macho", "pe", ] } -libloading = "0.7.3" +libloading = "0.8.0" memmap2 = "0.5.4" stdx.workspace = true diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 602e74275106..937834a82aef 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -15,7 +15,7 @@ doctest = false once_cell = "1.17.0" cfg-if = "1.0.0" libc = "0.2.135" -la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +la-arena.workspace = true countme = { version = "3.0.1", features = ["enable"] } jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true } diff --git a/crates/profile/src/tree.rs b/crates/profile/src/tree.rs index 62f0c30b5293..1290fba36fab 100644 --- a/crates/profile/src/tree.rs +++ b/crates/profile/src/tree.rs @@ -72,7 +72,7 @@ struct NodeIter<'a, T> { next: Option>, } -impl<'a, T> Iterator for NodeIter<'a, T> { +impl Iterator for NodeIter<'_, T> { type Item = Idx; fn next(&mut self) -> Option> { diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index 3abff64a83b2..75977fc5b04f 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml @@ -20,7 +20,7 @@ serde_json.workspace = true serde.workspace = true triomphe.workspace = true anyhow = "1.0.62" -la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +la-arena.workspace = true itertools = "0.10.5" # local deps diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 6cbf403cb2e1..fb0f3ab7d174 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -225,9 +225,8 @@ impl WorkspaceBuildScripts { let package_build_data = &mut res[idx].outputs[package]; if !package_build_data.is_unchanged() { tracing::info!( - "{}: {:?}", - workspace[package].manifest.parent().display(), - package_build_data, + "{}: {package_build_data:?}", + workspace[package].manifest.parent(), ); } } @@ -270,9 +269,8 @@ impl WorkspaceBuildScripts { let package_build_data = &outputs[package]; if !package_build_data.is_unchanged() { tracing::info!( - "{}: {:?}", - workspace[package].manifest.parent().display(), - package_build_data, + "{}: {package_build_data:?}", + workspace[package].manifest.parent(), ); } } @@ -424,7 +422,7 @@ impl WorkspaceBuildScripts { let target_libdir = AbsPathBuf::try_from(PathBuf::from(target_libdir)) .map_err(|_| anyhow::format_err!("target-libdir was not an absolute path"))?; - tracing::info!("Loading rustc proc-macro paths from {}", target_libdir.display()); + tracing::info!("Loading rustc proc-macro paths from {target_libdir}"); let proc_macro_dylibs: Vec<(String, AbsPathBuf)> = std::fs::read_dir(target_libdir)? .filter_map(|entry| { @@ -458,9 +456,8 @@ impl WorkspaceBuildScripts { let package_build_data = &bs.outputs[package]; if !package_build_data.is_unchanged() { tracing::info!( - "{}: {:?}", - rustc[package].manifest.parent().display(), - package_build_data, + "{}: {package_build_data:?}", + rustc[package].manifest.parent(), ); } } diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 92b454150c3e..e1117ac464bd 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use std::str::from_utf8; use std::{ops, process::Command}; -use anyhow::{Context, Result}; +use anyhow::Context; use base_db::Edition; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; @@ -236,7 +236,7 @@ impl CargoWorkspace { current_dir: &AbsPath, config: &CargoConfig, progress: &dyn Fn(String), - ) -> Result { + ) -> anyhow::Result { let targets = find_list_of_build_targets(config, cargo_toml); let mut meta = MetadataCommand::new(); diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 61acc646f810..901dcfd2b110 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -31,12 +31,13 @@ pub mod target_data_layout; mod tests; use std::{ + fmt, fs::{self, read_dir, ReadDir}, io, process::Command, }; -use anyhow::{bail, format_err, Context, Result}; +use anyhow::{bail, format_err, Context}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashSet; @@ -59,19 +60,19 @@ pub enum ProjectManifest { } impl ProjectManifest { - pub fn from_manifest_file(path: AbsPathBuf) -> Result { + pub fn from_manifest_file(path: AbsPathBuf) -> anyhow::Result { let path = ManifestPath::try_from(path) - .map_err(|path| format_err!("bad manifest path: {}", path.display()))?; + .map_err(|path| format_err!("bad manifest path: {path}"))?; if path.file_name().unwrap_or_default() == "rust-project.json" { return Ok(ProjectManifest::ProjectJson(path)); } if path.file_name().unwrap_or_default() == "Cargo.toml" { return Ok(ProjectManifest::CargoToml(path)); } - bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()); + bail!("project root must point to Cargo.toml or rust-project.json: {path}"); } - pub fn discover_single(path: &AbsPath) -> Result { + pub fn discover_single(path: &AbsPath) -> anyhow::Result { let mut candidates = ProjectManifest::discover(path)?; let res = match candidates.pop() { None => bail!("no projects"), @@ -145,7 +146,17 @@ impl ProjectManifest { } } -fn utf8_stdout(mut cmd: Command) -> Result { +impl fmt::Display for ProjectManifest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ProjectManifest::ProjectJson(it) | ProjectManifest::CargoToml(it) => { + fmt::Display::fmt(&it, f) + } + } + } +} + +fn utf8_stdout(mut cmd: Command) -> anyhow::Result { let output = cmd.output().with_context(|| format!("{cmd:?} failed"))?; if !output.status.success() { match String::from_utf8(output.stderr) { diff --git a/crates/project-model/src/manifest_path.rs b/crates/project-model/src/manifest_path.rs index 3f60e4dd92f4..490e1a4ea886 100644 --- a/crates/project-model/src/manifest_path.rs +++ b/crates/project-model/src/manifest_path.rs @@ -1,5 +1,5 @@ //! See [`ManifestPath`]. -use std::{ops, path::Path}; +use std::{fmt, ops, path::Path}; use paths::{AbsPath, AbsPathBuf}; @@ -40,6 +40,12 @@ impl ManifestPath { } } +impl fmt::Display for ManifestPath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.file, f) + } +} + impl ops::Deref for ManifestPath { type Target = AbsPath; diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index 0066f6717efe..8392718b2274 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -2,7 +2,6 @@ use std::process::Command; -use anyhow::Result; use rustc_hash::FxHashMap; use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath}; @@ -23,6 +22,9 @@ pub(crate) fn get( } } + // Add miri cfg, which is useful for mir eval in stdlib + res.push(CfgFlag::Atom("miri".into())); + match get_rust_cfgs(cargo_toml, target, extra_env) { Ok(rustc_cfgs) => { tracing::debug!( @@ -44,7 +46,7 @@ fn get_rust_cfgs( cargo_toml: Option<&ManifestPath>, target: Option<&str>, extra_env: &FxHashMap, -) -> Result { +) -> anyhow::Result { if let Some(cargo_toml) = cargo_toml { let mut cargo_config = Command::new(toolchain::cargo()); cargo_config.envs(extra_env); diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index e3a2de927c98..da862c9e87f2 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -85,9 +85,8 @@ impl Sysroot { " try running `rustup component add rust-src` to possible fix this" }; Some(format!( - "could not find libcore in loaded sysroot at `{}`{}", - self.src_root.as_path().display(), - var_note, + "could not find libcore in loaded sysroot at `{}`{var_note}", + self.src_root.as_path(), )) } else { None @@ -99,7 +98,7 @@ impl Sysroot { impl Sysroot { /// Attempts to discover the toolchain's sysroot from the given `dir`. pub fn discover(dir: &AbsPath, extra_env: &FxHashMap) -> Result { - tracing::debug!("discovering sysroot for {}", dir.display()); + tracing::debug!("discovering sysroot for {dir}"); let sysroot_dir = discover_sysroot_dir(dir, extra_env)?; let sysroot_src_dir = discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?; @@ -111,7 +110,7 @@ impl Sysroot { extra_env: &FxHashMap, src: AbsPathBuf, ) -> Result { - tracing::debug!("discovering sysroot for {}", current_dir.display()); + tracing::debug!("discovering sysroot for {current_dir}"); let sysroot_dir = discover_sysroot_dir(current_dir, extra_env)?; Ok(Sysroot::load(sysroot_dir, src)) } @@ -122,7 +121,7 @@ impl Sysroot { pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| { - format_err!("can't load standard library from sysroot path {}", sysroot_dir.display()) + format_err!("can't load standard library from sysroot path {sysroot_dir}") })?; Ok(Sysroot::load(sysroot_dir, sysroot_src_dir)) } @@ -220,10 +219,10 @@ fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option { if let Ok(path) = AbsPathBuf::try_from(path.as_str()) { let core = path.join("core"); if fs::metadata(&core).is_ok() { - tracing::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display()); + tracing::debug!("Discovered sysroot by RUST_SRC_PATH: {path}"); return Some(path); } - tracing::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); + tracing::debug!("RUST_SRC_PATH is set, but is invalid (no core: {core:?}), ignoring"); } else { tracing::debug!("RUST_SRC_PATH is set, but is invalid, ignoring"); } @@ -250,10 +249,9 @@ fn discover_sysroot_src_dir_or_add_component( format_err!( "\ can't load standard library from sysroot -{} +{sysroot_path} (discovered via `rustc --print sysroot`) try installing the Rust source the same way you installed rustc", - sysroot_path.display(), ) }) } @@ -261,7 +259,7 @@ try installing the Rust source the same way you installed rustc", fn get_rustc_src(sysroot_path: &AbsPath) -> Option { let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml"); let rustc_src = ManifestPath::try_from(rustc_src).ok()?; - tracing::debug!("checking for rustc source code: {}", rustc_src.display()); + tracing::debug!("checking for rustc source code: {rustc_src}"); if fs::metadata(&rustc_src).is_ok() { Some(rustc_src) } else { @@ -271,7 +269,7 @@ fn get_rustc_src(sysroot_path: &AbsPath) -> Option { fn get_rust_src(sysroot_path: &AbsPath) -> Option { let rust_src = sysroot_path.join("lib/rustlib/src/rust/library"); - tracing::debug!("checking sysroot library: {}", rust_src.display()); + tracing::debug!("checking sysroot library: {rust_src}"); if fs::metadata(&rust_src).is_ok() { Some(rust_src) } else { diff --git a/crates/project-model/src/target_data_layout.rs b/crates/project-model/src/target_data_layout.rs index 30ca7b348e84..cb995857ec7d 100644 --- a/crates/project-model/src/target_data_layout.rs +++ b/crates/project-model/src/target_data_layout.rs @@ -1,7 +1,6 @@ //! Runs `rustc --print target-spec-json` to get the target_data_layout. use std::process::Command; -use anyhow::Result; use rustc_hash::FxHashMap; use crate::{utf8_stdout, ManifestPath}; @@ -10,7 +9,7 @@ pub fn get( cargo_toml: Option<&ManifestPath>, target: Option<&str>, extra_env: &FxHashMap, -) -> Result { +) -> anyhow::Result { let output = (|| { if let Some(cargo_toml) = cargo_toml { let mut cmd = Command::new(toolchain::rustc()); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index b5fe237fc4c1..f51ea7eeb224 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -4,7 +4,7 @@ use std::{collections::VecDeque, fmt, fs, process::Command, sync}; -use anyhow::{format_err, Context, Result}; +use anyhow::{format_err, Context}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult, @@ -151,7 +151,16 @@ impl ProjectWorkspace { manifest: ProjectManifest, config: &CargoConfig, progress: &dyn Fn(String), - ) -> Result { + ) -> anyhow::Result { + ProjectWorkspace::load_inner(&manifest, config, progress) + .with_context(|| format!("Failed to load the project at {manifest}")) + } + + fn load_inner( + manifest: &ProjectManifest, + config: &CargoConfig, + progress: &dyn Fn(String), + ) -> anyhow::Result { let version = |current_dir, cmd_path, prefix: &str| { let cargo_version = utf8_stdout({ let mut cmd = Command::new(cmd_path); @@ -167,12 +176,10 @@ impl ProjectWorkspace { }; let res = match manifest { ProjectManifest::ProjectJson(project_json) => { - let file = fs::read_to_string(&project_json).with_context(|| { - format!("Failed to read json file {}", project_json.display()) - })?; - let data = serde_json::from_str(&file).with_context(|| { - format!("Failed to deserialize json file {}", project_json.display()) - })?; + let file = fs::read_to_string(&project_json) + .with_context(|| format!("Failed to read json file {project_json}"))?; + let data = serde_json::from_str(&file) + .with_context(|| format!("Failed to deserialize json file {project_json}"))?; let project_location = project_json.parent().to_path_buf(); let toolchain = version(&*project_location, toolchain::rustc(), "rustc ")?; let project_json = ProjectJson::new(&project_location, data); @@ -193,9 +200,7 @@ impl ProjectWorkspace { ) .with_context(|| { format!( - "Failed to read Cargo metadata from Cargo.toml file {}, {:?}", - cargo_toml.display(), - toolchain + "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", ) })?; let cargo = CargoWorkspace::new(meta); @@ -203,12 +208,12 @@ impl ProjectWorkspace { let sysroot = match (&config.sysroot, &config.sysroot_src) { (Some(RustLibSource::Path(path)), None) => { Sysroot::with_sysroot_dir(path.clone()).map_err(|e| { - Some(format!("Failed to find sysroot at {}:{e}", path.display())) + Some(format!("Failed to find sysroot at {path}:{e}")) }) } (Some(RustLibSource::Discover), None) => { Sysroot::discover(cargo_toml.parent(), &config.extra_env).map_err(|e| { - Some(format!("Failed to find sysroot for Cargo.toml file {}. Is rust-src installed? {e}", cargo_toml.display())) + Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}")) }) } (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => { @@ -220,21 +225,19 @@ impl ProjectWorkspace { &config.extra_env, sysroot_src.clone(), ).map_err(|e| { - Some(format!("Failed to find sysroot for Cargo.toml file {}. Is rust-src installed? {e}", cargo_toml.display())) + Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}")) }) } (None, _) => Err(None), }; if let Ok(sysroot) = &sysroot { - tracing::info!(workspace = %cargo_toml.display(), src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot"); + tracing::info!(workspace = %cargo_toml, src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); } let rustc_dir = match &config.rustc_source { Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) - .map_err(|p| { - Some(format!("rustc source path is not absolute: {}", p.display())) - }), + .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), Some(RustLibSource::Discover) => { sysroot.as_ref().ok().and_then(Sysroot::discover_rustc).ok_or_else(|| { Some(format!("Failed to discover rustc source for sysroot.")) @@ -244,7 +247,7 @@ impl ProjectWorkspace { }; let rustc = rustc_dir.and_then(|rustc_dir| { - tracing::info!(workspace = %cargo_toml.display(), rustc_dir = %rustc_dir.display(), "Using rustc source"); + tracing::info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source"); match CargoWorkspace::fetch_metadata( &rustc_dir, cargo_toml.parent(), @@ -266,13 +269,11 @@ impl ProjectWorkspace { Err(e) => { tracing::error!( %e, - "Failed to read Cargo metadata from rustc source at {}", - rustc_dir.display() + "Failed to read Cargo metadata from rustc source at {rustc_dir}", ); Err(Some(format!( - "Failed to read Cargo metadata from rustc source at {}: {e}", - rustc_dir.display()) - )) + "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}" + ))) } } }); @@ -330,7 +331,7 @@ impl ProjectWorkspace { (None, None) => Err(None), }; if let Ok(sysroot) = &sysroot { - tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot"); + tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); } let rustc_cfg = rustc_cfg::get(None, target, extra_env); @@ -340,26 +341,23 @@ impl ProjectWorkspace { pub fn load_detached_files( detached_files: Vec, config: &CargoConfig, - ) -> Result { + ) -> anyhow::Result { let sysroot = match &config.sysroot { Some(RustLibSource::Path(path)) => Sysroot::with_sysroot_dir(path.clone()) - .map_err(|e| Some(format!("Failed to find sysroot at {}:{e}", path.display()))), + .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))), Some(RustLibSource::Discover) => { let dir = &detached_files .first() .and_then(|it| it.parent()) .ok_or_else(|| format_err!("No detached files to load"))?; Sysroot::discover(dir, &config.extra_env).map_err(|e| { - Some(format!( - "Failed to find sysroot for {}. Is rust-src installed? {e}", - dir.display() - )) + Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}")) }) } None => Err(None), }; if let Ok(sysroot) = &sysroot { - tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot"); + tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); } let rustc_cfg = rustc_cfg::get(None, None, &Default::default()); Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) @@ -370,15 +368,12 @@ impl ProjectWorkspace { &self, config: &CargoConfig, progress: &dyn Fn(String), - ) -> Result { + ) -> anyhow::Result { match self { ProjectWorkspace::Cargo { cargo, toolchain, .. } => { WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, toolchain) .with_context(|| { - format!( - "Failed to run build scripts for {}", - &cargo.workspace_root().display() - ) + format!("Failed to run build scripts for {}", cargo.workspace_root()) }) } ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => { @@ -393,7 +388,7 @@ impl ProjectWorkspace { workspaces: &[ProjectWorkspace], config: &CargoConfig, progress: &dyn Fn(String), - ) -> Vec> { + ) -> Vec> { if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace) || config.run_build_script_command.is_none() { @@ -419,10 +414,7 @@ impl ProjectWorkspace { ProjectWorkspace::Cargo { cargo, .. } => match outputs { Ok(outputs) => Ok(outputs.next().unwrap()), Err(e) => Err(e.clone()).with_context(|| { - format!( - "Failed to run build scripts for {}", - &cargo.workspace_root().display() - ) + format!("Failed to run build scripts for {}", cargo.workspace_root()) }), }, _ => Ok(WorkspaceBuildScripts::default()), @@ -447,7 +439,7 @@ impl ProjectWorkspace { } } - pub fn find_sysroot_proc_macro_srv(&self) -> Result { + pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { match self { ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. } | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } @@ -459,22 +451,22 @@ impl ProjectWorkspace { .map(|segment| sysroot.root().join(segment).join(&standalone_server_name)) .find(|server_path| std::fs::metadata(server_path).is_ok()) .ok_or_else(|| { - anyhow::anyhow!( + anyhow::format_err!( "cannot find proc-macro server in sysroot `{}`", - sysroot.root().display() + sysroot.root() ) }) } ProjectWorkspace::DetachedFiles { .. } => { - Err(anyhow::anyhow!("cannot find proc-macro server, no sysroot was found")) + Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found")) } - ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::anyhow!( + ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::format_err!( "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", - cargo.workspace_root().display() + cargo.workspace_root() )), - ProjectWorkspace::Json { project, .. } => Err(anyhow::anyhow!( + ProjectWorkspace::Json { project, .. } => Err(anyhow::format_err!( "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", - project.path().display() + project.path() )), } } diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 5b72d57560b2..5bfac7ee45c6 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -34,10 +34,9 @@ serde.workspace = true rayon = "1.6.1" num_cpus = "1.15.0" mimalloc = { version = "0.1.30", default-features = false, optional = true } -lsp-server = { version = "0.7.0", path = "../../lib/lsp-server" } +lsp-server.workspace = true tracing = "0.1.35" tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "env-filter", "registry", "fmt", "tracing-log", @@ -48,12 +47,8 @@ triomphe.workspace = true nohash-hasher.workspace = true always-assert = "0.1.2" -# These dependencies are unused, but we pin them to a version here to restrict them for our transitive dependencies -# so that we don't pull in duplicates of their dependencies like windows-sys and syn 1 vs 2 -# these would pull in serde 2 -thiserror = "=1.0.39" -serde_repr = "=0.1.11" -# these would pull in windows-sys 0.45.0 +# These 3 deps are not used by r-a directly, but we list them here to lock in their versions +# in our transitive deps to prevent them from pulling in windows-sys 0.45.0 mio = "=0.8.5" filetime = "=0.2.19" parking_lot_core = "=0.9.6" @@ -67,13 +62,13 @@ ide-db.workspace = true # This should only be used in CLI ide-ssr.workspace = true ide.workspace = true +load-cargo.workspace = true proc-macro-api.workspace = true profile.workspace = true project-model.workspace = true stdx.workspace = true syntax.workspace = true toolchain.workspace = true -tt.workspace = true vfs-notify.workspace = true vfs.workspace = true diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs index 8caadecd850b..1f923f6cf8dd 100644 --- a/crates/rust-analyzer/src/bin/logger.rs +++ b/crates/rust-analyzer/src/bin/logger.rs @@ -8,10 +8,11 @@ use std::{ sync::Arc, }; -use rust_analyzer::Result; +use anyhow::Context; use tracing::{level_filters::LevelFilter, Event, Subscriber}; use tracing_log::NormalizeEvent; use tracing_subscriber::{ + filter::Targets, fmt::{ format::Writer, writer::BoxMakeWriter, FmtContext, FormatEvent, FormatFields, FormattedFields, MakeWriter, @@ -19,81 +20,62 @@ use tracing_subscriber::{ layer::SubscriberExt, registry::LookupSpan, util::SubscriberInitExt, - EnvFilter, Registry, + Registry, }; use tracing_tree::HierarchicalLayer; -pub(crate) struct Logger { - filter: EnvFilter, - file: Option, +pub(crate) struct LoggerConfig { + pub(crate) log_file: Option, + pub(crate) filter: String, + pub(crate) chalk_filter: Option, } struct MakeWriterStderr; -impl<'a> MakeWriter<'a> for MakeWriterStderr { +impl MakeWriter<'_> for MakeWriterStderr { type Writer = Stderr; - fn make_writer(&'a self) -> Self::Writer { + fn make_writer(&self) -> Self::Writer { io::stderr() } } -impl Logger { - pub(crate) fn new(file: Option, filter: Option<&str>) -> Logger { - let filter = filter.map_or(EnvFilter::default(), EnvFilter::new); +impl LoggerConfig { + pub(crate) fn init(self) -> anyhow::Result<()> { + let mut filter: Targets = self + .filter + .parse() + .with_context(|| format!("invalid log filter: `{}`", self.filter))?; - Logger { filter, file } - } + let mut chalk_layer = None; + if let Some(chalk_filter) = self.chalk_filter { + let level: LevelFilter = + chalk_filter.parse().with_context(|| "invalid chalk log filter")?; + chalk_layer = Some( + HierarchicalLayer::default() + .with_indent_lines(true) + .with_ansi(false) + .with_indent_amount(2) + .with_writer(io::stderr), + ); + filter = filter + .with_target("chalk_solve", level) + .with_target("chalk_ir", level) + .with_target("chalk_recursive", level); + }; - pub(crate) fn install(self) -> Result<()> { - // The meaning of CHALK_DEBUG I suspected is to tell chalk crates - // (i.e. chalk-solve, chalk-ir, chalk-recursive) how to filter tracing - // logs. But now we can only have just one filter, which means we have to - // merge chalk filter to our main filter (from RA_LOG env). - // - // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`. - // As the value should only affect chalk crates, we'd better manually - // specify the target. And for simplicity, CHALK_DEBUG only accept the value - // that specify level. - let chalk_level_dir = std::env::var("CHALK_DEBUG") - .map(|val| { - val.parse::().expect( - "invalid CHALK_DEBUG value, expect right log level (like debug or trace)", - ) - }) - .ok(); - - let chalk_layer = HierarchicalLayer::default() - .with_indent_lines(true) - .with_ansi(false) - .with_indent_amount(2) - .with_writer(io::stderr); - - let writer = match self.file { + let writer = match self.log_file { Some(file) => BoxMakeWriter::new(Arc::new(file)), None => BoxMakeWriter::new(io::stderr), }; let ra_fmt_layer = tracing_subscriber::fmt::layer().event_format(LoggerFormatter).with_writer(writer); - match chalk_level_dir { - Some(val) => { - Registry::default() - .with( - self.filter - .add_directive(format!("chalk_solve={val}").parse()?) - .add_directive(format!("chalk_ir={val}").parse()?) - .add_directive(format!("chalk_recursive={val}").parse()?), - ) - .with(ra_fmt_layer) - .with(chalk_layer) - .init(); - } - None => { - Registry::default().with(self.filter).with(ra_fmt_layer).init(); - } - }; - + let registry = Registry::default().with(filter).with(ra_fmt_layer); + match chalk_layer { + Some(chalk_layer) => registry.with(chalk_layer).init(), + None => registry.init(), + } Ok(()) } } diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 91911dd18096..2fa14fc7e283 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -7,14 +7,11 @@ mod logger; mod rustc_wrapper; -use std::{ - env, fs, - path::{Path, PathBuf}, - process, -}; +use std::{env, fs, path::PathBuf, process}; +use anyhow::Context; use lsp_server::Connection; -use rust_analyzer::{cli::flags, config::Config, from_json, Result}; +use rust_analyzer::{cli::flags, config::Config, from_json}; use vfs::AbsPathBuf; #[cfg(all(feature = "mimalloc"))] @@ -25,7 +22,7 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -fn main() { +fn main() -> anyhow::Result<()> { if std::env::var("RA_RUSTC_WRAPPER").is_ok() { let mut args = std::env::args_os(); let _me = args.next().unwrap(); @@ -41,14 +38,7 @@ fn main() { } let flags = flags::RustAnalyzer::from_env_or_exit(); - if let Err(err) = try_main(flags) { - tracing::error!("Unexpected error: {}", err); - eprintln!("{err}"); - process::exit(101); - } -} -fn try_main(flags: flags::RustAnalyzer) -> Result<()> { #[cfg(debug_assertions)] if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { #[allow(unused_mut)] @@ -58,14 +48,8 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { } } - let mut log_file = flags.log_file.as_deref(); + setup_logging(flags.log_file.clone())?; - let env_log_file = env::var("RA_LOG_FILE").ok(); - if let Some(env_log_file) = env_log_file.as_deref() { - log_file = Some(Path::new(env_log_file)); - } - - setup_logging(log_file)?; let verbosity = flags.verbosity(); match flags.subcommand { @@ -98,11 +82,12 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Scip(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::RunTests(cmd) => cmd.run()?, } Ok(()) } -fn setup_logging(log_file: Option<&Path>) -> Result<()> { +fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> { if cfg!(windows) { // This is required so that windows finds our pdb that is placed right beside the exe. // By default it doesn't look at the folder the exe resides in, only in the current working @@ -115,23 +100,42 @@ fn setup_logging(log_file: Option<&Path>) -> Result<()> { } } } + if env::var("RUST_BACKTRACE").is_err() { env::set_var("RUST_BACKTRACE", "short"); } + let log_file = env::var("RA_LOG_FILE").ok().map(PathBuf::from).or(log_file_flag); let log_file = match log_file { Some(path) => { if let Some(parent) = path.parent() { let _ = fs::create_dir_all(parent); } - Some(fs::File::create(path)?) + Some( + fs::File::create(&path) + .with_context(|| format!("can't create log file at {}", path.display()))?, + ) } None => None, }; - let filter = env::var("RA_LOG").ok(); - // deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually useful - // information in there for debugging - logger::Logger::new(log_file, filter.as_deref().or(Some("error"))).install()?; + + logger::LoggerConfig { + log_file, + // Deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually + // useful information in there for debugging. + filter: env::var("RA_LOG").ok().unwrap_or_else(|| "error".to_string()), + // The meaning of CHALK_DEBUG I suspected is to tell chalk crates + // (i.e. chalk-solve, chalk-ir, chalk-recursive) how to filter tracing + // logs. But now we can only have just one filter, which means we have to + // merge chalk filter to our main filter (from RA_LOG env). + // + // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`. + // As the value should only affect chalk crates, we'd better manually + // specify the target. And for simplicity, CHALK_DEBUG only accept the value + // that specify level. + chalk_filter: env::var("CHALK_DEBUG").ok(), + } + .init()?; profile::init(); @@ -146,8 +150,8 @@ const STACK_SIZE: usize = 1024 * 1024 * 8; fn with_extra_thread( thread_name: impl Into, thread_intent: stdx::thread::ThreadIntent, - f: impl FnOnce() -> Result<()> + Send + 'static, -) -> Result<()> { + f: impl FnOnce() -> anyhow::Result<()> + Send + 'static, +) -> anyhow::Result<()> { let handle = stdx::thread::Builder::new(thread_intent) .name(thread_name.into()) .stack_size(STACK_SIZE) @@ -158,7 +162,7 @@ fn with_extra_thread( Ok(()) } -fn run_server() -> Result<()> { +fn run_server() -> anyhow::Result<()> { tracing::info!("server version {} will start", rust_analyzer::version()); let (connection, io_threads) = Connection::stdio(); diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index e3520192110e..64646b33ad48 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs @@ -1,7 +1,6 @@ //! Various batch processing tasks, intended primarily for debugging. pub mod flags; -pub mod load_cargo; mod parse; mod symbols; mod highlight; @@ -10,13 +9,17 @@ mod diagnostics; mod ssr; mod lsif; mod scip; +mod run_tests; mod progress_report; use std::io::Read; use anyhow::Result; +use hir::{Module, Name}; +use hir_ty::db::HirDatabase; use ide::AnalysisHost; +use itertools::Itertools; use vfs::Vfs; #[derive(Clone, Copy)] @@ -36,7 +39,7 @@ impl Verbosity { } } -fn read_stdin() -> Result { +fn read_stdin() -> anyhow::Result { let mut buff = String::new(); std::io::stdin().read_to_string(&mut buff)?; Ok(buff) @@ -71,3 +74,14 @@ fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) { eprintln!("{remaining:>8} Remaining"); } + +fn full_name_of_item(db: &dyn HirDatabase, module: Module, name: Name) -> String { + module + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(name)) + .map(|it| it.display(db.upcast()).to_string()) + .join("::") +} diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 4cb917ce290a..8d68bf160a2d 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -15,7 +15,7 @@ use hir_def::{ hir::{ExprId, PatId}, }; use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; -use ide::{LineCol, RootDatabase}; +use ide::{Analysis, AnnotationConfig, DiagnosticsConfig, InlayHintsConfig, LineCol, RootDatabase}; use ide_db::{ base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, @@ -24,20 +24,20 @@ use ide_db::{ LineIndexDatabase, }; use itertools::Itertools; +use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; use oorandom::Rand32; use profile::{Bytes, StopWatch}; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; use rayon::prelude::*; use rustc_hash::FxHashSet; use syntax::{AstNode, SyntaxNode}; -use vfs::{AbsPathBuf, Vfs, VfsPath}; +use vfs::{AbsPathBuf, FileId, Vfs, VfsPath}; use crate::cli::{ flags::{self, OutputFormat}, - load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}, - print_memory_usage, + full_name_of_item, print_memory_usage, progress_report::ProgressReport, - report_metric, Result, Verbosity, + report_metric, Verbosity, }; /// Need to wrap Snapshot to provide `Clone` impl for `map_with` @@ -49,7 +49,7 @@ impl Clone for Snap> { } impl flags::AnalysisStats { - pub fn run(self, verbosity: Verbosity) -> Result<()> { + pub fn run(self, verbosity: Verbosity) -> anyhow::Result<()> { let mut rng = { let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64; Rand32::new(seed) @@ -95,17 +95,41 @@ impl flags::AnalysisStats { eprintln!(")"); let mut analysis_sw = self.stop_watch(); - let mut num_crates = 0; - let mut visited_modules = FxHashSet::default(); - let mut visit_queue = Vec::new(); let mut krates = Crate::all(db); if self.randomize { shuffle(&mut rng, &mut krates); } + + let mut item_tree_sw = self.stop_watch(); + let mut num_item_trees = 0; + let source_roots = + krates.iter().cloned().map(|krate| db.file_source_root(krate.root_file(db))).unique(); + for source_root_id in source_roots { + let source_root = db.source_root(source_root_id); + if !source_root.is_library || self.with_deps { + for file_id in source_root.iter() { + if let Some(p) = source_root.path_for_file(&file_id) { + if let Some((_, Some("rs"))) = p.name_and_extension() { + db.file_item_tree(file_id.into()); + num_item_trees += 1; + } + } + } + } + } + eprintln!(" item trees: {num_item_trees}"); + let item_tree_time = item_tree_sw.elapsed(); + eprintln!("{:<20} {}", "Item Tree Collection:", item_tree_time); + report_metric("item tree time", item_tree_time.time.as_millis() as u64, "ms"); + + let mut crate_def_map_sw = self.stop_watch(); + let mut num_crates = 0; + let mut visited_modules = FxHashSet::default(); + let mut visit_queue = Vec::new(); for krate in krates { let module = krate.root_module(db); - let file_id = module.definition_source(db).file_id; + let file_id = module.definition_source_file_id(db); let file_id = file_id.original_file(db); let source_root = db.file_source_root(file_id); let source_root = db.source_root(source_root); @@ -124,8 +148,10 @@ impl flags::AnalysisStats { let mut bodies = Vec::new(); let mut adts = Vec::new(); let mut consts = Vec::new(); + let mut file_ids = Vec::new(); while let Some(module) = visit_queue.pop() { if visited_modules.insert(module) { + file_ids.extend(module.as_source_file_id(db)); visit_queue.extend(module.children(db)); for decl in module.declarations(db) { @@ -171,7 +197,9 @@ impl flags::AnalysisStats { adts.len(), consts.len(), ); - eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed()); + let crate_def_map_time = crate_def_map_sw.elapsed(); + eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time); + report_metric("crate def map time", crate_def_map_time.time.as_millis() as u64, "ms"); if self.randomize { shuffle(&mut rng, &mut bodies); @@ -197,6 +225,10 @@ impl flags::AnalysisStats { self.run_const_eval(db, &consts, verbosity); } + if self.run_all_ide_things { + self.run_ide_things(host.analysis(), file_ids); + } + let total_span = analysis_sw.elapsed(); eprintln!("{:<20} {total_span}", "Total:"); report_metric("total time", total_span.time.as_millis() as u64, "ms"); @@ -242,21 +274,15 @@ impl flags::AnalysisStats { continue; } all += 1; - let Err(e) - = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into()) - else { - continue + let Err(e) = db.layout_of_adt( + hir_def::AdtId::from(a).into(), + Substitution::empty(Interner), + a.krate(db).into(), + ) else { + continue; }; if verbosity.is_spammy() { - let full_name = a - .module(db) - .path_to_root(db) - .into_iter() - .rev() - .filter_map(|it| it.name(db)) - .chain(Some(a.name(db))) - .map(|it| it.display(db).to_string()) - .join("::"); + let full_name = full_name_of_item(db, a.module(db), a.name(db)); println!("Data layout for {full_name} failed due {e:?}"); } fail += 1; @@ -278,15 +304,8 @@ impl flags::AnalysisStats { continue; }; if verbosity.is_spammy() { - let full_name = c - .module(db) - .path_to_root(db) - .into_iter() - .rev() - .filter_map(|it| it.name(db)) - .chain(c.name(db)) - .map(|it| it.display(db).to_string()) - .join("::"); + let full_name = + full_name_of_item(db, c.module(db), c.name(db).unwrap_or(Name::missing())); println!("Const eval for {full_name} failed due {e:?}"); } fail += 1; @@ -717,6 +736,83 @@ impl flags::AnalysisStats { report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms"); } + fn run_ide_things(&self, analysis: Analysis, mut file_ids: Vec) { + file_ids.sort(); + file_ids.dedup(); + let mut sw = self.stop_watch(); + + for &file_id in &file_ids { + _ = analysis.diagnostics( + &DiagnosticsConfig { + enabled: true, + proc_macros_enabled: true, + proc_attr_macros_enabled: true, + disable_experimental: false, + disabled: Default::default(), + expr_fill_default: Default::default(), + insert_use: ide_db::imports::insert_use::InsertUseConfig { + granularity: ide_db::imports::insert_use::ImportGranularity::Crate, + enforce_granularity: true, + prefix_kind: hir::PrefixKind::ByCrate, + group: true, + skip_glob_imports: true, + }, + prefer_no_std: Default::default(), + }, + ide::AssistResolveStrategy::All, + file_id, + ); + } + for &file_id in &file_ids { + _ = analysis.inlay_hints( + &InlayHintsConfig { + render_colons: false, + type_hints: true, + discriminant_hints: ide::DiscriminantHints::Always, + parameter_hints: true, + chaining_hints: true, + adjustment_hints: ide::AdjustmentHints::Always, + adjustment_hints_mode: ide::AdjustmentHintsMode::Postfix, + adjustment_hints_hide_outside_unsafe: false, + closure_return_type_hints: ide::ClosureReturnTypeHints::Always, + closure_capture_hints: true, + binding_mode_hints: true, + lifetime_elision_hints: ide::LifetimeElisionHints::Always, + param_names_for_lifetime_elision_hints: true, + hide_named_constructor_hints: false, + hide_closure_initialization_hints: false, + closure_style: hir::ClosureStyle::ImplFn, + max_length: Some(25), + closing_brace_hints_min_lines: Some(20), + }, + file_id, + None, + ); + } + for &file_id in &file_ids { + analysis + .annotations( + &AnnotationConfig { + binary_target: true, + annotate_runnables: true, + annotate_impls: true, + annotate_references: false, + annotate_method_references: false, + annotate_enum_variant_references: false, + location: ide::AnnotationLocation::AboveName, + }, + file_id, + ) + .unwrap() + .into_iter() + .for_each(|annotation| { + _ = analysis.resolve_annotation(annotation); + }); + } + let ide_time = sw.elapsed(); + eprintln!("{:<20} {} ({} files)", "IDE:", ide_time, file_ids.len()); + } + fn stop_watch(&self) -> StopWatch { StopWatch::start().memory(self.memory_usage) } diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 4306d721298d..0db5fb4740ea 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -7,11 +7,9 @@ use rustc_hash::FxHashSet; use hir::{db::HirDatabase, Crate, Module}; use ide::{AssistResolveStrategy, DiagnosticsConfig, Severity}; use ide_db::base_db::SourceDatabaseExt; +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; -use crate::cli::{ - flags, - load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}, -}; +use crate::cli::flags; impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { @@ -37,14 +35,14 @@ impl flags::Diagnostics { let mut visited_files = FxHashSet::default(); let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source(db).file_id.original_file(db); + let file_id = module.definition_source_file_id(db).original_file(db); let source_root = db.file_source_root(file_id); let source_root = db.source_root(source_root); !source_root.is_library }); for module in work { - let file_id = module.definition_source(db).file_id.original_file(db); + let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) { let crate_name = module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string(); diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 208a4e6ecd93..13b7f039bb0f 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -12,7 +12,7 @@ xflags::xflags! { /// LSP server for the Rust programming language. /// /// Subcommands and their flags do not provide any stability guarantees and may be removed or - /// changed without notice. Top-level flags that are not are marked as [Unstable] provide + /// changed without notice. Top-level flags that are not marked as [Unstable] provide /// backwards-compatibility and may be relied on. cmd rust-analyzer { /// Verbosity level, can be repeated multiple times. @@ -88,6 +88,16 @@ xflags::xflags! { optional --skip-data-layout /// Skip const evaluation optional --skip-const-eval + /// Runs several IDE features after analysis, including semantics highlighting, diagnostics + /// and annotations. This is useful for benchmarking the memory usage on a project that has + /// been worked on for a bit in a longer running session. + optional --run-all-ide-things + } + + /// Run unit tests of the project using mir interpreter + cmd run-tests { + /// Directory with Cargo.toml. + required path: PathBuf } cmd diagnostics { @@ -103,7 +113,7 @@ xflags::xflags! { } cmd ssr { - /// A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`) + /// A structured search replace rule (`$a.foo($b) ==>> bar($a, $b)`) repeated rule: SsrRule } @@ -147,6 +157,7 @@ pub enum RustAnalyzerCmd { Symbols(Symbols), Highlight(Highlight), AnalysisStats(AnalysisStats), + RunTests(RunTests), Diagnostics(Diagnostics), Ssr(Ssr), Search(Search), @@ -182,16 +193,22 @@ pub struct AnalysisStats { pub parallel: bool, pub memory_usage: bool, pub source_stats: bool, - pub skip_lowering: bool, - pub skip_inference: bool, - pub skip_mir_stats: bool, - pub skip_data_layout: bool, - pub skip_const_eval: bool, pub only: Option, pub with_deps: bool, pub no_sysroot: bool, pub disable_build_scripts: bool, pub disable_proc_macros: bool, + pub skip_lowering: bool, + pub skip_inference: bool, + pub skip_mir_stats: bool, + pub skip_data_layout: bool, + pub skip_const_eval: bool, + pub run_all_ide_things: bool, +} + +#[derive(Debug)] +pub struct RunTests { + pub path: PathBuf, } #[derive(Debug)] @@ -223,6 +240,7 @@ pub struct Lsif { #[derive(Debug)] pub struct Scip { pub path: PathBuf, + pub output: Option, } diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs deleted file mode 100644 index 4e8f99971674..000000000000 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! Loads a Cargo project into a static instance of analysis, without support -//! for incorporating changes. -use std::path::Path; - -use anyhow::{anyhow, Result}; -use crossbeam_channel::{unbounded, Receiver}; -use ide::{AnalysisHost, Change}; -use ide_db::{ - base_db::{CrateGraph, ProcMacros}, - FxHashMap, -}; -use proc_macro_api::ProcMacroServer; -use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; -use triomphe::Arc; -use vfs::{loader::Handle, AbsPath, AbsPathBuf}; - -use crate::reload::{load_proc_macro, ProjectFolders, SourceRootConfig}; - -// Note: Since this type is used by external tools that use rust-analyzer as a library -// what otherwise would be `pub(crate)` has to be `pub` here instead. -pub struct LoadCargoConfig { - pub load_out_dirs_from_check: bool, - pub with_proc_macro_server: ProcMacroServerChoice, - pub prefill_caches: bool, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ProcMacroServerChoice { - Sysroot, - Explicit(AbsPathBuf), - None, -} - -// Note: Since this function is used by external tools that use rust-analyzer as a library -// what otherwise would be `pub(crate)` has to be `pub` here instead. -pub fn load_workspace_at( - root: &Path, - cargo_config: &CargoConfig, - load_config: &LoadCargoConfig, - progress: &dyn Fn(String), -) -> Result<(AnalysisHost, vfs::Vfs, Option)> { - let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); - let root = ProjectManifest::discover_single(&root)?; - let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?; - - if load_config.load_out_dirs_from_check { - let build_scripts = workspace.run_build_scripts(cargo_config, progress)?; - workspace.set_build_scripts(build_scripts) - } - - load_workspace(workspace, &cargo_config.extra_env, load_config) -} - -// Note: Since this function is used by external tools that use rust-analyzer as a library -// what otherwise would be `pub(crate)` has to be `pub` here instead. -// -// The reason both, `load_workspace_at` and `load_workspace` are `pub` is that some of -// these tools need access to `ProjectWorkspace`, too, which `load_workspace_at` hides. -pub fn load_workspace( - ws: ProjectWorkspace, - extra_env: &FxHashMap, - load_config: &LoadCargoConfig, -) -> Result<(AnalysisHost, vfs::Vfs, Option)> { - let (sender, receiver) = unbounded(); - let mut vfs = vfs::Vfs::default(); - let mut loader = { - let loader = - vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap())); - Box::new(loader) - }; - - let proc_macro_server = match &load_config.with_proc_macro_server { - ProcMacroServerChoice::Sysroot => ws - .find_sysroot_proc_macro_srv() - .and_then(|it| ProcMacroServer::spawn(it).map_err(Into::into)), - ProcMacroServerChoice::Explicit(path) => { - ProcMacroServer::spawn(path.clone()).map_err(Into::into) - } - ProcMacroServerChoice::None => Err(anyhow!("proc macro server disabled")), - }; - - let (crate_graph, proc_macros) = ws.to_crate_graph( - &mut |path: &AbsPath| { - let contents = loader.load_sync(path); - let path = vfs::VfsPath::from(path.to_path_buf()); - vfs.set_file_contents(path.clone(), contents); - vfs.file_id(&path) - }, - extra_env, - ); - let proc_macros = { - let proc_macro_server = match &proc_macro_server { - Ok(it) => Ok(it), - Err(e) => Err(e.to_string()), - }; - proc_macros - .into_iter() - .map(|(crate_id, path)| { - ( - crate_id, - path.map_or_else( - |_| Err("proc macro crate is missing dylib".to_owned()), - |(_, path)| { - proc_macro_server.as_ref().map_err(Clone::clone).and_then( - |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]), - ) - }, - ), - ) - }) - .collect() - }; - - let project_folders = ProjectFolders::new(&[ws], &[]); - loader.set_config(vfs::loader::Config { - load: project_folders.load, - watch: vec![], - version: 0, - }); - - tracing::debug!("crate graph: {:?}", crate_graph); - let host = load_crate_graph( - crate_graph, - proc_macros, - project_folders.source_root_config, - &mut vfs, - &receiver, - ); - - if load_config.prefill_caches { - host.analysis().parallel_prime_caches(1, |_| {})?; - } - Ok((host, vfs, proc_macro_server.ok())) -} - -fn load_crate_graph( - crate_graph: CrateGraph, - proc_macros: ProcMacros, - source_root_config: SourceRootConfig, - vfs: &mut vfs::Vfs, - receiver: &Receiver, -) -> AnalysisHost { - let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok()); - let mut host = AnalysisHost::new(lru_cap); - let mut analysis_change = Change::new(); - - host.raw_database_mut().enable_proc_attr_macros(); - - // wait until Vfs has loaded all roots - for task in receiver { - match task { - vfs::loader::Message::Progress { n_done, n_total, config_version: _ } => { - if n_done == n_total { - break; - } - } - vfs::loader::Message::Loaded { files } => { - for (path, contents) in files { - vfs.set_file_contents(path.into(), contents); - } - } - } - } - let changes = vfs.take_changes(); - for file in changes { - if file.exists() { - let contents = vfs.file_contents(file.file_id); - if let Ok(text) = std::str::from_utf8(contents) { - analysis_change.change_file(file.file_id, Some(Arc::from(text))) - } - } - } - let source_roots = source_root_config.partition(vfs); - analysis_change.set_roots(source_roots); - - analysis_change.set_crate_graph(crate_graph); - analysis_change.set_proc_macros(proc_macros); - - host.apply_change(analysis_change); - host -} - -#[cfg(test)] -mod tests { - use super::*; - - use hir::Crate; - - #[test] - fn test_loading_rust_analyzer() { - let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); - let cargo_config = CargoConfig::default(); - let load_cargo_config = LoadCargoConfig { - load_out_dirs_from_check: false, - with_proc_macro_server: ProcMacroServerChoice::None, - prefill_caches: false, - }; - let (host, _vfs, _proc_macro) = - load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap(); - - let n_crates = Crate::all(host.raw_database()).len(); - // RA has quite a few crates, but the exact count doesn't matter - assert!(n_crates > 20); - } -} diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 7f5d08449671..42d180114e52 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -8,23 +8,22 @@ use ide::{ Analysis, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, }; -use ide_db::LineIndexDatabase; - -use ide_db::base_db::salsa::{self, ParallelDatabase}; -use ide_db::line_index::WideEncoding; +use ide_db::{ + base_db::salsa::{self, ParallelDatabase}, + line_index::WideEncoding, + LineIndexDatabase, +}; +use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; use lsp_types::{self, lsif}; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; use vfs::{AbsPathBuf, Vfs}; -use crate::cli::load_cargo::ProcMacroServerChoice; -use crate::cli::{ - flags, - load_cargo::{load_workspace, LoadCargoConfig}, - Result, +use crate::{ + cli::flags, + line_index::{LineEndings, LineIndex, PositionEncoding}, + to_proto, + version::version, }; -use crate::line_index::{LineEndings, LineIndex, PositionEncoding}; -use crate::to_proto; -use crate::version::version; /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap(DB); @@ -49,8 +48,8 @@ struct LsifManager<'a> { struct Id(i32); impl From for lsp_types::NumberOrString { - fn from(Id(x): Id) -> Self { - lsp_types::NumberOrString::Number(x) + fn from(Id(it): Id) -> Self { + lsp_types::NumberOrString::Number(it) } } @@ -89,8 +88,8 @@ impl LsifManager<'_> { } fn get_token_id(&mut self, id: TokenId) -> Id { - if let Some(x) = self.token_map.get(&id) { - return *x; + if let Some(it) = self.token_map.get(&id) { + return *it; } let result_set_id = self.add_vertex(lsif::Vertex::ResultSet(lsif::ResultSet { key: None })); self.token_map.insert(id, result_set_id); @@ -98,8 +97,8 @@ impl LsifManager<'_> { } fn get_package_id(&mut self, package_information: PackageInformation) -> Id { - if let Some(x) = self.package_map.get(&package_information) { - return *x; + if let Some(it) = self.package_map.get(&package_information) { + return *it; } let pi = package_information.clone(); let result_set_id = @@ -120,8 +119,8 @@ impl LsifManager<'_> { } fn get_range_id(&mut self, id: FileRange) -> Id { - if let Some(x) = self.range_map.get(&id) { - return *x; + if let Some(it) = self.range_map.get(&id) { + return *it; } let file_id = id.file_id; let doc_id = self.get_file_id(file_id); @@ -143,8 +142,8 @@ impl LsifManager<'_> { } fn get_file_id(&mut self, id: FileId) -> Id { - if let Some(x) = self.file_map.get(&id) { - return *x; + if let Some(it) = self.file_map.get(&id) { + return *it; } let path = self.vfs.file_path(id); let path = path.as_path().unwrap(); @@ -217,18 +216,18 @@ impl LsifManager<'_> { })); let mut edges = token.references.iter().fold( HashMap::<_, Vec>::new(), - |mut edges, x| { + |mut edges, it| { let entry = - edges.entry((x.range.file_id, x.is_definition)).or_insert_with(Vec::new); - entry.push((*self.range_map.get(&x.range).unwrap()).into()); + edges.entry((it.range.file_id, it.is_definition)).or_insert_with(Vec::new); + entry.push((*self.range_map.get(&it.range).unwrap()).into()); edges }, ); - for x in token.references { - if let Some(vertices) = edges.remove(&(x.range.file_id, x.is_definition)) { + for it in token.references { + if let Some(vertices) = edges.remove(&(it.range.file_id, it.is_definition)) { self.add_edge(lsif::Edge::Item(lsif::Item { - document: (*self.file_map.get(&x.range.file_id).unwrap()).into(), - property: Some(if x.is_definition { + document: (*self.file_map.get(&it.range.file_id).unwrap()).into(), + property: Some(if it.is_definition { lsif::ItemKind::Definitions } else { lsif::ItemKind::References @@ -286,7 +285,7 @@ impl LsifManager<'_> { } impl flags::Lsif { - pub fn run(self) -> Result<()> { + pub fn run(self) -> anyhow::Result<()> { eprintln!("Generating LSIF started..."); let now = Instant::now(); let mut cargo_config = CargoConfig::default(); diff --git a/crates/rust-analyzer/src/cli/run_tests.rs b/crates/rust-analyzer/src/cli/run_tests.rs new file mode 100644 index 000000000000..b63a266a57ae --- /dev/null +++ b/crates/rust-analyzer/src/cli/run_tests.rs @@ -0,0 +1,89 @@ +//! Run all tests in a project, similar to `cargo test`, but using the mir interpreter. + +use hir::{Crate, Module}; +use hir_ty::db::HirDatabase; +use ide_db::{base_db::SourceDatabaseExt, LineIndexDatabase}; +use profile::StopWatch; +use project_model::{CargoConfig, RustLibSource}; +use syntax::TextRange; + +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; + +use crate::cli::{flags, full_name_of_item, Result}; + +impl flags::RunTests { + pub fn run(self) -> Result<()> { + let mut cargo_config = CargoConfig::default(); + cargo_config.sysroot = Some(RustLibSource::Discover); + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: true, + with_proc_macro_server: ProcMacroServerChoice::Sysroot, + prefill_caches: false, + }; + let (host, _vfs, _proc_macro) = + load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; + let db = host.raw_database(); + + let tests = all_modules(db) + .into_iter() + .flat_map(|x| x.declarations(db)) + .filter_map(|x| match x { + hir::ModuleDef::Function(f) => Some(f), + _ => None, + }) + .filter(|x| x.is_test(db)); + let span_formatter = |file_id, text_range: TextRange| { + let line_col = match db.line_index(file_id).try_line_col(text_range.start()) { + None => " (unknown line col)".to_string(), + Some(x) => format!("#{}:{}", x.line + 1, x.col), + }; + let path = &db + .source_root(db.file_source_root(file_id)) + .path_for_file(&file_id) + .map(|x| x.to_string()); + let path = path.as_deref().unwrap_or(""); + format!("file://{path}{line_col}") + }; + let mut pass_count = 0; + let mut ignore_count = 0; + let mut fail_count = 0; + let mut sw_all = StopWatch::start(); + for test in tests { + let full_name = full_name_of_item(db, test.module(db), test.name(db)); + println!("test {}", full_name); + if test.is_ignore(db) { + println!("ignored"); + ignore_count += 1; + continue; + } + let mut sw_one = StopWatch::start(); + let result = test.eval(db, span_formatter); + if result.trim() == "pass" { + pass_count += 1; + } else { + fail_count += 1; + } + println!("{}", result); + eprintln!("{:<20} {}", format!("test {}", full_name), sw_one.elapsed()); + } + println!("{pass_count} passed, {fail_count} failed, {ignore_count} ignored"); + eprintln!("{:<20} {}", "All tests", sw_all.elapsed()); + Ok(()) + } +} + +fn all_modules(db: &dyn HirDatabase) -> Vec { + let mut worklist: Vec<_> = Crate::all(db) + .into_iter() + .filter(|x| x.origin(db).is_local()) + .map(|krate| krate.root_module(db)) + .collect(); + let mut modules = Vec::new(); + + while let Some(module) = worklist.pop() { + modules.push(module); + worklist.extend(module.children(db)); + } + + modules +} diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index b0b724bdfe73..4579aca30219 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -6,27 +6,23 @@ use std::{ time::Instant, }; -use crate::{ - cli::load_cargo::ProcMacroServerChoice, - line_index::{LineEndings, LineIndex, PositionEncoding}, -}; use ide::{ LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId, TokenStaticData, }; use ide_db::LineIndexDatabase; +use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; use scip::types as scip_types; use std::env; -use crate::cli::{ - flags, - load_cargo::{load_workspace, LoadCargoConfig}, - Result, +use crate::{ + cli::flags, + line_index::{LineEndings, LineIndex, PositionEncoding}, }; impl flags::Scip { - pub fn run(self) -> Result<()> { + pub fn run(self) -> anyhow::Result<()> { eprintln!("Generating SCIP start..."); let now = Instant::now(); let mut cargo_config = CargoConfig::default(); @@ -65,7 +61,7 @@ impl flags::Scip { path.normalize() .as_os_str() .to_str() - .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))? + .ok_or(anyhow::format_err!("Unable to normalize project_root path"))? ), text_document_encoding: scip_types::TextEncoding::UTF8.into(), special_fields: Default::default(), @@ -168,7 +164,7 @@ impl flags::Scip { let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip")); scip::write_message_to_file(out_path, index) - .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?; + .map_err(|err| anyhow::format_err!("Failed to write scip to file: {}", err))?; eprintln!("Generating SCIP finished {:?}", now.elapsed()); Ok(()) @@ -276,7 +272,7 @@ mod test { let change_fixture = ChangeFixture::parse(ra_fixture); host.raw_database_mut().apply_change(change_fixture.change); let (file_id, range_or_offset) = - change_fixture.file_position.expect("expected a marker ($0)"); + change_fixture.file_position.expect("expected a marker ()"); let offset = range_or_offset.expect_offset(); (host, FilePosition { file_id, offset }) } @@ -325,7 +321,7 @@ use foo::example_mod::func; fn main() { func$0(); } -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod example_mod { pub fn func() {} } @@ -338,7 +334,7 @@ pub mod example_mod { fn symbol_for_trait() { check_symbol( r#" -//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { pub fn func$0() {} @@ -353,7 +349,7 @@ pub mod module { fn symbol_for_trait_constant() { check_symbol( r#" - //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git + //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { const MY_CONST$0: u8; @@ -368,7 +364,7 @@ pub mod module { fn symbol_for_trait_type() { check_symbol( r#" - //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git + //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { type MyType$0; @@ -384,7 +380,7 @@ pub mod module { fn symbol_for_trait_impl_function() { check_symbol( r#" - //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git + //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub trait MyTrait { pub fn func() {} @@ -411,7 +407,7 @@ pub mod module { fn main() { let x = St { a$0: 2 }; } - //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git + //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub struct St { pub a: i32, } @@ -429,7 +425,7 @@ pub mod module { fn main() { func(); } - //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git + //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library pub mod module { pub fn func() { let x$0 = 2; diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index 82a769347df0..f87dcb889a48 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/crates/rust-analyzer/src/cli/ssr.rs @@ -1,16 +1,14 @@ //! Applies structured search replace rules from the command line. +use anyhow::Context; use ide_ssr::MatchFinder; +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; use project_model::{CargoConfig, RustLibSource}; -use crate::cli::{ - flags, - load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}, - Result, -}; +use crate::cli::flags; impl flags::Ssr { - pub fn run(self) -> Result<()> { + pub fn run(self) -> anyhow::Result<()> { use ide_db::base_db::SourceDatabaseExt; let mut cargo_config = CargoConfig::default(); cargo_config.sysroot = Some(RustLibSource::Discover); @@ -35,7 +33,8 @@ impl flags::Ssr { if let Some(path) = vfs.file_path(file_id).as_path() { let mut contents = db.file_text(file_id).to_string(); edit.apply(&mut contents); - std::fs::write(path, contents)?; + std::fs::write(path, contents) + .with_context(|| format!("failed to write {path}"))?; } } Ok(()) @@ -46,7 +45,7 @@ impl flags::Search { /// Searches for `patterns`, printing debug information for any nodes whose text exactly matches /// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful /// for much else. - pub fn run(self) -> Result<()> { + pub fn run(self) -> anyhow::Result<()> { use ide_db::base_db::SourceDatabaseExt; use ide_db::symbol_index::SymbolsDatabase; let cargo_config = CargoConfig::default(); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 6355c620f780..fa20c796ec27 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -1079,6 +1079,7 @@ impl Config { pub fn diagnostics(&self) -> DiagnosticsConfig { DiagnosticsConfig { + enabled: self.data.diagnostics_enable, proc_attr_macros_enabled: self.expand_proc_attr_macros(), proc_macros_enabled: self.data.procMacro_enable, disable_experimental: !self.data.diagnostics_experimental_enable, diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 4e57c6eb65a4..5e5cd9a02692 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -10,7 +10,7 @@ use crate::{ global_state::{GlobalState, GlobalStateSnapshot}, main_loop::Task, version::version, - LspError, Result, + LspError, }; /// A visitor for routing a raw JSON request to an appropriate handler function. @@ -32,13 +32,13 @@ pub(crate) struct RequestDispatcher<'a> { pub(crate) global_state: &'a mut GlobalState, } -impl<'a> RequestDispatcher<'a> { +impl RequestDispatcher<'_> { /// Dispatches the request onto the current thread, given full access to /// mutable global state. Unlike all other methods here, this one isn't /// guarded by `catch_unwind`, so, please, don't make bugs :-) pub(crate) fn on_sync_mut( &mut self, - f: fn(&mut GlobalState, R::Params) -> Result, + f: fn(&mut GlobalState, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request, @@ -63,7 +63,7 @@ impl<'a> RequestDispatcher<'a> { /// Dispatches the request onto the current thread. pub(crate) fn on_sync( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request, @@ -92,7 +92,7 @@ impl<'a> RequestDispatcher<'a> { /// without retrying it if it panics. pub(crate) fn on_no_retry( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -128,7 +128,7 @@ impl<'a> RequestDispatcher<'a> { /// Dispatches a non-latency-sensitive request onto the thread pool. pub(crate) fn on( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -141,7 +141,7 @@ impl<'a> RequestDispatcher<'a> { /// Dispatches a latency-sensitive request onto the thread pool. pub(crate) fn on_latency_sensitive( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -156,7 +156,7 @@ impl<'a> RequestDispatcher<'a> { /// We can't run this on the main thread though as we invoke rustfmt which may take arbitrary time to complete! pub(crate) fn on_fmt_thread( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -181,7 +181,7 @@ impl<'a> RequestDispatcher<'a> { fn on_with_thread_intent( &mut self, intent: ThreadIntent, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -245,7 +245,7 @@ impl<'a> RequestDispatcher<'a> { fn thread_result_to_response( id: lsp_server::RequestId, - result: thread::Result>, + result: thread::Result>, ) -> Result where R: lsp_types::request::Request, @@ -277,7 +277,7 @@ where fn result_to_response( id: lsp_server::RequestId, - result: Result, + result: anyhow::Result, ) -> Result where R: lsp_types::request::Request, @@ -289,7 +289,7 @@ where Err(e) => match e.downcast::() { Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), Err(e) => match e.downcast::() { - Ok(cancelled) => return Err(*cancelled), + Ok(cancelled) => return Err(cancelled), Err(e) => lsp_server::Response::new_err( id, lsp_server::ErrorCode::InternalError as i32, @@ -306,11 +306,11 @@ pub(crate) struct NotificationDispatcher<'a> { pub(crate) global_state: &'a mut GlobalState, } -impl<'a> NotificationDispatcher<'a> { +impl NotificationDispatcher<'_> { pub(crate) fn on_sync_mut( &mut self, - f: fn(&mut GlobalState, N::Params) -> Result<()>, - ) -> Result<&mut Self> + f: fn(&mut GlobalState, N::Params) -> anyhow::Result<()>, + ) -> anyhow::Result<&mut Self> where N: lsp_types::notification::Notification, N::Params: DeserializeOwned + Send, diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index cd74a5500d0a..c247e1bb2297 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs @@ -14,19 +14,21 @@ use crate::{ line_index::{LineIndex, PositionEncoding}, lsp_ext, lsp_utils::invalid_params_error, - Result, }; -pub(crate) fn abs_path(url: &lsp_types::Url) -> Result { - let path = url.to_file_path().map_err(|()| "url is not a file")?; +pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result { + let path = url.to_file_path().map_err(|()| anyhow::format_err!("url is not a file"))?; Ok(AbsPathBuf::try_from(path).unwrap()) } -pub(crate) fn vfs_path(url: &lsp_types::Url) -> Result { +pub(crate) fn vfs_path(url: &lsp_types::Url) -> anyhow::Result { abs_path(url).map(vfs::VfsPath::from) } -pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> Result { +pub(crate) fn offset( + line_index: &LineIndex, + position: lsp_types::Position, +) -> anyhow::Result { let line_col = match line_index.encoding { PositionEncoding::Utf8 => LineCol { line: position.line, col: position.character }, PositionEncoding::Wide(enc) => { @@ -42,7 +44,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R Ok(text_size) } -pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Result { +pub(crate) fn text_range( + line_index: &LineIndex, + range: lsp_types::Range, +) -> anyhow::Result { let start = offset(line_index, range.start)?; let end = offset(line_index, range.end)?; match end < start { @@ -51,14 +56,14 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Res } } -pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result { +pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> anyhow::Result { snap.url_to_file_id(url) } pub(crate) fn file_position( snap: &GlobalStateSnapshot, tdpp: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let file_id = file_id(snap, &tdpp.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; let offset = offset(&line_index, tdpp.position)?; @@ -69,7 +74,7 @@ pub(crate) fn file_range( snap: &GlobalStateSnapshot, text_document_identifier: lsp_types::TextDocumentIdentifier, range: lsp_types::Range, -) -> Result { +) -> anyhow::Result { file_range_uri(snap, &text_document_identifier.uri, range) } @@ -77,7 +82,7 @@ pub(crate) fn file_range_uri( snap: &GlobalStateSnapshot, document: &lsp_types::Url, range: lsp_types::Range, -) -> Result { +) -> anyhow::Result { let file_id = file_id(snap, document)?; let line_index = snap.file_line_index(file_id)?; let range = text_range(&line_index, range)?; @@ -101,7 +106,7 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option pub(crate) fn annotation( snap: &GlobalStateSnapshot, code_lens: lsp_types::CodeLens, -) -> Result> { +) -> anyhow::Result> { let data = code_lens.data.ok_or_else(|| invalid_params_error("code lens without data".to_string()))?; let resolve = from_json::("CodeLensResolveData", &data)?; diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index d5b0e3a57053..ea8a69751951 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -9,6 +9,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase}; +use load_cargo::SourceRootConfig; use lsp_types::{SemanticTokens, Url}; use nohash_hasher::IntMap; use parking_lot::{Mutex, RwLock}; @@ -27,10 +28,9 @@ use crate::{ main_loop::Task, mem_docs::MemDocs, op_queue::OpQueue, - reload::{self, SourceRootConfig}, + reload, task_pool::TaskPool, to_proto::url_from_abs_path, - Result, }; // Enforces drop order @@ -319,7 +319,7 @@ impl GlobalState { // crate see https://github.com/rust-lang/rust-analyzer/issues/13029 if let Some((path, force_crate_graph_reload)) = workspace_structure_change { self.fetch_workspaces_queue.request_op( - format!("workspace vfs file change: {}", path.display()), + format!("workspace vfs file change: {path}"), force_crate_graph_reload, ); } @@ -422,7 +422,7 @@ impl Drop for GlobalState { } impl GlobalStateSnapshot { - pub(crate) fn url_to_file_id(&self, url: &Url) -> Result { + pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result { url_to_file_id(&self.vfs.read().0, url) } @@ -481,8 +481,8 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { url_from_abs_path(path) } -pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result { +pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result { let path = from_proto::vfs_path(url)?; - let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {path}"))?; + let res = vfs.file_id(&path).ok_or_else(|| anyhow::format_err!("file not found: {path}"))?; Ok(res) } diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index ae1dc23153cd..e830e5e9a644 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -14,10 +14,10 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams, - lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, Result, + lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, }; -pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Result<()> { +pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> { let id: lsp_server::RequestId = match params.id { lsp_types::NumberOrString::Number(id) => id.into(), lsp_types::NumberOrString::String(id) => id.into(), @@ -29,7 +29,7 @@ pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Re pub(crate) fn handle_work_done_progress_cancel( state: &mut GlobalState, params: WorkDoneProgressCancelParams, -) -> Result<()> { +) -> anyhow::Result<()> { if let lsp_types::NumberOrString::String(s) = ¶ms.token { if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") { if let Ok(id) = u32::from_str_radix(id, 10) { @@ -49,7 +49,7 @@ pub(crate) fn handle_work_done_progress_cancel( pub(crate) fn handle_did_open_text_document( state: &mut GlobalState, params: DidOpenTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_did_open_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { @@ -68,7 +68,7 @@ pub(crate) fn handle_did_open_text_document( pub(crate) fn handle_did_change_text_document( state: &mut GlobalState, params: DidChangeTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_did_change_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { @@ -100,7 +100,7 @@ pub(crate) fn handle_did_change_text_document( pub(crate) fn handle_did_close_text_document( state: &mut GlobalState, params: DidCloseTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_did_close_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { @@ -120,14 +120,14 @@ pub(crate) fn handle_did_close_text_document( pub(crate) fn handle_did_save_text_document( state: &mut GlobalState, params: DidSaveTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { // Re-fetch workspaces if a workspace related file has changed if let Some(abs_path) = vfs_path.as_path() { if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) { state .fetch_workspaces_queue - .request_op(format!("DidSaveTextDocument {}", abs_path.display()), false); + .request_op(format!("DidSaveTextDocument {abs_path}"), false); } } @@ -146,7 +146,7 @@ pub(crate) fn handle_did_save_text_document( pub(crate) fn handle_did_change_configuration( state: &mut GlobalState, _params: DidChangeConfigurationParams, -) -> Result<()> { +) -> anyhow::Result<()> { // As stated in https://github.com/microsoft/language-server-protocol/issues/676, // this notification's parameters should be ignored and the actual config queried separately. state.send_request::( @@ -186,7 +186,7 @@ pub(crate) fn handle_did_change_configuration( pub(crate) fn handle_did_change_workspace_folders( state: &mut GlobalState, params: DidChangeWorkspaceFoldersParams, -) -> Result<()> { +) -> anyhow::Result<()> { let config = Arc::make_mut(&mut state.config); for workspace in params.event.removed { @@ -214,7 +214,7 @@ pub(crate) fn handle_did_change_workspace_folders( pub(crate) fn handle_did_change_watched_files( state: &mut GlobalState, params: DidChangeWatchedFilesParams, -) -> Result<()> { +) -> anyhow::Result<()> { for change in params.changes { if let Ok(path) = from_proto::abs_path(&change.uri) { state.loader.handle.invalidate(path); @@ -302,13 +302,13 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } } -pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { let _p = profile::span("handle_stop_flycheck"); state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); Ok(()) } -pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { let _p = profile::span("handle_clear_flycheck"); state.diagnostics.clear_check_all(); Ok(()) @@ -317,7 +317,7 @@ pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> Result<() pub(crate) fn handle_run_flycheck( state: &mut GlobalState, params: RunFlycheckParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_run_flycheck"); if let Some(text_document) = params.text_document { if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index a6a72552d57c..aad74b7466a2 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -8,6 +8,7 @@ use std::{ }; use anyhow::Context; + use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, @@ -20,9 +21,9 @@ use lsp_types::{ CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, CodeLens, CompletionItem, FoldingRange, FoldingRangeParams, HoverContents, InlayHint, InlayHintParams, Location, LocationLink, Position, PrepareRenameResponse, Range, RenameParams, - SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, - SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, - SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, + ResourceOp, ResourceOperationKind, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, + SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, + SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, }; use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; @@ -33,7 +34,7 @@ use vfs::{AbsPath, AbsPathBuf, VfsPath}; use crate::{ cargo_target_spec::CargoTargetSpec, - config::{RustfmtConfig, WorkspaceSymbolConfig}, + config::{Config, RustfmtConfig, WorkspaceSymbolConfig}, diff::diff, from_proto, global_state::{GlobalState, GlobalStateSnapshot}, @@ -43,10 +44,10 @@ use crate::{ FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, }, lsp_utils::{all_edits_are_disjoint, invalid_params_error}, - to_proto, LspError, Result, + to_proto, LspError, }; -pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { // FIXME: use `Arc::from_iter` when it becomes available state.proc_macro_clients = Arc::from(Vec::new()); state.proc_macro_changed = false; @@ -55,7 +56,7 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< Ok(()) } -pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { // FIXME: use `Arc::from_iter` when it becomes available state.proc_macro_clients = Arc::from(Vec::new()); state.proc_macro_changed = false; @@ -67,7 +68,7 @@ pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Resu pub(crate) fn handle_analyzer_status( snap: GlobalStateSnapshot, params: lsp_ext::AnalyzerStatusParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_analyzer_status"); let mut buf = String::new(); @@ -112,7 +113,7 @@ pub(crate) fn handle_analyzer_status( Ok(buf) } -pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result { +pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> anyhow::Result { let _p = profile::span("handle_memory_usage"); let mem = state.analysis_host.per_query_memory_usage(); @@ -125,7 +126,7 @@ pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result Result<()> { +pub(crate) fn handle_shuffle_crate_graph(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { state.analysis_host.shuffle_crate_graph(); Ok(()) } @@ -133,7 +134,7 @@ pub(crate) fn handle_shuffle_crate_graph(state: &mut GlobalState, _: ()) -> Resu pub(crate) fn handle_syntax_tree( snap: GlobalStateSnapshot, params: lsp_ext::SyntaxTreeParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_syntax_tree"); let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(id)?; @@ -145,7 +146,7 @@ pub(crate) fn handle_syntax_tree( pub(crate) fn handle_view_hir( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_hir"); let position = from_proto::file_position(&snap, params)?; let res = snap.analysis.view_hir(position)?; @@ -155,7 +156,7 @@ pub(crate) fn handle_view_hir( pub(crate) fn handle_view_mir( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_mir"); let position = from_proto::file_position(&snap, params)?; let res = snap.analysis.view_mir(position)?; @@ -165,7 +166,7 @@ pub(crate) fn handle_view_mir( pub(crate) fn handle_interpret_function( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_interpret_function"); let position = from_proto::file_position(&snap, params)?; let res = snap.analysis.interpret_function(position)?; @@ -175,7 +176,7 @@ pub(crate) fn handle_interpret_function( pub(crate) fn handle_view_file_text( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentIdentifier, -) -> Result { +) -> anyhow::Result { let file_id = from_proto::file_id(&snap, ¶ms.uri)?; Ok(snap.analysis.file_text(file_id)?.to_string()) } @@ -183,7 +184,7 @@ pub(crate) fn handle_view_file_text( pub(crate) fn handle_view_item_tree( snap: GlobalStateSnapshot, params: lsp_ext::ViewItemTreeParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_item_tree"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let res = snap.analysis.view_item_tree(file_id)?; @@ -193,16 +194,16 @@ pub(crate) fn handle_view_item_tree( pub(crate) fn handle_view_crate_graph( snap: GlobalStateSnapshot, params: ViewCrateGraphParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_crate_graph"); - let dot = snap.analysis.view_crate_graph(params.full)??; + let dot = snap.analysis.view_crate_graph(params.full)?.map_err(anyhow::Error::msg)?; Ok(dot) } pub(crate) fn handle_expand_macro( snap: GlobalStateSnapshot, params: lsp_ext::ExpandMacroParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_expand_macro"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -215,11 +216,11 @@ pub(crate) fn handle_expand_macro( pub(crate) fn handle_selection_range( snap: GlobalStateSnapshot, params: lsp_types::SelectionRangeParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_selection_range"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; - let res: Result> = params + let res: anyhow::Result> = params .positions .into_iter() .map(|position| { @@ -258,7 +259,7 @@ pub(crate) fn handle_selection_range( pub(crate) fn handle_matching_brace( snap: GlobalStateSnapshot, params: lsp_ext::MatchingBraceParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_matching_brace"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -281,7 +282,7 @@ pub(crate) fn handle_matching_brace( pub(crate) fn handle_join_lines( snap: GlobalStateSnapshot, params: lsp_ext::JoinLinesParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_join_lines"); let config = snap.config.join_lines(); @@ -306,7 +307,7 @@ pub(crate) fn handle_join_lines( pub(crate) fn handle_on_enter( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_on_enter"); let position = from_proto::file_position(&snap, params)?; let edit = match snap.analysis.on_enter(position)? { @@ -321,7 +322,7 @@ pub(crate) fn handle_on_enter( pub(crate) fn handle_on_type_formatting( snap: GlobalStateSnapshot, params: lsp_types::DocumentOnTypeFormattingParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_on_type_formatting"); let mut position = from_proto::file_position(&snap, params.text_document_position)?; let line_index = snap.file_line_index(position.file_id)?; @@ -361,7 +362,7 @@ pub(crate) fn handle_on_type_formatting( pub(crate) fn handle_document_symbol( snap: GlobalStateSnapshot, params: lsp_types::DocumentSymbolParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_document_symbol"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -450,7 +451,7 @@ pub(crate) fn handle_document_symbol( pub(crate) fn handle_workspace_symbol( snap: GlobalStateSnapshot, params: WorkspaceSymbolParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_workspace_symbol"); let config = snap.config.workspace_symbol(); @@ -513,7 +514,10 @@ pub(crate) fn handle_workspace_symbol( (all_symbols, libs) } - fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result> { + fn exec_query( + snap: &GlobalStateSnapshot, + query: Query, + ) -> anyhow::Result> { let mut res = Vec::new(); for nav in snap.analysis.symbol_search(query)? { let container_name = nav.container_name.as_ref().map(|v| v.to_string()); @@ -542,7 +546,7 @@ pub(crate) fn handle_workspace_symbol( pub(crate) fn handle_will_rename_files( snap: GlobalStateSnapshot, params: lsp_types::RenameFilesParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_will_rename_files"); let source_changes: Vec = params @@ -604,7 +608,7 @@ pub(crate) fn handle_will_rename_files( pub(crate) fn handle_goto_definition( snap: GlobalStateSnapshot, params: lsp_types::GotoDefinitionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_definition"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let nav_info = match snap.analysis.goto_definition(position)? { @@ -619,7 +623,7 @@ pub(crate) fn handle_goto_definition( pub(crate) fn handle_goto_declaration( snap: GlobalStateSnapshot, params: lsp_types::request::GotoDeclarationParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_declaration"); let position = from_proto::file_position(&snap, params.text_document_position_params.clone())?; let nav_info = match snap.analysis.goto_declaration(position)? { @@ -634,7 +638,7 @@ pub(crate) fn handle_goto_declaration( pub(crate) fn handle_goto_implementation( snap: GlobalStateSnapshot, params: lsp_types::request::GotoImplementationParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_implementation"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let nav_info = match snap.analysis.goto_implementation(position)? { @@ -649,7 +653,7 @@ pub(crate) fn handle_goto_implementation( pub(crate) fn handle_goto_type_definition( snap: GlobalStateSnapshot, params: lsp_types::request::GotoTypeDefinitionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_type_definition"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let nav_info = match snap.analysis.goto_type_definition(position)? { @@ -664,7 +668,7 @@ pub(crate) fn handle_goto_type_definition( pub(crate) fn handle_parent_module( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_parent_module"); if let Ok(file_path) = ¶ms.text_document.uri.to_file_path() { if file_path.file_name().unwrap_or_default() == "Cargo.toml" { @@ -731,7 +735,7 @@ pub(crate) fn handle_parent_module( pub(crate) fn handle_runnables( snap: GlobalStateSnapshot, params: lsp_ext::RunnablesParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_runnables"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -826,7 +830,7 @@ fn should_skip_for_offset(runnable: &Runnable, offset: Option) -> bool pub(crate) fn handle_related_tests( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_related_tests"); let position = from_proto::file_position(&snap, params)?; @@ -844,7 +848,7 @@ pub(crate) fn handle_related_tests( pub(crate) fn handle_completion( snap: GlobalStateSnapshot, params: lsp_types::CompletionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_completion"); let text_document_position = params.text_document_position.clone(); let position = from_proto::file_position(&snap, params.text_document_position)?; @@ -872,7 +876,7 @@ pub(crate) fn handle_completion( pub(crate) fn handle_completion_resolve( snap: GlobalStateSnapshot, mut original_completion: CompletionItem, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_completion_resolve"); if !all_edits_are_disjoint(&original_completion, &[]) { @@ -928,7 +932,7 @@ pub(crate) fn handle_completion_resolve( pub(crate) fn handle_folding_range( snap: GlobalStateSnapshot, params: FoldingRangeParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_folding_range"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let folds = snap.analysis.folding_ranges(file_id)?; @@ -945,7 +949,7 @@ pub(crate) fn handle_folding_range( pub(crate) fn handle_signature_help( snap: GlobalStateSnapshot, params: lsp_types::SignatureHelpParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_signature_help"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let help = match snap.analysis.signature_help(position)? { @@ -960,7 +964,7 @@ pub(crate) fn handle_signature_help( pub(crate) fn handle_hover( snap: GlobalStateSnapshot, params: lsp_ext::HoverParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_hover"); let range = match params.position { PositionOrRange::Position(position) => Range::new(position, position), @@ -997,7 +1001,7 @@ pub(crate) fn handle_hover( pub(crate) fn handle_prepare_rename( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_prepare_rename"); let position = from_proto::file_position(&snap, params)?; @@ -1011,7 +1015,7 @@ pub(crate) fn handle_prepare_rename( pub(crate) fn handle_rename( snap: GlobalStateSnapshot, params: RenameParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_rename"); let position = from_proto::file_position(&snap, params.text_document_position)?; @@ -1027,14 +1031,30 @@ pub(crate) fn handle_rename( if !change.file_system_edits.is_empty() && snap.config.will_rename() { change.source_file_edits.clear(); } + let workspace_edit = to_proto::workspace_edit(&snap, change)?; + + if let Some(lsp_types::DocumentChanges::Operations(ops)) = + workspace_edit.document_changes.as_ref() + { + for op in ops { + if let lsp_types::DocumentChangeOperation::Op(doc_change_op) = op { + if let Err(err) = + resource_ops_supported(&snap.config, resolve_resource_op(doc_change_op)) + { + return Err(err); + } + } + } + } + Ok(Some(workspace_edit)) } pub(crate) fn handle_references( snap: GlobalStateSnapshot, params: lsp_types::ReferenceParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_references"); let position = from_proto::file_position(&snap, params.text_document_position)?; @@ -1077,7 +1097,7 @@ pub(crate) fn handle_references( pub(crate) fn handle_formatting( snap: GlobalStateSnapshot, params: lsp_types::DocumentFormattingParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_formatting"); run_rustfmt(&snap, params.text_document, None) @@ -1086,7 +1106,7 @@ pub(crate) fn handle_formatting( pub(crate) fn handle_range_formatting( snap: GlobalStateSnapshot, params: lsp_types::DocumentRangeFormattingParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_range_formatting"); run_rustfmt(&snap, params.text_document, Some(params.range)) @@ -1095,7 +1115,7 @@ pub(crate) fn handle_range_formatting( pub(crate) fn handle_code_action( snap: GlobalStateSnapshot, params: lsp_types::CodeActionParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_code_action"); if !snap.config.code_action_literals() { @@ -1134,6 +1154,21 @@ pub(crate) fn handle_code_action( let resolve_data = if code_action_resolve_cap { Some((index, params.clone())) } else { None }; let code_action = to_proto::code_action(&snap, assist, resolve_data)?; + + // Check if the client supports the necessary `ResourceOperation`s. + let changes = code_action.edit.as_ref().and_then(|it| it.document_changes.as_ref()); + if let Some(changes) = changes { + for change in changes { + if let lsp_ext::SnippetDocumentChangeOperation::Op(res_op) = change { + if let Err(err) = + resource_ops_supported(&snap.config, resolve_resource_op(res_op)) + { + return Err(err); + } + } + } + } + res.push(code_action) } @@ -1158,7 +1193,7 @@ pub(crate) fn handle_code_action( pub(crate) fn handle_code_action_resolve( snap: GlobalStateSnapshot, mut code_action: lsp_ext::CodeAction, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_code_action_resolve"); let params = match code_action.data.take() { Some(it) => it, @@ -1216,10 +1251,25 @@ pub(crate) fn handle_code_action_resolve( let ca = to_proto::code_action(&snap, assist.clone(), None)?; code_action.edit = ca.edit; code_action.command = ca.command; + + if let Some(edit) = code_action.edit.as_ref() { + if let Some(changes) = edit.document_changes.as_ref() { + for change in changes { + if let lsp_ext::SnippetDocumentChangeOperation::Op(res_op) = change { + if let Err(err) = + resource_ops_supported(&snap.config, resolve_resource_op(res_op)) + { + return Err(err); + } + } + } + } + } + Ok(code_action) } -fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> { +fn parse_action_id(action_id: &str) -> anyhow::Result<(usize, SingleResolve), String> { let id_parts = action_id.split(':').collect::>(); match id_parts.as_slice() { [assist_id_string, assist_kind_string, index_string] => { @@ -1237,7 +1287,7 @@ fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> { pub(crate) fn handle_code_lens( snap: GlobalStateSnapshot, params: lsp_types::CodeLensParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_code_lens"); let lens_config = snap.config.lens(); @@ -1280,8 +1330,10 @@ pub(crate) fn handle_code_lens( pub(crate) fn handle_code_lens_resolve( snap: GlobalStateSnapshot, code_lens: CodeLens, -) -> Result { - let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { return Ok(code_lens) }; +) -> anyhow::Result { + let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { + return Ok(code_lens); + }; let annotation = snap.analysis.resolve_annotation(annotation)?; let mut acc = Vec::new(); @@ -1301,7 +1353,7 @@ pub(crate) fn handle_code_lens_resolve( pub(crate) fn handle_document_highlight( snap: GlobalStateSnapshot, params: lsp_types::DocumentHighlightParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_document_highlight"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let line_index = snap.file_line_index(position.file_id)?; @@ -1323,7 +1375,7 @@ pub(crate) fn handle_document_highlight( pub(crate) fn handle_ssr( snap: GlobalStateSnapshot, params: lsp_ext::SsrParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_ssr"); let selections = params .selections @@ -1343,7 +1395,7 @@ pub(crate) fn handle_ssr( pub(crate) fn handle_inlay_hints( snap: GlobalStateSnapshot, params: InlayHintParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_inlay_hints"); let document_uri = ¶ms.text_document.uri; let FileRange { file_id, range } = from_proto::file_range( @@ -1365,7 +1417,7 @@ pub(crate) fn handle_inlay_hints( pub(crate) fn handle_inlay_hints_resolve( _snap: GlobalStateSnapshot, hint: InlayHint, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_inlay_hints_resolve"); Ok(hint) } @@ -1373,7 +1425,7 @@ pub(crate) fn handle_inlay_hints_resolve( pub(crate) fn handle_call_hierarchy_prepare( snap: GlobalStateSnapshot, params: CallHierarchyPrepareParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_call_hierarchy_prepare"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; @@ -1395,7 +1447,7 @@ pub(crate) fn handle_call_hierarchy_prepare( pub(crate) fn handle_call_hierarchy_incoming( snap: GlobalStateSnapshot, params: CallHierarchyIncomingCallsParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_call_hierarchy_incoming"); let item = params.item; @@ -1430,7 +1482,7 @@ pub(crate) fn handle_call_hierarchy_incoming( pub(crate) fn handle_call_hierarchy_outgoing( snap: GlobalStateSnapshot, params: CallHierarchyOutgoingCallsParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_call_hierarchy_outgoing"); let item = params.item; @@ -1465,7 +1517,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( pub(crate) fn handle_semantic_tokens_full( snap: GlobalStateSnapshot, params: SemanticTokensParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_semantic_tokens_full"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; @@ -1495,7 +1547,7 @@ pub(crate) fn handle_semantic_tokens_full( pub(crate) fn handle_semantic_tokens_full_delta( snap: GlobalStateSnapshot, params: SemanticTokensDeltaParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_semantic_tokens_full_delta"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; @@ -1535,7 +1587,7 @@ pub(crate) fn handle_semantic_tokens_full_delta( pub(crate) fn handle_semantic_tokens_range( snap: GlobalStateSnapshot, params: SemanticTokensRangeParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_semantic_tokens_range"); let frange = from_proto::file_range(&snap, params.text_document, params.range)?; @@ -1561,7 +1613,7 @@ pub(crate) fn handle_semantic_tokens_range( pub(crate) fn handle_open_docs( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_open_docs"); let position = from_proto::file_position(&snap, params)?; @@ -1582,9 +1634,9 @@ pub(crate) fn handle_open_docs( let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else { return if snap.config.local_docs() { Ok(ExternalDocsResponse::WithLocal(Default::default())) - } else { + } else { Ok(ExternalDocsResponse::Simple(None)) - } + }; }; let web = remote_urls.web_url.and_then(|it| Url::parse(&it).ok()); @@ -1600,7 +1652,7 @@ pub(crate) fn handle_open_docs( pub(crate) fn handle_open_cargo_toml( snap: GlobalStateSnapshot, params: lsp_ext::OpenCargoTomlParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_open_cargo_toml"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; @@ -1618,7 +1670,7 @@ pub(crate) fn handle_open_cargo_toml( pub(crate) fn handle_move_item( snap: GlobalStateSnapshot, params: lsp_ext::MoveItemParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_move_item"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let range = from_proto::file_range(&snap, params.text_document, params.range)?; @@ -1637,6 +1689,34 @@ pub(crate) fn handle_move_item( } } +pub(crate) fn handle_view_recursive_memory_layout( + snap: GlobalStateSnapshot, + params: lsp_types::TextDocumentPositionParams, +) -> anyhow::Result> { + let _p = profile::span("view_recursive_memory_layout"); + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.file_line_index(file_id)?; + let offset = from_proto::offset(&line_index, params.position)?; + + let res = snap.analysis.get_recursive_memory_layout(FilePosition { file_id, offset })?; + Ok(res.map(|it| lsp_ext::RecursiveMemoryLayout { + nodes: it + .nodes + .iter() + .map(|n| lsp_ext::MemoryLayoutNode { + item_name: n.item_name.clone(), + typename: n.typename.clone(), + size: n.size, + offset: n.offset, + alignment: n.alignment, + parent_idx: n.parent_idx, + children_start: n.children_start, + children_len: n.children_len, + }) + .collect(), + })) +} + fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink { lsp_ext::CommandLink { tooltip: Some(tooltip), command } } @@ -1792,7 +1872,7 @@ fn run_rustfmt( snap: &GlobalStateSnapshot, text_document: TextDocumentIdentifier, range: Option, -) -> Result>> { +) -> anyhow::Result>> { let file_id = from_proto::file_id(snap, &text_document.uri)?; let file = snap.analysis.file_text(file_id)?; @@ -1942,7 +2022,7 @@ fn run_rustfmt( pub(crate) fn fetch_dependency_list( state: GlobalStateSnapshot, _params: FetchDependencyListParams, -) -> Result { +) -> anyhow::Result { let crates = state.analysis.fetch_crates()?; let crate_infos = crates .into_iter() @@ -1987,3 +2067,42 @@ fn to_url(path: VfsPath) -> Option { let str_path = path.as_os_str().to_str()?; Url::from_file_path(str_path).ok() } + +fn resource_ops_supported(config: &Config, kind: ResourceOperationKind) -> anyhow::Result<()> { + #[rustfmt::skip] + let resops = (|| { + config + .caps() + .workspace + .as_ref()? + .workspace_edit + .as_ref()? + .resource_operations + .as_ref() + })(); + + if !matches!(resops, Some(resops) if resops.contains(&kind)) { + return Err(LspError::new( + ErrorCode::RequestFailed as i32, + format!( + "Client does not support {} capability.", + match kind { + ResourceOperationKind::Create => "create", + ResourceOperationKind::Rename => "rename", + ResourceOperationKind::Delete => "delete", + } + ), + ) + .into()); + } + + Ok(()) +} + +fn resolve_resource_op(op: &ResourceOp) -> ResourceOperationKind { + match op { + ResourceOp::Create(_) => ResourceOperationKind::Create, + ResourceOp::Rename(_) => ResourceOperationKind::Rename, + ResourceOp::Delete(_) => ResourceOperationKind::Delete, + } +} diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index bd9f471a46d1..5a11012b93cf 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -20,7 +20,7 @@ use test_utils::project_root; use triomphe::Arc; use vfs::{AbsPathBuf, VfsPath}; -use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; #[test] fn integrated_highlighting_benchmark() { diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 65de4366e9f6..57e26c241bba 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -54,13 +54,12 @@ use serde::de::DeserializeOwned; pub use crate::{caps::server_capabilities, main_loop::main_loop, version::version}; -pub type Error = Box; -pub type Result = std::result::Result; - -pub fn from_json(what: &'static str, json: &serde_json::Value) -> Result { - let res = serde_json::from_value(json.clone()) - .map_err(|e| format!("Failed to deserialize {what}: {e}; {json}"))?; - Ok(res) +pub fn from_json( + what: &'static str, + json: &serde_json::Value, +) -> anyhow::Result { + serde_json::from_value(json.clone()) + .map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}")) } #[derive(Debug)] diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 4d67c8b305fe..d0989b3230dc 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -182,6 +182,33 @@ pub struct ExpandedMacro { pub expansion: String, } +pub enum ViewRecursiveMemoryLayout {} + +impl Request for ViewRecursiveMemoryLayout { + type Params = lsp_types::TextDocumentPositionParams; + type Result = Option; + const METHOD: &'static str = "rust-analyzer/viewRecursiveMemoryLayout"; +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RecursiveMemoryLayout { + pub nodes: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct MemoryLayoutNode { + pub item_name: String, + pub typename: String, + pub size: u64, + pub offset: u64, + pub alignment: u64, + pub parent_idx: i64, + pub children_start: i64, + pub children_len: u64, +} + pub enum CancelFlycheck {} impl Notification for CancelFlycheck { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 02dd94e5fa59..74036710fa30 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -23,10 +23,9 @@ use crate::{ lsp_ext, lsp_utils::{notification_is, Progress}, reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, - Result, }; -pub fn main_loop(config: Config, connection: Connection) -> Result<()> { +pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> { tracing::info!("initial config: {:#?}", config); // Windows scheduler implements priority boosts: if thread waits for an @@ -109,7 +108,7 @@ impl fmt::Debug for Event { } impl GlobalState { - fn run(mut self, inbox: Receiver) -> Result<()> { + fn run(mut self, inbox: Receiver) -> anyhow::Result<()> { self.update_status_or_notify(); if self.config.did_save_text_document_dynamic_registration() { @@ -134,7 +133,7 @@ impl GlobalState { self.handle_event(event)?; } - Err("client exited without proper shutdown sequence".into()) + anyhow::bail!("client exited without proper shutdown sequence") } fn register_did_save_capability(&mut self) { @@ -191,7 +190,7 @@ impl GlobalState { } } - fn handle_event(&mut self, event: Event) -> Result<()> { + fn handle_event(&mut self, event: Event) -> anyhow::Result<()> { let loop_start = Instant::now(); // NOTE: don't count blocking select! call as a loop-turn time let _p = profile::span("GlobalState::handle_event"); @@ -754,11 +753,12 @@ impl GlobalState { ) .on::(handlers::handle_will_rename_files) .on::(handlers::handle_ssr) + .on::(handlers::handle_view_recursive_memory_layout) .finish(); } /// Handles an incoming notification. - fn on_notification(&mut self, not: Notification) -> Result<()> { + fn on_notification(&mut self, not: Notification) -> anyhow::Result<()> { use crate::handlers::notification as handlers; use lsp_types::notification as notifs; @@ -843,11 +843,7 @@ impl GlobalState { d.code.as_str().to_string(), )), code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&format!( - "https://rust-analyzer.github.io/manual.html#{}", - d.code.as_str() - )) - .unwrap(), + href: lsp_types::Url::parse(&d.code.url()).unwrap(), }), source: Some("rust-analyzer".to_string()), message: d.message, diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 310c6b076c07..18d9151d4aab 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -12,26 +12,22 @@ //! correct. Instead, we try to provide a best-effort service. Even if the //! project is currently loading and we don't have a full project model, we //! still want to respond to various requests. -use std::{collections::hash_map::Entry, iter, mem, sync}; +use std::{iter, mem}; use flycheck::{FlycheckConfig, FlycheckHandle}; use hir::db::DefDatabase; use ide::Change; use ide_db::{ - base_db::{ - salsa::Durability, CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, - ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros, SourceRoot, VfsPath, - }, + base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, ProcMacros}, FxHashMap, }; -use itertools::Itertools; -use proc_macro_api::{MacroDylib, ProcMacroServer}; -use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts}; +use load_cargo::{load_proc_macro, ProjectFolders}; +use proc_macro_api::ProcMacroServer; +use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; use rustc_hash::FxHashSet; use stdx::{format_to, thread::ThreadIntent}; -use syntax::SmolStr; use triomphe::Arc; -use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; +use vfs::{AbsPath, ChangeKind}; use crate::{ config::{Config, FilesWatcher, LinkedProject}, @@ -41,8 +37,6 @@ use crate::{ op_queue::Cause, }; -use ::tt::token_id as tt; - #[derive(Debug)] pub(crate) enum ProjectWorkspaceProgress { Begin, @@ -307,7 +301,7 @@ impl GlobalState { res.map_or_else( |_| Err("proc macro crate is missing dylib".to_owned()), |(crate_name, path)| { - progress(path.display().to_string()); + progress(path.to_string()); client.as_ref().map_err(Clone::clone).and_then(|client| { load_proc_macro( client, @@ -340,7 +334,11 @@ impl GlobalState { let _p = profile::span("GlobalState::switch_workspaces"); tracing::info!(%cause, "will switch workspaces"); - let Some((workspaces, force_reload_crate_graph)) = self.fetch_workspaces_queue.last_op_result() else { return; }; + let Some((workspaces, force_reload_crate_graph)) = + self.fetch_workspaces_queue.last_op_result() + else { + return; + }; if let Err(_) = self.fetch_workspace_error() { if !self.workspaces.is_empty() { @@ -407,9 +405,9 @@ impl GlobalState { .flat_map(|root| { root.include.into_iter().flat_map(|it| { [ - format!("{}/**/*.rs", it.display()), - format!("{}/**/Cargo.toml", it.display()), - format!("{}/**/Cargo.lock", it.display()), + format!("{it}/**/*.rs"), + format!("{it}/**/Cargo.toml"), + format!("{it}/**/Cargo.lock"), ] }) }) @@ -447,17 +445,13 @@ impl GlobalState { None => ws.find_sysroot_proc_macro_srv()?, }; - tracing::info!("Using proc-macro server at {}", path.display(),); + tracing::info!("Using proc-macro server at {path}"); ProcMacroServer::spawn(path.clone()).map_err(|err| { tracing::error!( - "Failed to run proc-macro server from path {}, error: {:?}", - path.display(), - err + "Failed to run proc-macro server from path {path}, error: {err:?}", ); - anyhow::anyhow!( - "Failed to run proc-macro server from path {}, error: {:?}", - path.display(), - err + anyhow::format_err!( + "Failed to run proc-macro server from path {path}, error: {err:?}", ) }) }) @@ -534,7 +528,9 @@ impl GlobalState { pub(super) fn fetch_workspace_error(&self) -> Result<(), String> { let mut buf = String::new(); - let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) }; + let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else { + return Ok(()); + }; if last_op_result.is_empty() { stdx::format_to!(buf, "rust-analyzer failed to discover workspace"); } else { @@ -621,254 +617,6 @@ impl GlobalState { } } -#[derive(Default)] -pub(crate) struct ProjectFolders { - pub(crate) load: Vec, - pub(crate) watch: Vec, - pub(crate) source_root_config: SourceRootConfig, -} - -impl ProjectFolders { - pub(crate) fn new( - workspaces: &[ProjectWorkspace], - global_excludes: &[AbsPathBuf], - ) -> ProjectFolders { - let mut res = ProjectFolders::default(); - let mut fsc = FileSetConfig::builder(); - let mut local_filesets = vec![]; - - // Dedup source roots - // Depending on the project setup, we can have duplicated source roots, or for example in - // the case of the rustc workspace, we can end up with two source roots that are almost the - // same but not quite, like: - // PackageRoot { is_local: false, include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri")], exclude: [] } - // PackageRoot { - // is_local: true, - // include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri"), AbsPathBuf(".../rust/build/x86_64-pc-windows-msvc/stage0-tools/x86_64-pc-windows-msvc/release/build/cargo-miri-85801cd3d2d1dae4/out")], - // exclude: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri/.git"), AbsPathBuf(".../rust/src/tools/miri/cargo-miri/target")] - // } - // - // The first one comes from the explicit rustc workspace which points to the rustc workspace itself - // The second comes from the rustc workspace that we load as the actual project workspace - // These `is_local` differing in this kind of way gives us problems, especially when trying to filter diagnostics as we don't report diagnostics for external libraries. - // So we need to deduplicate these, usually it would be enough to deduplicate by `include`, but as the rustc example shows here that doesn't work, - // so we need to also coalesce the includes if they overlap. - - let mut roots: Vec<_> = workspaces - .iter() - .flat_map(|ws| ws.to_roots()) - .update(|root| root.include.sort()) - .sorted_by(|a, b| a.include.cmp(&b.include)) - .collect(); - - // map that tracks indices of overlapping roots - let mut overlap_map = FxHashMap::<_, Vec<_>>::default(); - let mut done = false; - - while !mem::replace(&mut done, true) { - // maps include paths to indices of the corresponding root - let mut include_to_idx = FxHashMap::default(); - // Find and note down the indices of overlapping roots - for (idx, root) in roots.iter().enumerate().filter(|(_, it)| !it.include.is_empty()) { - for include in &root.include { - match include_to_idx.entry(include) { - Entry::Occupied(e) => { - overlap_map.entry(*e.get()).or_default().push(idx); - } - Entry::Vacant(e) => { - e.insert(idx); - } - } - } - } - for (k, v) in overlap_map.drain() { - done = false; - for v in v { - let r = mem::replace( - &mut roots[v], - PackageRoot { is_local: false, include: vec![], exclude: vec![] }, - ); - roots[k].is_local |= r.is_local; - roots[k].include.extend(r.include); - roots[k].exclude.extend(r.exclude); - } - roots[k].include.sort(); - roots[k].exclude.sort(); - roots[k].include.dedup(); - roots[k].exclude.dedup(); - } - } - - for root in roots.into_iter().filter(|it| !it.include.is_empty()) { - let file_set_roots: Vec = - root.include.iter().cloned().map(VfsPath::from).collect(); - - let entry = { - let mut dirs = vfs::loader::Directories::default(); - dirs.extensions.push("rs".into()); - dirs.include.extend(root.include); - dirs.exclude.extend(root.exclude); - for excl in global_excludes { - if dirs - .include - .iter() - .any(|incl| incl.starts_with(excl) || excl.starts_with(incl)) - { - dirs.exclude.push(excl.clone()); - } - } - - vfs::loader::Entry::Directories(dirs) - }; - - if root.is_local { - res.watch.push(res.load.len()); - } - res.load.push(entry); - - if root.is_local { - local_filesets.push(fsc.len()); - } - fsc.add_file_set(file_set_roots) - } - - let fsc = fsc.build(); - res.source_root_config = SourceRootConfig { fsc, local_filesets }; - - res - } -} - -#[derive(Default, Debug)] -pub(crate) struct SourceRootConfig { - pub(crate) fsc: FileSetConfig, - pub(crate) local_filesets: Vec, -} - -impl SourceRootConfig { - pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec { - let _p = profile::span("SourceRootConfig::partition"); - self.fsc - .partition(vfs) - .into_iter() - .enumerate() - .map(|(idx, file_set)| { - let is_local = self.local_filesets.contains(&idx); - if is_local { - SourceRoot::new_local(file_set) - } else { - SourceRoot::new_library(file_set) - } - }) - .collect() - } -} - -/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace` -/// with an identity dummy expander. -pub(crate) fn load_proc_macro( - server: &ProcMacroServer, - path: &AbsPath, - dummy_replace: &[Box], -) -> ProcMacroLoadResult { - let res: Result, String> = (|| { - let dylib = MacroDylib::new(path.to_path_buf()); - let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?; - if vec.is_empty() { - return Err("proc macro library returned no proc macros".to_string()); - } - Ok(vec - .into_iter() - .map(|expander| expander_to_proc_macro(expander, dummy_replace)) - .collect()) - })(); - return match res { - Ok(proc_macros) => { - tracing::info!( - "Loaded proc-macros for {}: {:?}", - path.display(), - proc_macros.iter().map(|it| it.name.clone()).collect::>() - ); - Ok(proc_macros) - } - Err(e) => { - tracing::warn!("proc-macro loading for {} failed: {e}", path.display()); - Err(e) - } - }; - - fn expander_to_proc_macro( - expander: proc_macro_api::ProcMacro, - dummy_replace: &[Box], - ) -> ProcMacro { - let name = SmolStr::from(expander.name()); - let kind = match expander.kind() { - proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive, - proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike, - proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr, - }; - let expander: sync::Arc = - if dummy_replace.iter().any(|replace| &**replace == name) { - match kind { - ProcMacroKind::Attr => sync::Arc::new(IdentityExpander), - _ => sync::Arc::new(EmptyExpander), - } - } else { - sync::Arc::new(Expander(expander)) - }; - ProcMacro { name, kind, expander } - } - - #[derive(Debug)] - struct Expander(proc_macro_api::ProcMacro); - - impl ProcMacroExpander for Expander { - fn expand( - &self, - subtree: &tt::Subtree, - attrs: Option<&tt::Subtree>, - env: &Env, - ) -> Result { - let env = env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(); - match self.0.expand(subtree, attrs, env) { - Ok(Ok(subtree)) => Ok(subtree), - Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)), - Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), - } - } - } - - /// Dummy identity expander, used for attribute proc-macros that are deliberately ignored by the user. - #[derive(Debug)] - struct IdentityExpander; - - impl ProcMacroExpander for IdentityExpander { - fn expand( - &self, - subtree: &tt::Subtree, - _: Option<&tt::Subtree>, - _: &Env, - ) -> Result { - Ok(subtree.clone()) - } - } - - /// Empty expander, used for proc-macros that are deliberately ignored by the user. - #[derive(Debug)] - struct EmptyExpander; - - impl ProcMacroExpander for EmptyExpander { - fn expand( - &self, - _: &tt::Subtree, - _: Option<&tt::Subtree>, - _: &Env, - ) -> Result { - Ok(tt::Subtree::empty()) - } - } -} - pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool { const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"]; diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 648bc995ad5e..ba3421bf9e76 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -94,7 +94,10 @@ pub(crate) fn document_highlight_kind( pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity { match severity { Severity::Error => lsp_types::DiagnosticSeverity::ERROR, + Severity::Warning => lsp_types::DiagnosticSeverity::WARNING, Severity::WeakWarning => lsp_types::DiagnosticSeverity::HINT, + // unreachable + Severity::Allow => lsp_types::DiagnosticSeverity::INFORMATION, } } diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index b2a8041ae9b5..3c52ef5ef7f5 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -13,6 +13,7 @@ use rust_analyzer::{config::Config, lsp_ext, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; use test_utils::FixtureWithProjectMeta; +use tracing_subscriber::{prelude::*, Layer}; use vfs::AbsPathBuf; use crate::testdir::TestDir; @@ -24,7 +25,7 @@ pub(crate) struct Project<'a> { config: serde_json::Value, } -impl<'a> Project<'a> { +impl Project<'_> { pub(crate) fn with_fixture(fixture: &str) -> Project<'_> { Project { fixture, @@ -47,17 +48,17 @@ impl<'a> Project<'a> { } } - pub(crate) fn tmp_dir(mut self, tmp_dir: TestDir) -> Project<'a> { + pub(crate) fn tmp_dir(mut self, tmp_dir: TestDir) -> Self { self.tmp_dir = Some(tmp_dir); self } - pub(crate) fn root(mut self, path: &str) -> Project<'a> { + pub(crate) fn root(mut self, path: &str) -> Self { self.roots.push(path.into()); self } - pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> { + pub(crate) fn with_config(mut self, config: serde_json::Value) -> Self { fn merge(dst: &mut serde_json::Value, src: serde_json::Value) { match (dst, src) { (Value::Object(dst), Value::Object(src)) => { @@ -76,10 +77,11 @@ impl<'a> Project<'a> { let tmp_dir = self.tmp_dir.unwrap_or_else(TestDir::new); static INIT: Once = Once::new(); INIT.call_once(|| { - tracing_subscriber::fmt() - .with_test_writer() - .with_env_filter(tracing_subscriber::EnvFilter::from_env("RA_LOG")) - .init(); + let filter: tracing_subscriber::filter::Targets = + std::env::var("RA_LOG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); + let layer = + tracing_subscriber::fmt::Layer::new().with_test_writer().with_filter(filter); + tracing_subscriber::Registry::default().with(layer).init(); profile::init_from(crate::PROFILE); }); @@ -111,6 +113,14 @@ impl<'a> Project<'a> { relative_pattern_support: None, }, ), + workspace_edit: Some(lsp_types::WorkspaceEditClientCapabilities { + resource_operations: Some(vec![ + lsp_types::ResourceOperationKind::Create, + lsp_types::ResourceOperationKind::Delete, + lsp_types::ResourceOperationKind::Rename, + ]), + ..Default::default() + }), ..Default::default() }), text_document: Some(lsp_types::TextDocumentClientCapabilities { diff --git a/crates/sourcegen/src/lib.rs b/crates/sourcegen/src/lib.rs index c5da6ceb4d19..1514c6c7d4c1 100644 --- a/crates/sourcegen/src/lib.rs +++ b/crates/sourcegen/src/lib.rs @@ -61,7 +61,9 @@ impl CommentBlock { let mut blocks = CommentBlock::extract_untagged(text); blocks.retain_mut(|block| { let first = block.contents.remove(0); - let Some(id) = first.strip_prefix(&tag) else { return false; }; + let Some(id) = first.strip_prefix(&tag) else { + return false; + }; if block.is_doc { panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}"); diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index a67f36ae9006..536f000a44b7 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] libc = "0.2.135" -backtrace = { version = "0.3.65", optional = true } +backtrace = { version = "0.3.67", optional = true } always-assert = { version = "0.1.2", features = ["log"] } jod-thread = "0.1.2" crossbeam-channel = "0.5.5" diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index fb38d25ab541..5ee0c4792846 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -19,7 +19,7 @@ itertools = "0.10.5" rowan = "0.15.11" rustc-hash = "1.1.0" once_cell = "1.17.0" -indexmap = "1.9.1" +indexmap = "2.0.0" smol_str.workspace = true triomphe.workspace = true diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index b3ea6ca8d46a..606804aea25f 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -213,6 +213,28 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { } } } + + fn add_attr(&self, attr: ast::Attr) { + add_attr(self.syntax(), attr); + + fn add_attr(node: &SyntaxNode, attr: ast::Attr) { + let indent = IndentLevel::from_node(node); + attr.reindent_to(indent); + + let after_attrs_and_comments = node + .children_with_tokens() + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .map_or(Position::first_child_of(node), |it| Position::before(it)); + + ted::insert_all( + after_attrs_and_comments, + vec![ + attr.syntax().clone().into(), + make::tokens::whitespace(&format!("\n{indent}")).into(), + ], + ) + } + } } impl AttrsOwnerEdit for T {} @@ -676,12 +698,6 @@ fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken { } } -impl ast::StmtList { - pub fn push_front(&self, statement: ast::Stmt) { - ted::insert(Position::after(self.l_curly_token().unwrap()), statement.syntax()); - } -} - impl ast::VariantList { pub fn add_variant(&self, variant: ast::Variant) { let (indent, position) = match self.variants().last() { @@ -732,6 +748,27 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { Some(()) } +pub trait HasVisibilityEdit: ast::HasVisibility { + fn set_visibility(&self, visbility: ast::Visibility) { + match self.visibility() { + Some(current_visibility) => { + ted::replace(current_visibility.syntax(), visbility.syntax()) + } + None => { + let vis_before = self + .syntax() + .children_with_tokens() + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .unwrap_or_else(|| self.syntax().first_child_or_token().unwrap()); + + ted::insert(ted::Position::before(vis_before), visbility.syntax()); + } + } + } +} + +impl HasVisibilityEdit for T {} + pub trait Indent: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 3c2b7e56b065..4c6db0ef06cb 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -10,6 +10,8 @@ //! `parse(format!())` we use internally is an implementation detail -- long //! term, it will be replaced with direct tree manipulation. use itertools::Itertools; +use parser::T; +use rowan::NodeOrToken; use stdx::{format_to, never}; use crate::{ast, utils::is_raw_identifier, AstNode, SourceFile, SyntaxKind, SyntaxToken}; @@ -447,6 +449,21 @@ pub fn block_expr( ast_from_text(&format!("fn f() {buf}")) } +pub fn async_move_block_expr( + stmts: impl IntoIterator, + tail_expr: Option, +) -> ast::BlockExpr { + let mut buf = "async move {\n".to_string(); + for stmt in stmts.into_iter() { + format_to!(buf, " {stmt}\n"); + } + if let Some(tail_expr) = tail_expr { + format_to!(buf, " {tail_expr}\n"); + } + buf += "}"; + ast_from_text(&format!("const _: () = {buf};")) +} + pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr { ast_from_text(&format!("fn f() {{ {tail_expr} }}")) } @@ -848,6 +865,36 @@ pub fn param_list( ast_from_text(&list) } +pub fn trait_( + is_unsafe: bool, + ident: &str, + gen_params: Option, + where_clause: Option, + assoc_items: ast::AssocItemList, +) -> ast::Trait { + let mut text = String::new(); + + if is_unsafe { + format_to!(text, "unsafe "); + } + + format_to!(text, "trait {ident}"); + + if let Some(gen_params) = gen_params { + format_to!(text, "{} ", gen_params.to_string()); + } else { + text.push(' '); + } + + if let Some(where_clause) = where_clause { + format_to!(text, "{} ", where_clause.to_string()); + } + + format_to!(text, "{}", assoc_items.to_string()); + + ast_from_text(&text) +} + pub fn type_bound(bound: &str) -> ast::TypeBound { ast_from_text(&format!("fn f() {{ }}")) } @@ -985,6 +1032,41 @@ pub fn struct_( ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",)) } +pub fn attr_outer(meta: ast::Meta) -> ast::Attr { + ast_from_text(&format!("#[{meta}]")) +} + +pub fn attr_inner(meta: ast::Meta) -> ast::Attr { + ast_from_text(&format!("#![{meta}]")) +} + +pub fn meta_expr(path: ast::Path, expr: ast::Expr) -> ast::Meta { + ast_from_text(&format!("#[{path} = {expr}]")) +} + +pub fn meta_token_tree(path: ast::Path, tt: ast::TokenTree) -> ast::Meta { + ast_from_text(&format!("#[{path}{tt}]")) +} + +pub fn meta_path(path: ast::Path) -> ast::Meta { + ast_from_text(&format!("#[{path}]")) +} + +pub fn token_tree( + delimiter: SyntaxKind, + tt: Vec>, +) -> ast::TokenTree { + let (l_delimiter, r_delimiter) = match delimiter { + T!['('] => ('(', ')'), + T!['['] => ('[', ']'), + T!['{'] => ('{', '}'), + _ => panic!("invalid delimiter `{delimiter:?}`"), + }; + let tt = tt.into_iter().join(""); + + ast_from_text(&format!("tt!{l_delimiter}{tt}{r_delimiter}")) +} + #[track_caller] fn ast_from_text(text: &str) -> N { let parse = SourceFile::parse(text); @@ -1022,6 +1104,17 @@ pub mod tokens { ) }); + pub fn semicolon() -> SyntaxToken { + SOURCE_FILE + .tree() + .syntax() + .clone_for_update() + .descendants_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == SEMICOLON) + .unwrap() + } + pub fn single_space() -> SyntaxToken { SOURCE_FILE .tree() diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index efbf87966448..bed240a6d735 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -171,6 +171,109 @@ impl SourceFile { } } +impl ast::TokenTree { + pub fn reparse_as_expr(self) -> Parse { + let tokens = self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token); + + let mut parser_input = parser::Input::default(); + let mut was_joint = false; + for t in tokens { + let kind = t.kind(); + if kind.is_trivia() { + was_joint = false + } else { + if kind == SyntaxKind::IDENT { + let token_text = t.text(); + let contextual_kw = SyntaxKind::from_contextual_keyword(token_text) + .unwrap_or(SyntaxKind::IDENT); + parser_input.push_ident(contextual_kw); + } else { + if was_joint { + parser_input.was_joint(); + } + parser_input.push(kind); + // Tag the token as joint if it is float with a fractional part + // we use this jointness to inform the parser about what token split + // event to emit when we encounter a float literal in a field access + if kind == SyntaxKind::FLOAT_NUMBER && !t.text().ends_with('.') { + parser_input.was_joint(); + } + } + was_joint = true; + } + } + + let parser_output = parser::TopEntryPoint::Expr.parse(&parser_input); + + let mut tokens = + self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token); + let mut text = String::new(); + let mut pos = TextSize::from(0); + let mut builder = SyntaxTreeBuilder::default(); + for event in parser_output.iter() { + match event { + parser::Step::Token { kind, n_input_tokens } => { + let mut token = tokens.next().unwrap(); + while token.kind().is_trivia() { + let text = token.text(); + pos += TextSize::from(text.len() as u32); + builder.token(token.kind(), text); + + token = tokens.next().unwrap(); + } + text.push_str(token.text()); + for _ in 1..n_input_tokens { + let token = tokens.next().unwrap(); + text.push_str(token.text()); + } + + pos += TextSize::from(text.len() as u32); + builder.token(kind, &text); + text.clear(); + } + parser::Step::FloatSplit { ends_in_dot: has_pseudo_dot } => { + let token = tokens.next().unwrap(); + let text = token.text(); + + match text.split_once('.') { + Some((left, right)) => { + assert!(!left.is_empty()); + builder.start_node(SyntaxKind::NAME_REF); + builder.token(SyntaxKind::INT_NUMBER, left); + builder.finish_node(); + + // here we move the exit up, the original exit has been deleted in process + builder.finish_node(); + + builder.token(SyntaxKind::DOT, "."); + + if has_pseudo_dot { + assert!(right.is_empty(), "{left}.{right}"); + } else { + builder.start_node(SyntaxKind::NAME_REF); + builder.token(SyntaxKind::INT_NUMBER, right); + builder.finish_node(); + + // the parser creates an unbalanced start node, we are required to close it here + builder.finish_node(); + } + } + None => unreachable!(), + } + pos += TextSize::from(text.len() as u32); + } + parser::Step::Enter { kind } => builder.start_node(kind), + parser::Step::Exit => builder.finish_node(), + parser::Step::Error { msg } => builder.error(msg.to_owned(), pos), + } + } + + let (green, errors) = builder.finish_raw(); + + Parse { green, errors: Arc::new(errors), _ty: PhantomData } + } +} + /// Matches a `SyntaxNode` against an `ast` type. /// /// # Example: diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs index 602baed37070..75e7a3fec004 100644 --- a/crates/test-utils/src/fixture.rs +++ b/crates/test-utils/src/fixture.rs @@ -47,13 +47,10 @@ //! ``` //! //! Metadata allows specifying all settings and variables -//! that are available in a real rust project: -//! - crate names via `crate:cratename` -//! - dependencies via `deps:dep1,dep2` -//! - configuration settings via `cfg:dbg=false,opt_level=2` -//! - environment variables via `env:PATH=/bin,RUST_LOG=debug` +//! that are available in a real rust project. See [`Fixture`] +//! for the syntax. //! -//! Example using all available metadata: +//! Example using some available metadata: //! ``` //! " //! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo @@ -68,17 +65,74 @@ use stdx::trim_indent; #[derive(Debug, Eq, PartialEq)] pub struct Fixture { + /// Specifies the path for this file. It must start with "/". pub path: String, - pub text: String, + /// Defines a new crate and make this file its root module. + /// + /// Version and repository URL of the crate can optionally be specified; if + /// either one is specified, the other must also be specified. + /// + /// Syntax: + /// - `crate:my_awesome_lib` + /// - `crate:my_awesome_lib@0.0.1,https://example.com/repo.git` pub krate: Option, + /// Specifies dependencies of this crate. This must be used with `crate` meta. + /// + /// Syntax: `deps:hir-def,ide-assists` pub deps: Vec, + /// Limits crates in the extern prelude. The set of crate names must be a + /// subset of `deps`. This must be used with `crate` meta. + /// + /// If this is not specified, all the dependencies will be in the extern prelude. + /// + /// Syntax: `extern-prelude:hir-def,ide-assists` pub extern_prelude: Option>, - pub cfg_atoms: Vec, - pub cfg_key_values: Vec<(String, String)>, + /// Specifies configuration options to be enabled. Options may have associated + /// values. + /// + /// Syntax: `cfg:test,dbg=false,opt_level=2` + pub cfgs: Vec<(String, Option)>, + /// Specifies the edition of this crate. This must be used with `crate` meta. If + /// this is not specified, ([`base_db::input::Edition::CURRENT`]) will be used. + /// This must be used with `crate` meta. + /// + /// Syntax: `edition:2021` pub edition: Option, + /// Specifies environment variables. + /// + /// Syntax: `env:PATH=/bin,RUST_LOG=debug` pub env: FxHashMap, + /// Introduces a new [source root](base_db::input::SourceRoot). This file **and + /// the following files** will belong the new source root. This must be used + /// with `crate` meta. + /// + /// Use this if you want to test something that uses `SourceRoot::is_library()` + /// to check editability. + /// + /// Note that files before the first fixture with `new_source_root` meta will + /// belong to an implicitly defined local source root. + /// + /// Syntax: + /// - `new_source_root:library` + /// - `new_source_root:local` pub introduce_new_source_root: Option, + /// Explicitly declares this crate as a library outside current workspace. This + /// must be used with `crate` meta. + /// + /// This is implied if this file belongs to a library source root. + /// + /// Use this if you want to test something that checks if a crate is a workspace + /// member via [`CrateOrigin`](base_db::input::CrateOrigin). + /// + /// Syntax: `library` + pub library: bool, + /// Specifies LLVM data layout to be used. + /// + /// You probably don't want to manually specify this. See LLVM manual for the + /// syntax, if you must: https://llvm.org/docs/LangRef.html#data-layout pub target_data_layout: Option, + /// Actual file contents. All meta comments are stripped. + pub text: String, } pub struct MiniCore { @@ -178,23 +232,28 @@ impl FixtureWithProjectMeta { fn parse_meta_line(meta: &str) -> Fixture { assert!(meta.starts_with("//-")); let meta = meta["//-".len()..].trim(); - let components = meta.split_ascii_whitespace().collect::>(); + let mut components = meta.split_ascii_whitespace(); - let path = components[0].to_string(); + let path = components.next().expect("fixture meta must start with a path").to_string(); assert!(path.starts_with('/'), "fixture path does not start with `/`: {path:?}"); let mut krate = None; let mut deps = Vec::new(); let mut extern_prelude = None; let mut edition = None; - let mut cfg_atoms = Vec::new(); - let mut cfg_key_values = Vec::new(); + let mut cfgs = Vec::new(); let mut env = FxHashMap::default(); let mut introduce_new_source_root = None; + let mut library = false; let mut target_data_layout = Some( "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_string(), ); - for component in components[1..].iter() { + for component in components { + if component == "library" { + library = true; + continue; + } + let (key, value) = component.split_once(':').unwrap_or_else(|| panic!("invalid meta line: {meta:?}")); match key { @@ -212,8 +271,8 @@ impl FixtureWithProjectMeta { "cfg" => { for entry in value.split(',') { match entry.split_once('=') { - Some((k, v)) => cfg_key_values.push((k.to_string(), v.to_string())), - None => cfg_atoms.push(entry.to_string()), + Some((k, v)) => cfgs.push((k.to_string(), Some(v.to_string()))), + None => cfgs.push((entry.to_string(), None)), } } } @@ -243,11 +302,11 @@ impl FixtureWithProjectMeta { krate, deps, extern_prelude, - cfg_atoms, - cfg_key_values, + cfgs, edition, env, introduce_new_source_root, + library, target_data_layout, } } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 266bc2391f15..c765f42447a3 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -20,6 +20,7 @@ //! deref_mut: deref //! deref: sized //! derive: +//! discriminant: //! drop: //! eq: sized //! error: fmt @@ -36,6 +37,7 @@ //! iterator: option //! iterators: iterator, fn //! manually_drop: drop +//! non_null: //! non_zero: //! option: panic //! ord: eq, option @@ -129,6 +131,14 @@ pub mod marker { #[lang = "phantom_data"] pub struct PhantomData; // endregion:phantom_data + + // region:discriminant + #[lang = "discriminant_kind"] + pub trait DiscriminantKind { + #[lang = "discriminant_type"] + type Discriminant; + } + // endregion:discriminant } // region:default @@ -354,6 +364,11 @@ pub mod mem { pub fn size_of() -> usize; } // endregion:size_of + + // region:discriminant + use crate::marker::DiscriminantKind; + pub struct Discriminant(::Discriminant); + // endregion:discriminant } pub mod ptr { @@ -377,6 +392,19 @@ pub mod ptr { type Metadata; } // endregion:pointee + // region:non_null + #[rustc_layout_scalar_valid_range_start(1)] + #[rustc_nonnull_optimization_guaranteed] + pub struct NonNull { + pointer: *const T, + } + // region:coerce_unsized + impl crate::ops::CoerceUnsized> for NonNull where + T: crate::marker::Unsize + { + } + // endregion:coerce_unsized + // endregion:non_null } pub mod ops { @@ -1287,6 +1315,11 @@ mod macros { pub macro derive($item:item) { /* compiler built-in */ } + + #[rustc_builtin_macro] + pub macro derive_const($item:item) { + /* compiler built-in */ + } } // endregion:derive @@ -1354,24 +1387,24 @@ pub mod error { pub mod prelude { pub mod v1 { pub use crate::{ - clone::Clone, // :clone - cmp::{Eq, PartialEq}, // :eq - cmp::{Ord, PartialOrd}, // :ord - convert::AsRef, // :as_ref - convert::{From, Into}, // :from - default::Default, // :default - iter::{IntoIterator, Iterator}, // :iterator - macros::builtin::derive, // :derive - marker::Copy, // :copy - marker::Send, // :send - marker::Sized, // :sized - marker::Sync, // :sync - mem::drop, // :drop - ops::Drop, // :drop - ops::{Fn, FnMut, FnOnce}, // :fn - option::Option::{self, None, Some}, // :option - panic, // :panic - result::Result::{self, Err, Ok}, // :result + clone::Clone, // :clone + cmp::{Eq, PartialEq}, // :eq + cmp::{Ord, PartialOrd}, // :ord + convert::AsRef, // :as_ref + convert::{From, Into}, // :from + default::Default, // :default + iter::{IntoIterator, Iterator}, // :iterator + macros::builtin::{derive, derive_const}, // :derive + marker::Copy, // :copy + marker::Send, // :send + marker::Sized, // :sized + marker::Sync, // :sync + mem::drop, // :drop + ops::Drop, // :drop + ops::{Fn, FnMut, FnOnce}, // :fn + option::Option::{self, None, Some}, // :option + panic, // :panic + result::Result::{self, Err, Ok}, // :result }; } diff --git a/crates/tt/src/buffer.rs b/crates/tt/src/buffer.rs index 0615a3763dfa..cade2e9f67a2 100644 --- a/crates/tt/src/buffer.rs +++ b/crates/tt/src/buffer.rs @@ -134,7 +134,7 @@ pub enum TokenTreeRef<'a, Span> { Leaf(&'a Leaf, &'a TokenTree), } -impl<'a, Span: Clone> TokenTreeRef<'a, Span> { +impl TokenTreeRef<'_, Span> { pub fn cloned(&self) -> TokenTree { match self { TokenTreeRef::Subtree(subtree, tt) => match tt { @@ -153,13 +153,13 @@ pub struct Cursor<'a, Span> { ptr: EntryPtr, } -impl<'a, Span> PartialEq for Cursor<'a, Span> { +impl PartialEq for Cursor<'_, Span> { fn eq(&self, other: &Cursor<'_, Span>) -> bool { self.ptr == other.ptr && std::ptr::eq(self.buffer, other.buffer) } } -impl<'a, Span> Eq for Cursor<'a, Span> {} +impl Eq for Cursor<'_, Span> {} impl<'a, Span> Cursor<'a, Span> { /// Check whether it is eof diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index c2ebf03746a8..1b8d4ba42a59 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs @@ -68,6 +68,21 @@ pub mod token_id { Self::Subtree(Subtree { delimiter: Delimiter::unspecified(), token_trees: vec![] }) } } + + impl Subtree { + pub fn visit_ids(&mut self, f: &impl Fn(TokenId) -> TokenId) { + self.delimiter.open = f(self.delimiter.open); + self.delimiter.close = f(self.delimiter.close); + self.token_trees.iter_mut().for_each(|tt| match tt { + crate::TokenTree::Leaf(leaf) => match leaf { + crate::Leaf::Literal(it) => it.span = f(it.span), + crate::Leaf::Punct(it) => it.span = f(it.span), + crate::Leaf::Ident(it) => it.span = f(it.span), + }, + crate::TokenTree::Subtree(s) => s.visit_ids(f), + }) + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index 5d61a227284e..95c5142517a4 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -15,7 +15,8 @@ doctest = false tracing = "0.1.35" walkdir = "2.3.2" crossbeam-channel = "0.5.5" -notify = "5.0" +# We demand 5.1.0 as any higher version pulls in a new windows-sys dupe +notify = "=5.1.0" stdx.workspace = true vfs.workspace = true diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index 3ae3dc83ca9b..c35785cf98c4 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] rustc-hash = "1.1.0" fst = "0.4.7" -indexmap = "1.9.1" +indexmap = "2.0.0" nohash-hasher.workspace = true paths.workspace = true diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index d327f2edf144..52ada32bdfd6 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs @@ -292,8 +292,8 @@ impl From for VfsPath { impl fmt::Display for VfsPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.0 { - VfsPathRepr::PathBuf(it) => fmt::Display::fmt(&it.display(), f), - VfsPathRepr::VirtualPath(VirtualPath(it)) => fmt::Display::fmt(it, f), + VfsPathRepr::PathBuf(it) => it.fmt(f), + VfsPathRepr::VirtualPath(VirtualPath(it)) => it.fmt(f), } } } @@ -307,8 +307,8 @@ impl fmt::Debug for VfsPath { impl fmt::Debug for VfsPathRepr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self { - VfsPathRepr::PathBuf(it) => fmt::Debug::fmt(&it.display(), f), - VfsPathRepr::VirtualPath(VirtualPath(it)) => fmt::Debug::fmt(&it, f), + VfsPathRepr::PathBuf(it) => it.fmt(f), + VfsPathRepr::VirtualPath(VirtualPath(it)) => it.fmt(f), } } } diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index bc58aa7220dc..024acb877097 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/hidden_type_mismatch.rs:11:20 + | +LL | pub type Sep = impl Sized + std::fmt::Display; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, got `String` + | +note: previous use here + --> $DIR/hidden_type_mismatch.rs:37:21 + | +LL | pub type Tait = impl Copy + From> + Into>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/issues/issue-83190.rs b/tests/ui/type-alias-impl-trait/nested-rpit-with-lifetimes.rs similarity index 100% rename from tests/ui/issues/issue-83190.rs rename to tests/ui/type-alias-impl-trait/nested-rpit-with-lifetimes.rs index da931c3edaf6..11b659eec973 100644 --- a/tests/ui/issues/issue-83190.rs +++ b/tests/ui/type-alias-impl-trait/nested-rpit-with-lifetimes.rs @@ -1,7 +1,7 @@ -// check-pass - // Regression test for issue #83190, triggering an ICE in borrowck. +// check-pass + pub trait Any {} impl Any for T {} From 09434a2575cee12b241c516ad91f21d4b4f9c3fd Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 21 Jul 2023 15:42:25 +0100 Subject: [PATCH 019/105] lint/ctypes: only try normalize Now that this lint runs on any external-ABI fn-ptr, normalization won't always succeed, so use `try_normalize_erasing_regions` instead. Signed-off-by: David Wood --- compiler/rustc_lint/src/types.rs | 21 +++++++++------------ tests/ui/lint/lint-ctypes-113900.rs | 12 ++++++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 tests/ui/lint/lint-ctypes-113900.rs diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index cc8a34080040..3379479b1742 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -966,12 +966,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { let field_ty = field.ty(self.cx.tcx, args); - if field_ty.has_opaque_types() { - self.check_type_for_ffi(cache, field_ty) - } else { - let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty); - self.check_type_for_ffi(cache, field_ty) - } + let field_ty = self + .cx + .tcx + .try_normalize_erasing_regions(self.cx.param_env, field_ty) + .unwrap_or(field_ty); + self.check_type_for_ffi(cache, field_ty) } /// Checks if the given `VariantDef`'s field types are "ffi-safe". @@ -1320,7 +1320,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if let Some(ty) = self .cx .tcx - .normalize_erasing_regions(self.cx.param_env, ty) + .try_normalize_erasing_regions(self.cx.param_env, ty) + .unwrap_or(ty) .visit_with(&mut ProhibitOpaqueTypes) .break_value() { @@ -1338,16 +1339,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { is_static: bool, is_return_type: bool, ) { - // We have to check for opaque types before `normalize_erasing_regions`, - // which will replace opaque types with their underlying concrete type. if self.check_for_opaque_ty(sp, ty) { // We've already emitted an error due to an opaque type. return; } - // it is only OK to use this function because extern fns cannot have - // any generic types right now: - let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); + let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty); // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. So, first test that the top level isn't an array, and then diff --git a/tests/ui/lint/lint-ctypes-113900.rs b/tests/ui/lint/lint-ctypes-113900.rs new file mode 100644 index 000000000000..ac4ff1ae2dfd --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113900.rs @@ -0,0 +1,12 @@ +// check-pass + +// Extending `improper_ctypes` to check external-ABI fn-ptrs means that it can encounter +// projections which cannot be normalized - unsurprisingly, this shouldn't crash the compiler. + +trait Bar { + type Assoc; +} + +type Foo = extern "C" fn() -> ::Assoc; + +fn main() {} From 8d5d2fea96d666adca10c74f8bba24031c2047e7 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Fri, 21 Jul 2023 12:36:43 -0400 Subject: [PATCH 020/105] Bump compiler_builtins to 0.1.98 --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c2c3f5e628b..72f0236fd08a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,9 +641,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.95" +version = "0.1.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6866e0f3638013234db3c89ead7a14d278354338e7237257407500009012b23f" +checksum = "dfbefa16407456e5cad1ad066c84dfcb980afe4437103a0007d1c2f136130210" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index c129a753912f..c47f910dadfa 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -18,7 +18,7 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true } -compiler_builtins = { version = "0.1.95" } +compiler_builtins = { version = "0.1.98" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } From b7d91ca5b26c239c523b9f6096e8431b8571ff3d Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 22 Jul 2023 01:06:58 +0330 Subject: [PATCH 021/105] Normalize expected ty in call arguments --- crates/hir-ty/src/infer/expr.rs | 1 + crates/hir-ty/src/tests/traits.rs | 44 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 4b14345aa39f..72e6443beb77 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1665,6 +1665,7 @@ impl InferenceContext<'_> { // the parameter to coerce to the expected type (for example in // `coerce_unsize_expected_type_4`). let param_ty = self.normalize_associated_types_in(param_ty); + let expected_ty = self.normalize_associated_types_in(expected_ty); let expected = Expectation::rvalue_hint(self, expected_ty); // infer with the expected type we have... let ty = self.infer_expr_inner(arg, &expected); diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 5f5cd794512c..542df8b3468f 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4434,3 +4434,47 @@ fn test(v: S) { "#, ); } + +#[test] +fn associated_type_in_argument() { + check( + r#" + trait A { + fn m(&self) -> i32; + } + + fn x(k: &::Ty) { + k.m(); + } + + struct X; + struct Y; + + impl A for X { + fn m(&self) -> i32 { + 8 + } + } + + impl A for Y { + fn m(&self) -> i32 { + 32 + } + } + + trait B { + type Ty: A; + } + + impl B for u16 { + type Ty = X; + } + + fn ttt() { + let inp = Y; + x::(&inp); + //^^^^ expected &X, got &Y + } + "#, + ); +} From 302ad2175d938a5307dd34779e20aada691fd28a Mon Sep 17 00:00:00 2001 From: Alex Zepeda Date: Fri, 21 Jul 2023 17:55:49 -0700 Subject: [PATCH 022/105] rust-installer: Use env(1) in the shebang. This fixes the case (e.g. *BSD) where bash is installed on the host system, but not at the typical location of /bin. --- src/tools/rust-installer/install-template.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-installer/install-template.sh b/src/tools/rust-installer/install-template.sh index 6415644e0451..b477c3eac350 100644 --- a/src/tools/rust-installer/install-template.sh +++ b/src/tools/rust-installer/install-template.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # No undefined variables set -u From a3426170598e2b6a351c6c32dc7be2e5ba00799c Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 21 Jul 2023 19:58:43 +0200 Subject: [PATCH 023/105] improve debuggability --- compiler/rustc_metadata/src/rmeta/encoder.rs | 9 ++++----- src/librustdoc/passes/collect_intra_doc_links.rs | 10 ++++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index ac86110f2bdb..8f065dd6b580 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2244,13 +2244,12 @@ pub fn provide(providers: &mut Providers) { tcx.resolutions(()) .doc_link_resolutions .get(&def_id) - .expect("no resolutions for a doc link") + .unwrap_or_else(|| span_bug!(tcx.def_span(def_id), "no resolutions for a doc link")) }, doc_link_traits_in_scope: |tcx, def_id| { - tcx.resolutions(()) - .doc_link_traits_in_scope - .get(&def_id) - .expect("no traits in scope for a doc link") + tcx.resolutions(()).doc_link_traits_in_scope.get(&def_id).unwrap_or_else(|| { + span_bug!(tcx.def_span(def_id), "no traits in scope for a doc link") + }) }, traits: |tcx, LocalCrate| { let mut traits = Vec::new(); diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 91f3729bfa24..4de30e4ed9d2 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -14,7 +14,7 @@ use rustc_hir::def::{DefKind, Namespace, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::Mutability; use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution}; use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics}; use rustc_session::lint::Lint; @@ -402,7 +402,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // `doc_link_resolutions` is missing a `path_str`, that means that there are valid links // that are being missed. To fix the ICE, change // `rustc_resolve::rustdoc::attrs_to_preprocessed_links` to cache the link. - .unwrap_or_else(|| panic!("no resolution for {:?} {:?} {:?}", path_str, ns, module_id)) + .unwrap_or_else(|| { + span_bug!( + self.cx.tcx.def_span(item_id), + "no resolution for {path_str:?} {ns:?} {module_id:?}", + ) + }) .and_then(|res| res.try_into().ok()) .or_else(|| resolve_primitive(path_str, ns)); debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); @@ -963,6 +968,7 @@ fn preprocessed_markdown_links(s: &str) -> Vec { } impl LinkCollector<'_, '_> { + #[instrument(level = "debug", skip_all)] fn resolve_links(&mut self, item: &Item) { if !self.cx.render_options.document_private && let Some(def_id) = item.item_id.as_def_id() From 9ebd8095fad4ab1bc23717139402e6b3a834020a Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 21 Jul 2023 21:18:37 +0200 Subject: [PATCH 024/105] fix doc links on `use` items --- compiler/rustc_resolve/src/late.rs | 13 +++++++++++-- tests/rustdoc/intra-doc/nested-use.rs | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/rustdoc/intra-doc/nested-use.rs diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 05128a51016a..d74ada3250d1 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -549,6 +549,7 @@ enum MaybeExported<'a> { Ok(NodeId), Impl(Option), ImplItem(Result), + NestedUse(&'a Visibility), } impl MaybeExported<'_> { @@ -559,7 +560,9 @@ impl MaybeExported<'_> { trait_def_id.as_local() } MaybeExported::Impl(None) => return true, - MaybeExported::ImplItem(Err(vis)) => return vis.kind.is_pub(), + MaybeExported::ImplItem(Err(vis)) | MaybeExported::NestedUse(vis) => { + return vis.kind.is_pub(); + } }; def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id)) } @@ -2284,7 +2287,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_item(&mut self, item: &'ast Item) { let mod_inner_docs = matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs); - if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..)) { + if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..) | ItemKind::Use(..)) { self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); } @@ -2428,6 +2431,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } ItemKind::Use(ref use_tree) => { + let maybe_exported = match use_tree.kind { + UseTreeKind::Simple(_) | UseTreeKind::Glob => MaybeExported::Ok(item.id), + UseTreeKind::Nested(_) => MaybeExported::NestedUse(&item.vis), + }; + self.resolve_doc_links(&item.attrs, maybe_exported); + self.future_proof_import(use_tree); } diff --git a/tests/rustdoc/intra-doc/nested-use.rs b/tests/rustdoc/intra-doc/nested-use.rs new file mode 100644 index 000000000000..19ebfff1bce0 --- /dev/null +++ b/tests/rustdoc/intra-doc/nested-use.rs @@ -0,0 +1,16 @@ +// Regression test for issue #113896: Intra-doc links on nested use items. + +#![crate_name = "foo"] + +// @has foo/struct.Foo.html +// @has - '//a[@href="struct.Foo.html"]' 'Foo' +// @has - '//a[@href="struct.Bar.html"]' 'Bar' + +/// [`Foo`] +pub use m::{Foo, Bar}; + +mod m { + /// [`Bar`] + pub struct Foo; + pub struct Bar; +} From 5924043b868e31623f50a86d6eb41d10ac8e5173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 22 Jul 2023 12:20:17 +0200 Subject: [PATCH 025/105] rustdoc: handle cross-crate RPITITs correctly --- compiler/rustc_middle/src/query/mod.rs | 4 +-- .../rustc_trait_selection/src/traits/mod.rs | 9 +++-- src/librustdoc/clean/blanket_impl.rs | 3 +- src/librustdoc/clean/inline.rs | 2 ++ src/librustdoc/html/render/mod.rs | 4 +-- .../auxiliary/ret-pos-impl-trait-in-trait.rs | 35 +++++++++++++++++++ .../ret-pos-impl-trait-in-trait.rs | 35 +++++++++++++++++++ 7 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs create mode 100644 tests/rustdoc/inline_cross/ret-pos-impl-trait-in-trait.rs diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b5b00b7b640f..4347c0de4c2b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2081,9 +2081,9 @@ rustc_queries! { } } - query is_impossible_method(key: (DefId, DefId)) -> bool { + query is_impossible_associated_item(key: (DefId, DefId)) -> bool { desc { |tcx| - "checking if `{}` is impossible to call within `{}`", + "checking if `{}` is impossible to reference within `{}`", tcx.def_path_str(key.1), tcx.def_path_str(key.0), } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 1c1459991841..6bab87e5de54 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -474,11 +474,14 @@ fn subst_and_check_impossible_predicates<'tcx>( result } -/// Checks whether a trait's method is impossible to call on a given impl. +/// Checks whether a trait's associated item is impossible to reference on a given impl. /// /// This only considers predicates that reference the impl's generics, and not /// those that reference the method's generics. -fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefId, DefId)) -> bool { +fn is_impossible_associated_item( + tcx: TyCtxt<'_>, + (impl_def_id, trait_item_def_id): (DefId, DefId), +) -> bool { struct ReferencesOnlyParentGenerics<'tcx> { tcx: TyCtxt<'tcx>, generics: &'tcx ty::Generics, @@ -556,7 +559,7 @@ pub fn provide(providers: &mut Providers) { specializes: specialize::specializes, subst_and_check_impossible_predicates, check_tys_might_be_eq: misc::check_tys_might_be_eq, - is_impossible_method, + is_impossible_associated_item, ..*providers }; } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index f5e02940c82e..9d744237b577 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -121,7 +121,8 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .tcx .associated_items(impl_def_id) .in_definition_order() - .map(|x| clean_middle_assoc_item(x, cx)) + .filter(|item| !item.is_impl_trait_in_trait()) + .map(|item| clean_middle_assoc_item(item, cx)) .collect::>(), polarity: ty::ImplPolarity::Positive, kind: ImplKind::Blanket(Box::new(clean_middle_ty( diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index cfd9875e1c8f..89eaf561e6a5 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -216,6 +216,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean .tcx .associated_items(did) .in_definition_order() + .filter(|item| !item.is_impl_trait_in_trait()) .map(|item| clean_middle_assoc_item(item, cx)) .collect(); @@ -459,6 +460,7 @@ pub(crate) fn build_impl( None => ( tcx.associated_items(did) .in_definition_order() + .filter(|item| !item.is_impl_trait_in_trait()) .filter(|item| { // If this is a trait impl, filter out associated items whose corresponding item // in the associated trait is marked `doc(hidden)`. diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 4d029407afa4..2fcf61d00490 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1678,11 +1678,11 @@ fn render_impl( rendering_params: ImplRenderingParameters, ) { for trait_item in &t.items { - // Skip over any default trait items that are impossible to call + // Skip over any default trait items that are impossible to reference // (e.g. if it has a `Self: Sized` bound on an unsized type). if let Some(impl_def_id) = parent.item_id.as_def_id() && let Some(trait_item_def_id) = trait_item.item_id.as_def_id() - && cx.tcx().is_impossible_method((impl_def_id, trait_item_def_id)) + && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id)) { continue; } diff --git a/tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs b/tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs new file mode 100644 index 000000000000..c72f011152d8 --- /dev/null +++ b/tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs @@ -0,0 +1,35 @@ +#![feature(return_position_impl_trait_in_trait)] + +pub trait Trait { + fn create() -> impl Iterator { + std::iter::empty() + } +} + +pub struct Basic; +pub struct Intermediate; +pub struct Advanced; + +impl Trait for Basic { + // method provided by the trait +} + +impl Trait for Intermediate { + fn create() -> std::ops::Range { // concrete return type + 0..1 + } +} + +impl Trait for Advanced { + fn create() -> impl Iterator { // opaque return type + std::iter::repeat(0) + } +} + +// Regression test for issue #113929: + +pub trait Def { + fn def() -> impl Default {} +} + +impl Def for () {} diff --git a/tests/rustdoc/inline_cross/ret-pos-impl-trait-in-trait.rs b/tests/rustdoc/inline_cross/ret-pos-impl-trait-in-trait.rs new file mode 100644 index 000000000000..8e9ef9020153 --- /dev/null +++ b/tests/rustdoc/inline_cross/ret-pos-impl-trait-in-trait.rs @@ -0,0 +1,35 @@ +#![crate_name = "user"] +// aux-crate:rpitit=ret-pos-impl-trait-in-trait.rs +// edition:2021 + +// Test that we can correctly render cross-crate RPITITs. +// In particular, check that we don't render the internal associated type generated by +// their desugaring. We count the number of associated items and ensure that it is exactly one. +// This is more robust than checking for the absence of the associated type. + +// @has user/trait.Trait.html +// @has - '//*[@id="method.create"]' 'fn create() -> impl Iterator' +// The class "method" is used for all three kinds of associated items at the time of writing. +// @count - '//*[@id="main-content"]//section[@class="method"]' 1 +pub use rpitit::Trait; + +// @has user/struct.Basic.html +// @has - '//*[@id="method.create"]' 'fn create() -> impl Iterator' +// @count - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]' 1 +pub use rpitit::Basic; + +// @has user/struct.Intermediate.html +// @has - '//*[@id="method.create"]' 'fn create() -> Range' +// @count - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]' 1 +pub use rpitit::Intermediate; + +// @has user/struct.Advanced.html +// @has - '//*[@id="method.create"]' 'fn create() -> impl Iterator' +// @count - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]' 1 +pub use rpitit::Advanced; + +// Regression test for issue #113929: + +// @has user/trait.Def.html +// @has - '//*[@id="method.def"]' 'fn def() -> impl Default' +pub use rpitit::Def; From bb98f3ad4d67db68292724d767a4fd41386c8d20 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 21 Jul 2023 23:10:07 +0200 Subject: [PATCH 026/105] fix doc links on `extern crate` items --- src/librustdoc/clean/mod.rs | 15 ++++++--------- src/librustdoc/clean/utils.rs | 12 ++++++++---- src/librustdoc/html/format.rs | 10 +++++++++- tests/rustdoc/issue-33178.rs | 9 +++++---- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d14953f1bb78..440874df14cf 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2643,15 +2643,12 @@ fn clean_extern_crate<'tcx>( } } - // FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason - vec![Item { - name: Some(name), - attrs: Box::new(Attributes::from_ast(attrs)), - item_id: crate_def_id.into(), - kind: Box::new(ExternCrateItem { src: orig_name }), - cfg: attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), - inline_stmt_id: Some(krate_owner_def_id), - }] + vec![Item::from_def_id_and_parts( + krate_owner_def_id, + Some(name), + ExternCrateItem { src: orig_name }, + cx, + )] } fn clean_use_statement<'tcx>( diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index df9da9e7c7fd..5c8db3b87744 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -38,11 +38,15 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { for it in &module.items { // `compiler_builtins` should be masked too, but we can't apply // `#[doc(masked)]` to the injected `extern crate` because it's unstable. - if it.is_extern_crate() - && (it.attrs.has_doc_flag(sym::masked) - || cx.tcx.is_compiler_builtins(it.item_id.krate())) - { + if cx.tcx.is_compiler_builtins(it.item_id.krate()) { cx.cache.masked_crates.insert(it.item_id.krate()); + } else if it.is_extern_crate() + && it.attrs.has_doc_flag(sym::masked) + && let Some(def_id) = it.item_id.as_def_id() + && let Some(local_def_id) = def_id.as_local() + && let Some(cnum) = cx.tcx.extern_mod_stmt_cnum(local_def_id) + { + cx.cache.masked_crates.insert(cnum); } } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 1099c68b004c..f60f40267d63 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -18,7 +18,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty; use rustc_middle::ty::TyCtxt; @@ -662,6 +662,14 @@ pub(crate) fn href_with_root_path( // documented on their parent's page tcx.parent(did) } + DefKind::ExternCrate => { + // Link to the crate itself, not the `extern crate` item. + if let Some(local_did) = did.as_local() { + tcx.extern_mod_stmt_cnum(local_did).unwrap_or(LOCAL_CRATE).as_def_id() + } else { + did + } + } _ => did, }; let cache = cx.cache(); diff --git a/tests/rustdoc/issue-33178.rs b/tests/rustdoc/issue-33178.rs index 1f45fe723918..ed643f5ae118 100644 --- a/tests/rustdoc/issue-33178.rs +++ b/tests/rustdoc/issue-33178.rs @@ -4,10 +4,11 @@ // ignore-cross-compile // @has issue_33178/index.html -// @has - //a/@title empty -// @has - //a/@href ../empty/index.html +// @has - '//a[@title="mod empty"][@href="../empty/index.html"]' empty pub extern crate empty; -// @has - //a/@title variant_struct -// @has - //a/@href ../variant_struct/index.html +// @has - '//a[@title="mod variant_struct"][@href="../variant_struct/index.html"]' variant_struct pub extern crate variant_struct as foo; + +// @has - '//a[@title="mod issue_33178"][@href="index.html"]' self +pub extern crate self as bar; From 237ed1630f56dfdbd332ccfa1402208cf3e0825c Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 21 Jul 2023 23:10:12 +0200 Subject: [PATCH 027/105] add tests for broken links in unused doc strings --- .../broken-link-in-unused-doc-string.rs | 16 +++++++++++ .../broken-link-in-unused-doc-string.stderr | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.rs create mode 100644 tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.stderr diff --git a/tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.rs b/tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.rs new file mode 100644 index 000000000000..f3f044f61058 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.rs @@ -0,0 +1,16 @@ +// Test that we don't ICE with broken links that don't show up in the docs. + +// check-pass +// edition: 2021 + +/// [1] +//~^ WARN unresolved link to `1` +//~| WARN unresolved link to `1` +pub use {std, core}; + +/// [2] +pub use {}; + +/// [3] +//~^ WARN unresolved link to `3` +pub extern crate alloc; diff --git a/tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.stderr b/tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.stderr new file mode 100644 index 000000000000..83e0d3962ca1 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/broken-link-in-unused-doc-string.stderr @@ -0,0 +1,27 @@ +warning: unresolved link to `3` + --> $DIR/broken-link-in-unused-doc-string.rs:14:6 + | +LL | /// [3] + | ^ no item named `3` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default + +warning: unresolved link to `1` + --> $DIR/broken-link-in-unused-doc-string.rs:6:6 + | +LL | /// [1] + | ^ no item named `1` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: unresolved link to `1` + --> $DIR/broken-link-in-unused-doc-string.rs:6:6 + | +LL | /// [1] + | ^ no item named `1` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: 3 warnings emitted + From 65823b0c744be7e45005a3a98a488814ca53e622 Mon Sep 17 00:00:00 2001 From: 1Kinoti Date: Sat, 22 Jul 2023 17:16:51 +0300 Subject: [PATCH 028/105] limit change_visibility assist to applicable items --- .../src/handlers/change_visibility.rs | 75 +++++++++++++++++-- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/crates/ide-assists/src/handlers/change_visibility.rs b/crates/ide-assists/src/handlers/change_visibility.rs index 2b1d8f6f0132..e6179ab8b1ba 100644 --- a/crates/ide-assists/src/handlers/change_visibility.rs +++ b/crates/ide-assists/src/handlers/change_visibility.rs @@ -2,9 +2,10 @@ use syntax::{ ast::{self, HasName, HasVisibility}, AstNode, SyntaxKind::{ - CONST, ENUM, FN, MACRO_DEF, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, USE, VISIBILITY, + self, ASSOC_ITEM_LIST, CONST, ENUM, FN, MACRO_DEF, MODULE, SOURCE_FILE, STATIC, STRUCT, + TRAIT, TYPE_ALIAS, USE, VISIBILITY, }, - T, + SyntaxNode, T, }; use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; @@ -46,13 +47,11 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let (offset, target) = if let Some(keyword) = item_keyword { let parent = keyword.parent()?; - let def_kws = - vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT, USE, MACRO_DEF]; - // Parent is not a definition, can't add visibility - if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { + + if !can_add(&parent) { return None; } - // Already have visibility, do nothing + // Already has visibility, do nothing if parent.children().any(|child| child.kind() == VISIBILITY) { return None; } @@ -86,6 +85,29 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { ) } +fn can_add(node: &SyntaxNode) -> bool { + const LEGAL: &[SyntaxKind] = + &[CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT, USE, MACRO_DEF]; + + LEGAL.contains(&node.kind()) && { + let Some(p) = node.parent() else { + return false; + }; + + if p.kind() == ASSOC_ITEM_LIST { + p.parent() + .and_then(|it| ast::Impl::cast(it)) + // inherent impls i.e 'non-trait impls' have a non-local + // effect, thus can have visibility even when nested. + // so filter them out + .filter(|imp| imp.for_token().is_none()) + .is_some() + } else { + matches!(p.kind(), SOURCE_FILE | MODULE) + } + } +} + fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { if vis.syntax().text() == "pub" { let target = vis.syntax().text_range(); @@ -129,6 +151,16 @@ mod tests { check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}"); check_assist(change_visibility, "$0macro foo() {}", "pub(crate) macro foo() {}"); check_assist(change_visibility, "$0use foo;", "pub(crate) use foo;"); + check_assist( + change_visibility, + "impl Foo { f$0n foo() {} }", + "impl Foo { pub(crate) fn foo() {} }", + ); + check_assist( + change_visibility, + "fn bar() { impl Foo { f$0n foo() {} } }", + "fn bar() { impl Foo { pub(crate) fn foo() {} } }", + ); } #[test] @@ -213,4 +245,33 @@ mod tests { check_assist_target(change_visibility, "pub(crate)$0 fn foo() {}", "pub(crate)"); check_assist_target(change_visibility, "struct S { $0field: u32 }", "field"); } + + #[test] + fn not_applicable_for_items_within_traits() { + check_assist_not_applicable(change_visibility, "trait Foo { f$0n run() {} }"); + check_assist_not_applicable(change_visibility, "trait Foo { con$0st FOO: u8 = 69; }"); + check_assist_not_applicable(change_visibility, "impl Foo for Bar { f$0n quox() {} }"); + } + + #[test] + fn not_applicable_for_items_within_fns() { + check_assist_not_applicable(change_visibility, "fn foo() { f$0n inner() {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { unsafe f$0n inner() {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { const f$0n inner() {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { con$0st FOO: u8 = 69; }"); + check_assist_not_applicable(change_visibility, "fn foo() { en$0um Foo {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { stru$0ct Foo {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { mo$0d foo {} }"); + check_assist_not_applicable(change_visibility, "fn foo() { $0use foo; }"); + check_assist_not_applicable(change_visibility, "fn foo() { $0type Foo = Bar; }"); + check_assist_not_applicable(change_visibility, "fn foo() { tr$0ait Foo {} }"); + check_assist_not_applicable( + change_visibility, + "fn foo() { impl Trait for Bar { f$0n bar() {} } }", + ); + check_assist_not_applicable( + change_visibility, + "fn foo() { impl Trait for Bar { con$0st FOO: u8 = 69; } }", + ); + } } From c0c2d39668be6d0633dbcb7fa19bd54ed7122587 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 11 Jul 2023 15:37:09 +0000 Subject: [PATCH 029/105] Don't call a type uncallable if its signature has errors in it --- compiler/rustc_hir_typeck/src/callee.rs | 8 ++++++++ tests/ui/typeck/apit-with-error-type-in-sig.rs | 8 ++++++++ tests/ui/typeck/apit-with-error-type-in-sig.stderr | 9 +++++++++ 3 files changed, 25 insertions(+) create mode 100644 tests/ui/typeck/apit-with-error-type-in-sig.rs create mode 100644 tests/ui/typeck/apit-with-error-type-in-sig.stderr diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index a24d1ff077f3..a1fd09d4050f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -581,6 +581,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_ty: Ty<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], ) -> ErrorGuaranteed { + // Callee probe fails when APIT references errors, so suppress those + // errors here. + if let Some((_, _, args)) = self.extract_callable_info(callee_ty) + && let Err(err) = args.error_reported() + { + return err; + } + let mut unit_variant = None; if let hir::ExprKind::Path(qpath) = &callee_expr.kind && let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _) diff --git a/tests/ui/typeck/apit-with-error-type-in-sig.rs b/tests/ui/typeck/apit-with-error-type-in-sig.rs new file mode 100644 index 000000000000..35990fc16c89 --- /dev/null +++ b/tests/ui/typeck/apit-with-error-type-in-sig.rs @@ -0,0 +1,8 @@ +type Foo = Bar; +//~^ ERROR cannot find type `Bar` in this scope + +fn check(f: impl FnOnce(Foo), val: Foo) { + f(val); +} + +fn main() {} diff --git a/tests/ui/typeck/apit-with-error-type-in-sig.stderr b/tests/ui/typeck/apit-with-error-type-in-sig.stderr new file mode 100644 index 000000000000..49b2eac1b65e --- /dev/null +++ b/tests/ui/typeck/apit-with-error-type-in-sig.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Bar` in this scope + --> $DIR/apit-with-error-type-in-sig.rs:1:12 + | +LL | type Foo = Bar; + | ^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. From 65e52bb26c0b1aff874ee5e9de22856a4d23a780 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 14 Jul 2023 08:02:23 -0400 Subject: [PATCH 030/105] Add #[inline] to core debug assertion helpers --- library/core/src/intrinsics.rs | 3 +++ .../mem_replace.mem_replace.PreCodegen.after.mir | 10 ++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index deef68e4699c..d042aaf3084d 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2524,12 +2524,14 @@ pub(crate) use assert_unsafe_precondition; /// Checks whether `ptr` is properly aligned with respect to /// `align_of::()`. +#[inline] pub(crate) fn is_aligned_and_not_null(ptr: *const T) -> bool { !ptr.is_null() && ptr.is_aligned() } /// Checks whether an allocation of `len` instances of `T` exceeds /// the maximum allowed allocation size. +#[inline] pub(crate) fn is_valid_allocation_size(len: usize) -> bool { let max_len = const { let size = crate::mem::size_of::(); @@ -2540,6 +2542,7 @@ pub(crate) fn is_valid_allocation_size(len: usize) -> bool { /// Checks whether the regions of memory starting at `src` and `dst` of size /// `count * size_of::()` do *not* overlap. +#[inline] pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) -> bool { let src_usize = src.addr(); let dst_usize = dst.addr(); diff --git a/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.mir index eeb2e3a1b03f..26919dd98dd2 100644 --- a/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.mir @@ -10,22 +10,16 @@ fn mem_replace(_1: &mut u32, _2: u32) -> u32 { scope 2 { scope 3 { debug result => _0; - scope 7 (inlined std::ptr::write::) { + scope 6 (inlined std::ptr::write::) { debug dst => _1; debug src => _2; - scope 8 { - scope 9 (inlined std::ptr::write::runtime::) { - debug dst => _1; - } + scope 7 { } } } scope 4 (inlined std::ptr::read::) { debug src => _1; scope 5 { - scope 6 (inlined std::ptr::read::runtime::) { - debug src => _1; - } } } } From 5cca093fa856aafb0d6608d0fedbc40b731d855e Mon Sep 17 00:00:00 2001 From: Tetsuharu Ohzeki Date: Sat, 15 Jul 2023 00:03:40 +0900 Subject: [PATCH 031/105] editor/code: Break down CI steps to know what is failing easily To do this change, we reorganize npm-script. | previous | after | |--------------------|----------------------------------------| | `npm run lint` | `npm run lint && npm run format:check` | | `npm run fix` | `npm run lint:fix && npm run format` | The previous `npm run fix` sometimes does not complete fix automatically because ESLint's autofix doees not follow prettier's formatting. So we need to run `npm run lint:fix && npm run format` by this order. --- .github/workflows/ci.yaml | 15 +++++++++++---- editors/code/package.json | 9 ++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 31bb7eed8d73..9f246098e767 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -161,10 +161,21 @@ jobs: # if: runner.os == 'Linux' # working-directory: ./editors/code + # If this steps fails, your code's type integrity might be wrong at some places at TypeScript level. + - run: npm run typecheck + working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' + + # You may fix the code automatically by running `npm run lint:fix` if this steps fails. - run: npm run lint working-directory: ./editors/code if: needs.changes.outputs.typescript == 'true' + # To fix this steps, please run `npm run format`. + - run: npm run format:check + working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' + - name: Run VS Code tests (Linux) if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true' env: @@ -179,10 +190,6 @@ jobs: run: npm test working-directory: ./editors/code - - run: npm run pretest - working-directory: ./editors/code - if: needs.changes.outputs.typescript == 'true' - - run: npm run package --scripts-prepend-node-path working-directory: ./editors/code if: needs.changes.outputs.typescript == 'true' diff --git a/editors/code/package.json b/editors/code/package.json index ffb5dd9079ad..31c483beb63d 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -35,9 +35,12 @@ "build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16", "build": "npm run build-base -- --sourcemap", "watch": "npm run build-base -- --sourcemap --watch", - "lint": "prettier --check . && eslint -c .eslintrc.js --ext ts ./src ./tests", - "fix": "prettier --write . && eslint -c .eslintrc.js --ext ts ./src ./tests --fix", - "pretest": "tsc && npm run build", + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint": "eslint -c .eslintrc.js --ext ts ./src ./tests", + "lint:fix": "npm run lint -- --fix", + "typecheck": "tsc", + "pretest": "npm run typecheck && npm run build", "test": "node ./out/tests/runTests.js" }, "dependencies": { From d46804c62a3c9c5e1ce680093c2e73f61a249229 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 23 Jul 2023 00:53:39 +0800 Subject: [PATCH 032/105] Fix test panics for submodule of book is not updated --- src/bootstrap/doc.rs | 18 ++++++++++++++---- src/bootstrap/lib.rs | 6 +----- src/bootstrap/util.rs | 4 ++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index e58f736d67f5..d4807aff4d69 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -16,7 +16,7 @@ use crate::cache::{Interned, INTERNER}; use crate::compile; use crate::config::{Config, TargetSelection}; use crate::tool::{self, prepare_tool_cargo, SourceType, Tool}; -use crate::util::{symlink_dir, t, up_to_date}; +use crate::util::{dir_is_empty, symlink_dir, t, up_to_date}; use crate::Mode; macro_rules! submodule_helper { @@ -197,11 +197,21 @@ impl Step for TheBook { let compiler = self.compiler; let target = self.target; + let absolute_path = builder.src.join(&relative_path); + let redirect_path = absolute_path.join("redirects"); + if !absolute_path.exists() + || !redirect_path.exists() + || dir_is_empty(&absolute_path) + || dir_is_empty(&redirect_path) + { + eprintln!("Please checkout submodule: {}", relative_path.display()); + crate::exit!(1); + } // build book builder.ensure(RustbookSrc { target, name: INTERNER.intern_str("book"), - src: INTERNER.intern_path(builder.src.join(&relative_path)), + src: INTERNER.intern_path(absolute_path.clone()), parent: Some(self), }); @@ -210,7 +220,7 @@ impl Step for TheBook { builder.ensure(RustbookSrc { target, name: INTERNER.intern_string(format!("book/{}", edition)), - src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)), + src: INTERNER.intern_path(absolute_path.join(edition)), // There should only be one book that is marked as the parent for each target, so // treat the other editions as not having a parent. parent: Option::::None, @@ -225,7 +235,7 @@ impl Step for TheBook { // build the redirect pages let _guard = builder.msg_doc(compiler, "book redirect pages", target); - for file in t!(fs::read_dir(builder.src.join(&relative_path).join("redirects"))) { + for file in t!(fs::read_dir(redirect_path)) { let file = t!(file); let path = file.path(); let path = path.to_str().unwrap(); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 059eb9ffc7bc..3dd4d8d6617b 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -36,7 +36,7 @@ use once_cell::sync::OnceCell; use crate::builder::Kind; use crate::config::{LlvmLibunwind, TargetSelection}; use crate::util::{ - exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed, + dir_is_empty, exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed, }; mod bolt; @@ -535,10 +535,6 @@ impl Build { /// /// `relative_path` should be relative to the root of the git repository, not an absolute path. pub(crate) fn update_submodule(&self, relative_path: &Path) { - fn dir_is_empty(dir: &Path) -> bool { - t!(std::fs::read_dir(dir)).next().is_none() - } - if !self.config.submodules(&self.rust_info()) { return; } diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index 2725813aba3a..84ed29854647 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -493,3 +493,7 @@ pub fn lld_flag_no_threads(is_windows: bool) -> &'static str { }); if is_windows { windows } else { other } } + +pub fn dir_is_empty(dir: &Path) -> bool { + t!(std::fs::read_dir(dir)).next().is_none() +} From 51b35ccb1b134d511cd29ea4a06c8d1abaaffabb Mon Sep 17 00:00:00 2001 From: oxalica Date: Sun, 23 Jul 2023 04:24:35 +0800 Subject: [PATCH 033/105] Add comments for why skip highlighting for invalid char/byte literals --- crates/ide/src/syntax_highlighting/escape.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/ide/src/syntax_highlighting/escape.rs b/crates/ide/src/syntax_highlighting/escape.rs index 41b27683f6c9..5913ca5e454c 100644 --- a/crates/ide/src/syntax_highlighting/escape.rs +++ b/crates/ide/src/syntax_highlighting/escape.rs @@ -26,6 +26,9 @@ pub(super) fn highlight_escape_string( pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) { if char.value().is_none() { + // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad + // state and this token contains junks, since `'` is not a reliable delimiter (consider + // lifetimes). Nonetheless, parser errors should already be emitted. return; } @@ -46,6 +49,7 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start: TextSize) { if byte.value().is_none() { + // See `highlight_escape_char` for why no error highlighting here. return; } From df9bd80d74b72aacf336d4f1a4e44ddaff2757ba Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 16 Jul 2023 18:59:05 +0000 Subject: [PATCH 034/105] reimplement C string literals --- compiler/rustc_lexer/src/cursor.rs | 4 ++ compiler/rustc_lexer/src/lib.rs | 7 ++++ compiler/rustc_parse/src/lexer/mod.rs | 26 +++++++++++- .../rfcs/rfc-3348-c-string-literals/basic.rs | 3 +- .../rfc-3348-c-string-literals/basic.stderr | 25 ------------ .../rfc-3348-c-string-literals/gate.stderr | 31 +++++--------- .../rfc-3348-c-string-literals/no-nuls.rs | Bin 760 -> 623 bytes .../rfc-3348-c-string-literals/no-nuls.stderr | Bin 4477 -> 674 bytes .../rfc-3348-c-string-literals/non-ascii.rs | 3 +- .../non-ascii.stderr | 38 ------------------ 10 files changed, 48 insertions(+), 89 deletions(-) delete mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/basic.stderr delete mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.stderr diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index eceef59802eb..aba7f95487e9 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -24,6 +24,10 @@ impl<'a> Cursor<'a> { } } + pub fn as_str(&self) -> &'a str { + self.chars.as_str() + } + /// Returns the last eaten symbol (or `'\0'` in release builds). /// (For debug assertions only.) pub(crate) fn prev(&self) -> char { diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 29335a8c0f4c..d511d2b1280d 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -367,6 +367,13 @@ impl Cursor<'_> { Some(|terminated| Byte { terminated }), ), + // c-string literal, raw c-string literal or identifier. + 'c' => self.c_or_byte_string( + |terminated| CStr { terminated }, + |n_hashes| RawCStr { n_hashes }, + None, + ), + // Identifier (this should be checked after other variant that can // start as identifier). c if is_id_start(c) => self.ident_or_unknown_prefix(), diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index c6e6b46e4551..cfcc2ec42fac 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -9,8 +9,8 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::contains_text_flow_control_chars; use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey}; use rustc_lexer::unescape::{self, EscapeError, Mode}; -use rustc_lexer::Cursor; use rustc_lexer::{Base, DocStyle, RawStrError}; +use rustc_lexer::{Cursor, LiteralKind}; use rustc_session::lint::builtin::{ RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, }; @@ -118,6 +118,7 @@ impl<'a> StringReader<'a> { let mut swallow_next_invalid = 0; // Skip trivial (whitespace & comments) tokens loop { + let str_before = self.cursor.as_str(); let token = self.cursor.advance_token(); let start = self.pos; self.pos = self.pos + BytePos(token.len); @@ -203,6 +204,29 @@ impl<'a> StringReader<'a> { .push(span); token::Ident(sym, false) } + // split up (raw) c string literals to an ident and a string literal when edition < 2021. + rustc_lexer::TokenKind::Literal { + kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }), + suffix_start: _, + } if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => { + let prefix_len = match kind { + LiteralKind::CStr { .. } => 1, + LiteralKind::RawCStr { .. } => 2, + _ => unreachable!(), + }; + + // reset the state so that only the prefix ("c" or "cr") + // was consumed. + let lit_start = start + BytePos(prefix_len); + self.pos = lit_start; + self.cursor = Cursor::new(&str_before[prefix_len as usize..]); + + self.report_unknown_prefix(start); + let sym = nfc_normalize(self.str_from(start)); + let prefix_span = self.mk_sp(start, lit_start); + self.sess.symbol_gallery.insert(sym, prefix_span); + return (Token::new(token::Ident(sym, false), prefix_span), preceded_by_whitespace); + } rustc_lexer::TokenKind::Literal { kind, suffix_start } => { let suffix_start = start + BytePos(suffix_start); let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind); diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs index 3fc5fd481ea6..5037396000bf 100644 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs @@ -1,5 +1,4 @@ -// FIXME(c_str_literals): This should be `run-pass` -// known-bug: #113333 +// run-pass // edition: 2021 #![feature(c_str_literals)] diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/basic.stderr b/tests/ui/rfcs/rfc-3348-c-string-literals/basic.stderr deleted file mode 100644 index 571c319d8c53..000000000000 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/basic.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: prefix `c` is unknown - --> $DIR/basic.rs:8:27 - | -LL | assert_eq!(b"test\0", c"test".to_bytes_with_nul()); - | ^ unknown prefix - | - = note: prefixed identifiers and literals are reserved since Rust 2021 -help: consider inserting whitespace here - | -LL | assert_eq!(b"test\0", c "test".to_bytes_with_nul()); - | + - -error: no rules expected the token `"test"` - --> $DIR/basic.rs:8:28 - | -LL | assert_eq!(b"test\0", c"test".to_bytes_with_nul()); - | -^^^^^ - | | - | no rules expected this token in macro call - | help: missing comma here - | - = note: while trying to match sequence start - -error: aborting due to 2 previous errors - diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr index 8de36ca4a6ed..ea666e433083 100644 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr @@ -1,32 +1,21 @@ -error: prefix `c` is unknown +error[E0658]: `c".."` literals are experimental --> $DIR/gate.rs:10:5 | LL | c"foo"; - | ^ unknown prefix + | ^^^^^^ | - = note: prefixed identifiers and literals are reserved since Rust 2021 -help: consider inserting whitespace here - | -LL | c "foo"; - | + + = note: see issue #105723 for more information + = help: add `#![feature(c_str_literals)]` to the crate attributes to enable -error: prefix `c` is unknown +error[E0658]: `c".."` literals are experimental --> $DIR/gate.rs:13:8 | LL | m!(c"test"); - | ^ unknown prefix + | ^^^^^^^ | - = note: prefixed identifiers and literals are reserved since Rust 2021 -help: consider inserting whitespace here - | -LL | m!(c "test"); - | + + = note: see issue #105723 for more information + = help: add `#![feature(c_str_literals)]` to the crate attributes to enable -error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `"foo"` - --> $DIR/gate.rs:10:6 - | -LL | c"foo"; - | ^^^^^ expected one of 8 possible tokens - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs index 96945f125da71a9097f6f26c75a328f097448568..369173e23184e4537fc5fb34dbe716a984667c07 100644 GIT binary patch delta 208 zcmeyt`krN?m=9-NoPujmkbjWEWNSugO@*AylGLKaoMJ961qB6#WTlwWY6F8>C2KHO nUmr!a3WjP1>G<`$GxLZeedMMI^e z#Hyr3Q%h69)=r_i)|#u9i%U5=IW1ko&Cw^=RWlZp21-(kOR%K@w0KK~yFFP6DSVP4 megeld*mgw?a5~b2BqW3oBn_YoGbo{pfXzk|1lgepay;g#a8@WTDay=CSIEgMNi9muDON}#|q zFHtBiEhxw@DoIV@Qc%#%h$~-%_-I^Dz-ASGUrlIP^jVZ@lmKz0D)ws z7y~71upo%5pr8;J$HhepyDhQVU0Q8mP>XJVTpX?3U}%KJ5e#Vd)5iOTrdX_wsW89@ veVX~*0@DFSN;o~An3P{s0!m~lrKt)f`3j~A1x2Z4nfax~3gFaH%*6!&$AQVP literal 4477 zcmYc-D#|aiQYa`&P0OrMNJvgl$ShVU&CAZqFVEvrP|(%2Q&4g74ARfb*Uc-fn=o^10`#)Ac(6F2QvX`E?5Rc*ec}Zm!!h1OHEP8Oi9fv z$xO>kEh<(>%u7+o$t+1NO3W!%NGwWKC`v6(EhyV&d)2(Oi3+L$jmEFEh@>(OIIk*$Sg@ME=WvHRmey!N(EVo>`VnTcPeOeagh-K zh6dO|ptRb+pmvCa0)|`34GCi`A;B;-0s+ODKyxX!P$(jzL@>0( z42B{llmLz!tYt!x0;)q5umu7UVPJ$M4A6?3!5RcqDi)1Li$!P+X>5p@A5c@-;7tlB zxt7elU~GiN|0AzhG{#apjcM3^ zauFXF7Z<01?i>q+g8bs*%%q%Dg_8X2)VyMNw;Ww1jT~-dX+>?9Cnn`X#w1coLB3Kj UGzSfalx60Z7At_q9E!QP0I=mZZ2$lO diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs index 066505c23dfc..380445d7a7fb 100644 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs @@ -1,5 +1,4 @@ -// FIXME(c_str_literals): This should be `run-pass` -// known-bug: #113333 +// run-pass // edition: 2021 #![feature(c_str_literals)] diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.stderr b/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.stderr deleted file mode 100644 index 47361fb61d27..000000000000 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: prefix `c` is unknown - --> $DIR/non-ascii.rs:9:9 - | -LL | c"\xEF\x80🦀\u{1F980}".to_bytes_with_nul(), - | ^ unknown prefix - | - = note: prefixed identifiers and literals are reserved since Rust 2021 -help: consider inserting whitespace here - | -LL | c "\xEF\x80🦀\u{1F980}".to_bytes_with_nul(), - | + - -error: out of range hex escape - --> $DIR/non-ascii.rs:9:11 - | -LL | c"\xEF\x80🦀\u{1F980}".to_bytes_with_nul(), - | ^^^^ must be a character in the range [\x00-\x7f] - -error: out of range hex escape - --> $DIR/non-ascii.rs:9:15 - | -LL | c"\xEF\x80🦀\u{1F980}".to_bytes_with_nul(), - | ^^^^ must be a character in the range [\x00-\x7f] - -error: no rules expected the token `"\xEF\x80🦀\u{1F980}"` - --> $DIR/non-ascii.rs:9:10 - | -LL | c"\xEF\x80🦀\u{1F980}".to_bytes_with_nul(), - | -^^^^^^^^^^^^^^^^^^^^ - | | - | no rules expected this token in macro call - | help: missing comma here - | -note: while trying to match `,` - --> $SRC_DIR/core/src/macros/mod.rs:LL:COL - -error: aborting due to 4 previous errors - From abde841f0abfced6be0a51d9de6d172f7b993081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 23 Jul 2023 09:31:34 +0200 Subject: [PATCH 035/105] remove redundant clones --- compiler/rustc_codegen_ssa/src/errors.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- compiler/rustc_metadata/src/fs.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index c72d37be7481..dbe1d6e8ba89 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -353,7 +353,7 @@ impl IntoDiagnostic<'_> for LinkingFailed<'_> { let contains_undefined_ref = self.escaped_output.contains("undefined reference to"); - diag.note(format!("{:?}", self.command)).note(self.escaped_output.to_string()); + diag.note(format!("{:?}", self.command)).note(self.escaped_output); // Trying to match an error from OS linkers // which by now we have no way to translate. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 94f64a1ffdb6..fa1056e724a1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1069,7 +1069,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if let ExprKind::MethodCall(..) = rcvr.kind { err.span_note( sp, - modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.", + modifies_rcvr_note + ", it is not meant to be used in method chains.", ); } else { err.span_note(sp, modifies_rcvr_note); diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 238f963ed463..69a77e82f987 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -104,8 +104,8 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { }; // Load metadata back to memory: codegen may need to include it in object files. - let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir) - .unwrap_or_else(|err| { + let metadata = + EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| { tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err }); }); From 7a7708904bf69c8f85a41efea1db4cd5ebc6fecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 23 Jul 2023 10:12:40 +0200 Subject: [PATCH 036/105] match on chars instead of &strs for .split() or .strip_prefix() --- compiler/rustc_hir_typeck/src/upvar.rs | 2 +- compiler/rustc_middle/src/traits/solve/inspect/format.rs | 4 ++-- compiler/rustc_session/src/config.rs | 4 ++-- compiler/rustc_session/src/options.rs | 2 +- library/test/src/formatters/junit.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index fb81a8395d79..22fe823acb76 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -932,7 +932,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { var_hir_id: hir::HirId, closure_clause: hir::CaptureBy, ) -> Option>> { - let auto_traits_def_id = vec![ + let auto_traits_def_id = [ self.tcx.lang_items().clone_trait(), self.tcx.lang_items().sync_trait(), self.tcx.get_diagnostic_item(sym::Send), diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index f19f1189e444..d0d2080b6a16 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -15,11 +15,11 @@ struct Indentor<'a, 'b> { impl Write for Indentor<'_, '_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { - for line in s.split_inclusive("\n") { + for line in s.split_inclusive('\n') { if self.on_newline { self.f.write_str(" ")?; } - self.on_newline = line.ends_with("\n"); + self.on_newline = line.ends_with('\n'); self.f.write_str(line)?; } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a8147ede970c..36b5c385ab19 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -279,11 +279,11 @@ impl LinkSelfContained { // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been // set in bulk with its historical values, then manually setting a component clears that // `explicitly_set` state. - if let Some(component_to_enable) = component.strip_prefix("+") { + if let Some(component_to_enable) = component.strip_prefix('+') { self.explicitly_set = None; self.components.insert(component_to_enable.parse()?); Ok(()) - } else if let Some(component_to_disable) = component.strip_prefix("-") { + } else if let Some(component_to_disable) = component.strip_prefix('-') { self.explicitly_set = None; self.components.remove(component_to_disable.parse()?); Ok(()) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 39efe9abeecd..ff433fdf16dc 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1145,7 +1145,7 @@ mod parse { } // 2. Parse a list of enabled and disabled components. - for comp in s.split(",") { + for comp in s.split(',') { if slot.handle_cli_component(comp).is_err() { return false; } diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 9f5bf24367ea..a211ebf1ded1 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -32,7 +32,7 @@ fn str_to_cdata(s: &str) -> String { let escaped_output = s.replace("]]>", "]]]]>"); let escaped_output = escaped_output.replace(" ", ""); format!("", escaped_output) From adf759bf6a15e0f4782866dd38e071d60cfbb50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 23 Jul 2023 10:50:14 +0200 Subject: [PATCH 037/105] fix couple of clippy findings: filter_map_identity iter_kv_map needless_question_mark redundant_at_rest_pattern filter_next derivable_impls --- compiler/rustc_builtin_macros/src/asm.rs | 2 +- compiler/rustc_parse/src/parser/generics.rs | 2 +- compiler/rustc_query_impl/src/plumbing.rs | 2 +- compiler/rustc_session/src/code_stats.rs | 6 ++---- .../src/traits/error_reporting/on_unimplemented.rs | 9 ++------- library/test/src/lib.rs | 3 +-- 6 files changed, 8 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 19acefc38e31..21f459352184 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -401,7 +401,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, // should have errored above during parsing [] => unreachable!(), [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), - [abis @ ..] => { + abis => { for (abi, span) in abis { args.clobber_abis.push((*abi, *span)); } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 8ab38c4fb8be..242c9d332bb0 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -49,7 +49,7 @@ impl<'a> Parser<'a> { && self.check_ident() // `Const` followed by IDENT { - return Ok(self.recover_const_param_with_mistyped_const(preceding_attrs, ident)?); + return self.recover_const_param_with_mistyped_const(preceding_attrs, ident); } // Parse optional colon and param bounds. diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 12a3f2ac802b..def6ac280b80 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -183,7 +183,7 @@ pub(super) fn encode_all_query_results<'tcx>( encoder: &mut CacheEncoder<'_, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ) { - for encode in super::ENCODE_QUERY_RESULTS.iter().copied().filter_map(|e| e) { + for encode in super::ENCODE_QUERY_RESULTS.iter().copied().flatten() { encode(tcx, encoder, query_result_index); } } diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index cabe1c96bf79..df81e1f8305e 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -227,10 +227,8 @@ impl CodeStats { } pub fn print_vtable_sizes(&self, crate_name: &str) { - let mut infos = std::mem::take(&mut *self.vtable_sizes.lock()) - .into_iter() - .map(|(_did, stats)| stats) - .collect::>(); + let mut infos = + std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::>(); // Primary sort: cost % in reverse order (from largest to smallest) // Secondary sort: trait_name diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index fbe8d4a66803..a530b27bc918 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -329,18 +329,13 @@ pub struct OnUnimplementedNote { } /// Append a message for `~const Trait` errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] pub enum AppendConstMessage { + #[default] Default, Custom(Symbol), } -impl Default for AppendConstMessage { - fn default() -> Self { - AppendConstMessage::Default - } -} - impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index b40b6009ea45..0bd447552cf5 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -183,8 +183,7 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { let test = tests .into_iter() - .filter(|test| test.desc.name.as_slice() == name) - .next() + .find(|test| test.desc.name.as_slice() == name) .unwrap_or_else(|| panic!("couldn't find a test with the provided name '{name}'")); let TestDescAndFn { desc, testfn } = test; match testfn.into_runnable() { From b594798ae3f3499ed4fc8c12e54aa11ce4dc4b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 23 Jul 2023 11:14:52 +0200 Subject: [PATCH 038/105] fix clippy::useless_format --- compiler/rustc_hir_analysis/src/coherence/orphan.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 6 +++--- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 6 +++--- .../nice_region_error/static_impl_trait.rs | 8 ++------ compiler/rustc_resolve/src/diagnostics.rs | 4 ++-- .../src/traits/error_reporting/suggestions.rs | 2 +- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 21ffbefcd081..8d9a6f4d4461 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -352,7 +352,7 @@ fn emit_orphan_check_error<'tcx>( let this = |name: &str| { if !trait_ref.def_id.is_local() && !is_target_ty { - msg("this", &format!(" because this is a foreign trait")) + msg("this", " because this is a foreign trait") } else { msg("this", &format!(" because {name} are always foreign")) } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7597ce88f934..af8afcc0f0b9 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -705,10 +705,10 @@ impl<'a, 'tcx> CastCheck<'tcx> { ) }), |lint| { - lint.help(format!( + lint.help( "cast can be replaced by coercion; this might \ - require a temporary variable" - )) + require a temporary variable", + ) }, ); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 8e3c76d6a4b4..d3978e242a85 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -264,11 +264,11 @@ fn msg_span_from_named_region<'tcx>( ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(Some(span)), .. }, .. - }) => (format!("the anonymous lifetime defined here"), Some(span)), + }) => ("the anonymous lifetime defined here".to_owned(), Some(span)), ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(None), .. }, .. - }) => (format!("an anonymous lifetime"), None), + }) => ("an anonymous lifetime".to_owned(), None), _ => bug!("{:?}", region), } } @@ -2354,7 +2354,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span) && snip.starts_with(' ') { - format!("{new_lt}") + new_lt.to_string() } else { format!("{new_lt} ") } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index cc92bfbfdfeb..40dc64976113 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -333,11 +333,7 @@ pub fn suggest_new_region_bound( } else { None }; - let name = if let Some(name) = &existing_lt_name { - format!("{}", name) - } else { - format!("'a") - }; + let name = if let Some(name) = &existing_lt_name { name } else { "'a" }; // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used. // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any if let Some(id) = scope_def_id @@ -350,7 +346,7 @@ pub fn suggest_new_region_bound( if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_) (p.span.shrink_to_hi(),format!("{name} ")) } else { // Underscore (elided with '_) - (p.span, format!("{name}")) + (p.span, name.to_string()) } ) .collect::>() diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 2cfde2f62d8c..73cd596c0769 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1395,7 +1395,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let head_span = source_map.guess_head_span(span); err.subdiagnostic(ConsiderAddingADerive { span: head_span.shrink_to_lo(), - suggestion: format!("#[derive(Default)]\n") + suggestion: "#[derive(Default)]\n".to_string(), }); } for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { @@ -1718,7 +1718,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if next_binding.is_none() && let Some(span) = non_exhaustive { note_span.push_span_label( span, - format!("cannot be constructed because it is `#[non_exhaustive]`"), + "cannot be constructed because it is `#[non_exhaustive]`", ); } err.span_note(note_span, msg); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 05d2934d4c53..9c51de1814b3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1471,7 +1471,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let span = if needs_parens { span } else { span.shrink_to_lo() }; let suggestions = if !needs_parens { - vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))] + vec![(span.shrink_to_lo(), sugg_prefix)] } else { vec![ (span.shrink_to_lo(), format!("{}(", sugg_prefix)), From 0d9c871736092fa804e1edf4e2d198cf2fe659df Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 23 Jul 2023 10:09:43 +0000 Subject: [PATCH 039/105] add proc macro test --- .../auxiliary/count.rs | 14 ++++++++++++++ .../rfc-3348-c-string-literals/edition-spans.rs | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/auxiliary/count.rs create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/auxiliary/count.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/auxiliary/count.rs new file mode 100644 index 000000000000..0907061d64a1 --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/auxiliary/count.rs @@ -0,0 +1,14 @@ +// force-host +// edition: 2018 +// no-prefer-dynamic +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; +use std::str::FromStr; + +#[proc_macro] +pub fn number_of_tokens(_: TokenStream) -> TokenStream { + TokenStream::from_str("c\"\"").unwrap().into_iter().count().to_string().parse().unwrap() +} diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs new file mode 100644 index 000000000000..b3557c71b744 --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs @@ -0,0 +1,16 @@ +// even if this crate is edition 2021, proc macros compiled using older +// editions should still be able to observe the pre-2021 token behavior +// +// adapted from tests/ui/rust-2021/reserved-prefixes-via-macro.rs + +// edition: 2021 +// check-pass + +// aux-build: count.rs +extern crate count; + +const _: () = { + assert!(count::number_of_tokens!() == 2); +}; + +fn main() {} From e06633fde62b2b08cdc0829446f7225ee657b748 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 23 Jul 2023 13:05:40 +0200 Subject: [PATCH 040/105] Migrate GUI colors test to original CSS color format --- tests/rustdoc-gui/label-next-to-symbol.goml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rustdoc-gui/label-next-to-symbol.goml b/tests/rustdoc-gui/label-next-to-symbol.goml index eb603d6c6b6a..d23f9114d361 100644 --- a/tests/rustdoc-gui/label-next-to-symbol.goml +++ b/tests/rustdoc-gui/label-next-to-symbol.goml @@ -10,11 +10,11 @@ assert: (".stab.portability") // make sure that deprecated and portability have the right colors assert-css: ( ".item-table .item-name .stab.deprecated", - { "background-color": "rgb(255, 245, 214)" }, + { "background-color": "#fff5d6" }, ) assert-css: ( ".item-table .item-name .stab.portability", - { "background-color": "rgb(255, 245, 214)" }, + { "background-color": "#fff5d6" }, ) // table like view From d1380a18447e9509cc9cd5307edcb4e22ca0351b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 23 Jul 2023 13:56:03 -0400 Subject: [PATCH 041/105] Use erased self type when autoderefing for trait error suggestion --- .../src/traits/error_reporting/suggestions.rs | 10 +++---- .../dont-autoderef-ty-with-escaping-var.rs | 22 +++++++++++++++ ...dont-autoderef-ty-with-escaping-var.stderr | 27 +++++++++++++++++++ 3 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 tests/ui/traits/dont-autoderef-ty-with-escaping-var.rs create mode 100644 tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 073a2a2b1a0c..3494e819b2e7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -776,18 +776,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { real_trait_pred = parent_trait_pred; } - let real_ty = real_trait_pred.self_ty(); // We `erase_late_bound_regions` here because `make_subregion` does not handle // `ReLateBound`, and we don't particularly care about the regions. - if !self.can_eq( - obligation.param_env, - self.tcx.erase_late_bound_regions(real_ty), - arg_ty, - ) { + let real_ty = self.tcx.erase_late_bound_regions(real_trait_pred.self_ty()); + if !self.can_eq(obligation.param_env, real_ty, arg_ty) { continue; } - if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() { + if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { let autoderef = (self.autoderef_steps)(base_ty); if let Some(steps) = autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { diff --git a/tests/ui/traits/dont-autoderef-ty-with-escaping-var.rs b/tests/ui/traits/dont-autoderef-ty-with-escaping-var.rs new file mode 100644 index 000000000000..d5ba3847ac38 --- /dev/null +++ b/tests/ui/traits/dont-autoderef-ty-with-escaping-var.rs @@ -0,0 +1,22 @@ +// issue:113951 + +trait Foo<'x, T> {} + +trait RefFoo { + fn ref_foo(&self); +} + +impl RefFoo for T +where + for<'a> &'a mut Vec<&'a u32>: Foo<'static, T>, +{ + fn ref_foo(&self) {} +} + +fn coerce_lifetime2() { + >::ref_foo(unknown); + //~^ ERROR cannot find value `unknown` in this scope + //~| ERROR the trait bound `for<'a> &'a mut Vec<&'a u32>: Foo<'static, i32>` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr b/tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr new file mode 100644 index 000000000000..934d20b22677 --- /dev/null +++ b/tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr @@ -0,0 +1,27 @@ +error[E0425]: cannot find value `unknown` in this scope + --> $DIR/dont-autoderef-ty-with-escaping-var.rs:17:35 + | +LL | >::ref_foo(unknown); + | ^^^^^^^ not found in this scope + +error[E0277]: the trait bound `for<'a> &'a mut Vec<&'a u32>: Foo<'static, i32>` is not satisfied + --> $DIR/dont-autoderef-ty-with-escaping-var.rs:17:35 + | +LL | >::ref_foo(unknown); + | ----------------------------- ^^^^^^^ the trait `for<'a> Foo<'static, i32>` is not implemented for `&'a mut Vec<&'a u32>` + | | + | required by a bound introduced by this call + | +note: required for `i32` to implement `RefFoo` + --> $DIR/dont-autoderef-ty-with-escaping-var.rs:9:9 + | +LL | impl RefFoo for T + | ^^^^^^^^^ ^ +LL | where +LL | for<'a> &'a mut Vec<&'a u32>: Foo<'static, T>, + | --------------- unsatisfied trait bound introduced here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0425. +For more information about an error, try `rustc --explain E0277`. From f3553691a89ffeb9af770051a4f7e7ac0d771406 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 23 Jul 2023 12:30:52 -0700 Subject: [PATCH 042/105] Comment stuff in the new solver --- .../src/solve/alias_relate.rs | 20 +++++++++++++- .../src/solve/assembly/structural_traits.rs | 2 ++ .../src/solve/eval_ctxt/canonical.rs | 27 ++++++++++++------- .../src/solve/inherent_projection.rs | 6 +++++ .../rustc_trait_selection/src/solve/mod.rs | 13 ++++++--- .../src/solve/normalize.rs | 3 ++- .../src/solve/opaques.rs | 3 +++ .../src/solve/weak_types.rs | 5 ++++ 8 files changed, 63 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 73362d823065..c46b4df11251 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -1,3 +1,15 @@ +//! Implements the `AliasRelate` goal, which is used to unify two aliases in the +//! new solver, which uses "lazy normalization". +//! +//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches: +//! * normalizes-to: If `A` is a projection, we can prove the equivalent +//! projection predicate with B as the right-hand side of the projection. +//! This goal is computed in both directions, if both are aliases. +//! * subst-relate: Equate `A` and `B` by their substs, if they're both +//! aliases with the same def-id. +//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both +//! may apply, then we can compute the "intersection" of both normalizes-to by +//! performing them together. This is used specifically to resolve ambiguities. use super::{EvalCtxt, SolverMode}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; @@ -115,6 +127,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + // Computes the normalizes-to branch, with side-effects. This must be performed + // in a probe in order to not taint the evaluation context. fn normalizes_to_inner( &mut self, param_env: ty::ParamEnv<'tcx>, @@ -124,9 +138,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { invert: Invert, ) -> Result<(), NoSolution> { let other = match direction { - // This is purely an optimization. + // This is purely an optimization. No need to instantiate a new + // infer var and equate the RHS to it. ty::AliasRelationDirection::Equate => other, + // Instantiate an infer var and subtype our RHS to it, so that we + // properly represent a subtype relation between the LHS and RHS + // of the goal. ty::AliasRelationDirection::Subtype => { let fresh = self.next_term_infer_of_kind(other); let (sub, sup) = match invert { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index a8ba98bef6d9..a2db35e069eb 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -1,3 +1,5 @@ +//! Code which is used by built-in goals that match "structurally", such a auto +//! traits, `Copy`/`Clone`. use rustc_data_structures::fx::FxHashMap; use rustc_hir::{def_id::DefId, Movability, Mutability}; use rustc_infer::traits::query::NoSolution; diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 9f58e66d04e8..7323b98b8ce2 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -1,13 +1,13 @@ -/// Canonicalization is used to separate some goal from its context, -/// throwing away unnecessary information in the process. -/// -/// This is necessary to cache goals containing inference variables -/// and placeholders without restricting them to the current `InferCtxt`. -/// -/// Canonicalization is fairly involved, for more details see the relevant -/// section of the [rustc-dev-guide][c]. -/// -/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html +//! Canonicalization is used to separate some goal from its context, +//! throwing away unnecessary information in the process. +//! +//! This is necessary to cache goals containing inference variables +//! and placeholders without restricting them to the current `InferCtxt`. +//! +//! Canonicalization is fairly involved, for more details see the relevant +//! section of the [rustc-dev-guide][c]. +//! +//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; use crate::solve::{CanonicalResponse, QueryResult, Response}; @@ -135,6 +135,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) } + /// Computes the region constraints and *new* opaque types registered when + /// proving a goal. + /// + /// If an opaque was already constrained before proving this goal, then the + /// external constraints do not need to record that opaque, since if it is + /// further constrained by inference, that will be passed back in the var + /// values. #[instrument(level = "debug", skip(self), ret)] fn compute_external_query_constraints(&self) -> Result, NoSolution> { // We only check for leaks from universes which were entered inside diff --git a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs index d10a14ff742e..28fe59b7f6a7 100644 --- a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs +++ b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs @@ -1,3 +1,9 @@ +//! Computes a normalizes-to (projection) goal for inherent associated types, +//! `#![feature(inherent_associated_type)]`. Since astconv already determines +//! which impl the IAT is being projected from, we just: +//! 1. instantiate substs, +//! 2. equate the self type, and +//! 3. instantiate and register where clauses. use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 7c15c3c0e8b5..cc59dd5df03d 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -1,14 +1,19 @@ -//! The new trait solver, currently still WIP. +//! The next-generation trait solver, currently still WIP. //! -//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to -//! interact with this solver. +//! As a user of rust, you can use `-Ztrait-solver=next` or `next-coherence` +//! to enable the new trait solver always, or just within coherence, respectively. +//! +//! As a developer of rustc, you probably shouldn't be using the new trait +//! solver without asking the trait-system-refactor-initiative, but it can +//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will +//! ensure that trait solving using that inference context will be routed +//! to the new trait solver. //! //! For a high-level overview of how this solver works, check out the relevant //! section of the rustc-dev-guide. //! //! FIXME(@lcnr): Write that section. If you read this before then ask me //! about it on zulip. - use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index f51f4edb9334..0e5b137c5de7 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -28,7 +28,8 @@ pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable>>( /// its input to be already fully resolved. /// /// Additionally takes a list of universes which represents the binders which have been -/// entered before passing `value` to the function. +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. pub(crate) fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable>>( at: At<'_, 'tcx>, value: T, diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index 2e537d1c358a..f08adc0208b5 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -1,3 +1,6 @@ +//! Computes a normalizes-to (projection) goal for opaque types. This goal +//! behaves differently depending on the param-env's reveal mode and whether +//! the opaque is in a defining scope. use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::traits::Reveal; diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs index c7717879a4ac..fea634a2ad2f 100644 --- a/compiler/rustc_trait_selection/src/solve/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs @@ -1,3 +1,8 @@ +//! Computes a normalizes-to (projection) goal for inherent associated types, +//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`. +//! +//! Since a weak alias is not ambiguous, this just computes the `type_of` of +//! the alias and registers any where-clause predicates on the type alias. use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; From ed4c5fef728e2d1288134ff0938182ec52cff621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 23 Jul 2023 12:08:42 +0200 Subject: [PATCH 043/105] fix some clippy::style findings comparison_to_empty iter_nth_zero for_kv_map manual_next_back redundant_pattern --- compiler/rustc_hir_typeck/src/demand.rs | 2 +- .../src/infer/region_constraints/leak_check.rs | 8 +++++--- compiler/rustc_lexer/src/unescape.rs | 2 +- compiler/rustc_monomorphize/src/partitioning.rs | 11 ++++++----- .../rustc_parse/src/lexer/unescape_error_reporting.rs | 2 +- compiler/rustc_ty_utils/src/implied_bounds.rs | 2 +- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 429c3366fedf..45085534f72f 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -621,7 +621,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // is in a different line, so we point at both. err.span_label(secondary_span, "expected due to the type of this binding"); err.span_label(primary_span, format!("expected due to this{post_message}")); - } else if post_message == "" { + } else if post_message.is_empty() { // We are pointing at either the assignment lhs or the binding def pattern. err.span_label(primary_span, "expected due to the type of this binding"); } else { diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index dd65f66ccd14..b6ff8f2f512d 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -425,9 +425,11 @@ impl<'tcx> MiniGraph<'tcx> { } } } else { - for (constraint, _origin) in ®ion_constraints.data().constraints { - each_constraint(constraint) - } + region_constraints + .data() + .constraints + .keys() + .for_each(|constraint| each_constraint(constraint)); } } diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index c9ad54d8d980..717b042fbdab 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -372,7 +372,7 @@ where callback(start..end, EscapeError::MultipleSkippedLinesWarning); } let tail = &tail[first_non_space..]; - if let Some(c) = tail.chars().nth(0) { + if let Some(c) = tail.chars().next() { if c.is_whitespace() { // For error reporting, we would like the span to contain the character that was not // skipped. The +1 is necessary to account for the leading \ that started the escape. diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 71aef53192f4..ffd8bf002295 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1229,12 +1229,13 @@ fn dump_mono_items_stats<'tcx>( // Gather instantiated mono items grouped by def_id let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { - for (&mono_item, _) in cgu.items() { + cgu.items() + .keys() // Avoid variable-sized compiler-generated shims - if mono_item.is_user_defined() { + .filter(|mono_item| mono_item.is_user_defined()) + .for_each(|mono_item| { items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item); - } - } + }); } #[derive(serde::Serialize)] @@ -1287,7 +1288,7 @@ fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet { let mut result = items.clone(); for cgu in cgus { - for (item, _) in cgu.items() { + for item in cgu.items().keys() { if let MonoItem::Fn(ref instance) = item { let did = instance.def_id(); if !visited.insert(did) { diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 461a34b67db6..d1f852b1a405 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -27,7 +27,7 @@ pub(crate) fn emit_unescape_error( lit, span_with_quotes, mode, range, error ); let last_char = || { - let c = lit[range.clone()].chars().rev().next().unwrap(); + let c = lit[range.clone()].chars().next_back().unwrap(); let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32)); (c, span) }; diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index be063a097c02..fa1976510cf2 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -51,7 +51,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' // assumed_wf_types should include those of `Opaque`, `Opaque` itself // and `&'static T`. DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"), - def_kind @ _ => { + def_kind => { bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}") } }, From af2b370100cdc1767772af738a3259875bba38f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 23 Jul 2023 13:11:20 +0200 Subject: [PATCH 044/105] more clippy::style fixes: get_first single_char_add_str unnecessary_mut_passed manual_map manual_is_ascii_check --- compiler/rustc_ast/src/util/comments.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 2 +- compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 8 ++++---- compiler/rustc_data_structures/src/sso/map.rs | 6 +----- compiler/rustc_hir/src/hir.rs | 3 +-- compiler/rustc_monomorphize/src/partitioning.rs | 5 +---- .../src/typeid/typeid_itanium_cxx_abi.rs | 2 +- compiler/rustc_trait_selection/src/solve/normalize.rs | 4 ++-- 8 files changed, 12 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index eece99a3eef0..bdf5143b0f70 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -62,7 +62,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { CommentKind::Block => { // Whatever happens, we skip the first line. let mut i = lines - .get(0) + .first() .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 }) .unwrap_or(0); let mut j = lines.len(); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index dcaaaafedbea..0954cf03da9a 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -286,7 +286,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf( self.lower_ty( container, - &mut ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf), + &ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf), ), self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))), ), diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 609920180a2f..8767bbcb2106 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -697,15 +697,15 @@ pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> St write!(template, "{n}").unwrap(); if p.format_options != Default::default() || p.format_trait != FormatTrait::Display { - template.push_str(":"); + template.push(':'); } if let Some(fill) = p.format_options.fill { template.push(fill); } match p.format_options.alignment { - Some(FormatAlignment::Left) => template.push_str("<"), - Some(FormatAlignment::Right) => template.push_str(">"), - Some(FormatAlignment::Center) => template.push_str("^"), + Some(FormatAlignment::Left) => template.push('<'), + Some(FormatAlignment::Right) => template.push('>'), + Some(FormatAlignment::Center) => template.push('^'), None => {} } match p.format_options.sign { diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index 99581ed23759..04e359a54708 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -268,11 +268,7 @@ impl SsoHashMap { pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> { match self { SsoHashMap::Array(array) => { - if let Some(index) = array.iter().position(|(k, _v)| k == key) { - Some(array.swap_remove(index)) - } else { - None - } + array.iter().position(|(k, _v)| k == key).map(|index| array.swap_remove(index)) } SsoHashMap::Map(map) => map.remove_entry(key), } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 68f1559ea220..3663c450ba64 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3013,8 +3013,7 @@ pub struct FieldDef<'hir> { impl FieldDef<'_> { // Still necessary in couple of places pub fn is_positional(&self) -> bool { - let first = self.ident.as_str().as_bytes()[0]; - (b'0'..=b'9').contains(&first) + self.ident.as_str().as_bytes()[0].is_ascii_digit() } } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index ffd8bf002295..c34c2c248fe5 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1041,10 +1041,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< } elem(curr, curr_count); - let mut s = "[".to_string(); - s.push_str(&v.join(", ")); - s.push_str("]"); - s + format!("[{}]", v.join(", ")) } }; diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 4a938ff938a0..f683832e3df0 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -400,7 +400,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { let _ = write!(s, "{}", name.len()); // Prepend a '_' if name starts with a digit or '_' - if let Some(first) = name.as_bytes().get(0) { + if let Some(first) = name.as_bytes().first() { if first.is_ascii_digit() || *first == b'_' { s.push('_'); } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index f51f4edb9334..9a12a2ad3dc1 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -194,7 +194,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { mapped_regions, mapped_types, mapped_consts, - &mut self.universes, + &self.universes, result, )) } else { @@ -224,7 +224,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { mapped_regions, mapped_types, mapped_consts, - &mut self.universes, + &self.universes, result, )) } else { From a8f1b72f08018127a6270009ae36957e5dfd2a97 Mon Sep 17 00:00:00 2001 From: Chris Copeland Date: Mon, 17 Apr 2023 21:55:19 -0700 Subject: [PATCH 045/105] Remove "-unknown" from `llvm_target` for arm*v7r-none-eabi* targets. --- compiler/rustc_target/src/spec/armebv7r_none_eabi.rs | 2 +- compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs | 2 +- compiler/rustc_target/src/spec/armv7r_none_eabi.rs | 2 +- compiler/rustc_target/src/spec/armv7r_none_eabihf.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index 5632bcfcefe2..f9a1fd821a64 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -5,7 +5,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armebv7r-unknown-none-eabi".into(), + llvm_target: "armebv7r-none-eabi".into(), pointer_width: 32, data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index 2815de3589db..837a4166121d 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -5,7 +5,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armebv7r-unknown-none-eabihf".into(), + llvm_target: "armebv7r-none-eabihf".into(), pointer_width: 32, data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index 74905ed5a4e0..a2864b385d7c 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -4,7 +4,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armv7r-unknown-none-eabi".into(), + llvm_target: "armv7r-none-eabi".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index 516b3f5c17ee..6a51cbd49c51 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -4,7 +4,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armv7r-unknown-none-eabihf".into(), + llvm_target: "armv7r-none-eabihf".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), From ef8994827e4075e3b0d69b71f96fe07cf3d0f881 Mon Sep 17 00:00:00 2001 From: Chris Copeland Date: Mon, 17 Jul 2023 22:18:29 -0700 Subject: [PATCH 046/105] Remove redundant `c_enum_min_bits` option from the thumbv4t-none-eabi target. This option is the same as the `thumb_base` defaults used by this target. --- compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs index 9c59bb9114ec..88a76f49acd6 100644 --- a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -45,8 +45,6 @@ pub fn target() -> Target { relocation_model: RelocModel::Static, // suggested from thumb_base, rust-lang/rust#44993. emit_debug_gdb_scripts: false, - // suggested from thumb_base, with no-os gcc/clang use 8-bit enums - c_enum_min_bits: Some(8), frame_pointer: FramePointer::MayOmit, main_needs_argc_argv: false, From 8e54caba04bbb040b2221da2bbaa6d11655f09f5 Mon Sep 17 00:00:00 2001 From: Chris Copeland Date: Mon, 17 Jul 2023 22:13:15 -0700 Subject: [PATCH 047/105] Fix comments about GCC/Clang's enum width for arm-none targets. GCC uses the `-fshort-enums` ABI for arm-none and the `int`-sized enum ABI for arm-linux. Clang uses the `int`-sized enum ABI for all arm targets. Both options are permitted by AAPCS. Rust is matching GCC's behavior for these targets, as interop with code code compiled by GCC is desirable in the bare-metal context. See #87917. --- compiler/rustc_target/src/spec/armebv7r_none_eabi.rs | 2 +- compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs | 2 +- compiler/rustc_target/src/spec/armv4t_none_eabi.rs | 4 ++-- compiler/rustc_target/src/spec/armv7a_none_eabihf.rs | 2 +- compiler/rustc_target/src/spec/armv7r_none_eabi.rs | 2 +- compiler/rustc_target/src/spec/armv7r_none_eabihf.rs | 2 +- compiler/rustc_target/src/spec/thumb_base.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index f9a1fd821a64..446efa90d098 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -18,7 +18,7 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Abort, max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index 837a4166121d..0c9e99ff84bf 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -19,7 +19,7 @@ pub fn target() -> Target { features: "+vfp3,-d32,-fp16".into(), max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armv4t_none_eabi.rs b/compiler/rustc_target/src/spec/armv4t_none_eabi.rs index d0f988b278ff..44fdd3178af4 100644 --- a/compiler/rustc_target/src/spec/armv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv4t_none_eabi.rs @@ -39,9 +39,9 @@ pub fn target() -> Target { has_thumb_interworking: true, relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - // from thumb_base, rust-lang/rust#44993. + // From thumb_base, rust-lang/rust#44993. emit_debug_gdb_scripts: false, - // from thumb_base, apparently gcc/clang give enums a minimum of 8 bits on no-os targets + // From thumb_base, GCC gives enums a minimum of 8 bits on no-os targets. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs index 8cdf3c36ba2e..c134f3e09078 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -18,7 +18,7 @@ pub fn target() -> Target { max_atomic_width: Some(64), panic_strategy: PanicStrategy::Abort, emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }; diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index a2864b385d7c..68b2527985a6 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -17,7 +17,7 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Abort, max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index 6a51cbd49c51..909765a310fe 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -18,7 +18,7 @@ pub fn target() -> Target { features: "+vfp3,-d32,-fp16".into(), max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs index 2220b9326c97..0decfecb4cd1 100644 --- a/compiler/rustc_target/src/spec/thumb_base.rs +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -52,7 +52,7 @@ pub fn opts() -> TargetOptions { // breaks debugging. Preserve LR by default to prevent that from happening. frame_pointer: FramePointer::Always, // ARM supports multiple ABIs for enums, the linux one matches the default of 32 here - // but any arm-none or thumb-none target will be defaulted to 8 on GCC and clang + // but any arm-none or thumb-none target will be defaulted to 8 on GCC. c_enum_min_bits: Some(8), ..Default::default() } From c2158a44e1674f03e839d85e05d12554ccaa813e Mon Sep 17 00:00:00 2001 From: Eric Mark Martin Date: Sun, 23 Jul 2023 23:52:19 -0400 Subject: [PATCH 048/105] generic smir stable impl for Binder --- compiler/rustc_smir/src/rustc_smir/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 97bde5c3c191..1b8c78fc2801 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -525,13 +525,17 @@ impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { } } -impl<'tcx> Stable<'tcx> for ty::PolyFnSig<'tcx> { - type T = stable_mir::ty::PolyFnSig; +impl<'tcx, S, V> Stable<'tcx> for ty::Binder<'tcx, S> +where + S: Stable<'tcx, T = V>, +{ + type T = stable_mir::ty::Binder; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::Binder; Binder { - value: self.skip_binder().stable(tables), + value: self.as_ref().skip_binder().stable(tables), bound_vars: self .bound_vars() .iter() From e36b901933bbd4e747a7eddfbadf23bda93eba02 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 24 Jul 2023 00:08:09 -0400 Subject: [PATCH 049/105] Optimize format usage Per #112156, using `&` in `format!` may cause a small perf delay, so I tried to clean up one module at a time format usage. This PR includes a few removals of the ref in format (they do compile locally without the ref), as well as a few format inlining for consistency. --- compiler/rustc_errors/src/diagnostic.rs | 10 +++++----- compiler/rustc_errors/src/diagnostic_impls.rs | 2 +- compiler/rustc_errors/src/emitter.rs | 4 ++-- compiler/rustc_errors/src/lib.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index ed0d06ed0ff7..a96e317df55d 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -420,13 +420,13 @@ impl Diagnostic { let expected_label = if expected_label.is_empty() { "expected".to_string() } else { - format!("expected {}", expected_label) + format!("expected {expected_label}") }; let found_label = found_label.to_string(); let found_label = if found_label.is_empty() { "found".to_string() } else { - format!("found {}", found_label) + format!("found {found_label}") }; let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { (expected_label.len() - found_label.len(), 0) @@ -439,13 +439,13 @@ impl Diagnostic { StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), })); - msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); + msg.push((format!("`{expected_extra}\n"), Style::NoStyle)); msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle)); msg.extend(found.0.iter().map(|x| match *x { StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), })); - msg.push((format!("`{}", found_extra), Style::NoStyle)); + msg.push((format!("`{found_extra}"), Style::NoStyle)); // For now, just attach these as notes. self.highlighted_note(msg); @@ -454,7 +454,7 @@ impl Diagnostic { pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ - (format!("`{}` from trait: `", name), Style::NoStyle), + (format!("`{name}` from trait: `"), Style::NoStyle), (signature, Style::Highlight), ("`".to_string(), Style::NoStyle), ]); diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 10fe7fc74a87..9c972342f251 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -102,7 +102,7 @@ impl IntoDiagnosticArg for bool { impl IntoDiagnosticArg for char { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) + DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}"))) } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index a0fa4115c3e4..961feba3250c 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -279,12 +279,12 @@ pub trait Emitter: Translate { let msg = if substitution.is_empty() || sugg.style.hide_inline() { // This substitution is only removal OR we explicitly don't want to show the // code inline (`hide_inline`). Therefore, we don't show the substitution. - format!("help: {}", &msg) + format!("help: {msg}") } else { // Show the default suggestion text with the substitution format!( "help: {}{}: `{}`", - &msg, + msg, if self.source_map().is_some_and(|sm| is_case_difference( sm, substitution, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 1da02e1bb012..eae7a46e07a3 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1485,7 +1485,7 @@ impl HandlerInner { let _ = self.fatal(errors); } (_, _) => { - let _ = self.fatal(format!("{}; {}", &errors, &warnings)); + let _ = self.fatal(format!("{errors}; {warnings}")); } } From badb617eb583d219fcce797e9a22499562a4dedb Mon Sep 17 00:00:00 2001 From: Eric Mark Martin Date: Sun, 23 Jul 2023 23:53:08 -0400 Subject: [PATCH 050/105] Dynamic for smir --- compiler/rustc_smir/src/rustc_internal/mod.rs | 8 ++ compiler/rustc_smir/src/rustc_smir/mod.rs | 77 ++++++++++++++++++- compiler/rustc_smir/src/stable_mir/ty.rs | 36 +++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index e0cf698acd74..6a26a4a22ed4 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -59,6 +59,10 @@ pub fn br_named_def(did: DefId) -> stable_mir::ty::BrNamedDef { with_tables(|t| t.br_named_def(did)) } +pub fn trait_def(did: DefId) -> stable_mir::ty::TraitDef { + with_tables(|t| t.trait_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -100,6 +104,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::BrNamedDef(self.create_def_id(did)) } + pub fn trait_def(&mut self, did: DefId) -> stable_mir::ty::TraitDef { + stable_mir::ty::TraitDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 1b8c78fc2801..0805f4f4d5b5 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -258,6 +258,72 @@ impl<'tcx> Stable<'tcx> for ty::AliasTy<'tcx> { } } +impl<'tcx> Stable<'tcx> for ty::DynKind { + type T = stable_mir::ty::DynKind; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use ty::DynKind; + match self { + DynKind::Dyn => stable_mir::ty::DynKind::Dyn, + DynKind::DynStar => stable_mir::ty::DynKind::DynStar, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> { + type T = stable_mir::ty::ExistentialPredicate; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::ExistentialPredicate::*; + match self { + ty::ExistentialPredicate::Trait(existential_trait_ref) => { + Trait(existential_trait_ref.stable(tables)) + } + ty::ExistentialPredicate::Projection(existential_projection) => { + Projection(existential_projection.stable(tables)) + } + ty::ExistentialPredicate::AutoTrait(def_id) => AutoTrait(tables.trait_def(*def_id)), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> { + type T = stable_mir::ty::ExistentialTraitRef; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + let ty::ExistentialTraitRef { def_id, args } = self; + stable_mir::ty::ExistentialTraitRef { + def_id: tables.trait_def(*def_id), + generic_args: args.stable(tables), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> { + type T = stable_mir::ty::TermKind; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::TermKind; + match self { + ty::TermKind::Ty(ty) => TermKind::Type(tables.intern_ty(*ty)), + ty::TermKind::Const(const_) => TermKind::Const(opaque(const_)), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> { + type T = stable_mir::ty::ExistentialProjection; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + let ty::ExistentialProjection { def_id, args, term } = self; + stable_mir::ty::ExistentialProjection { + def_id: tables.trait_def(*def_id), + generic_args: args.stable(tables), + term: term.unpack().stable(tables), + } + } +} + impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion { type T = stable_mir::mir::PointerCoercion; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { @@ -675,7 +741,16 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { generic_args.stable(tables), )), ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(tables))), - ty::Dynamic(_, _, _) => todo!(), + ty::Dynamic(existential_predicates, region, dyn_kind) => { + TyKind::RigidTy(RigidTy::Dynamic( + existential_predicates + .iter() + .map(|existential_predicate| existential_predicate.stable(tables)) + .collect(), + opaque(region), + dyn_kind.stable(tables), + )) + } ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( rustc_internal::closure_def(*def_id), generic_args.stable(tables), diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 885beeda78c9..7b4747a7fe29 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -38,6 +38,7 @@ pub enum RigidTy { FnPtr(PolyFnSig), Closure(ClosureDef, GenericArgs), Generator(GeneratorDef, GenericArgs, Movability), + Dynamic(Vec>, Region, DynKind), Never, Tuple(Vec), } @@ -98,6 +99,9 @@ pub struct AdtDef(pub(crate) DefId); #[derive(Clone, PartialEq, Eq, Debug)] pub struct AliasDef(pub(crate) DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct TraitDef(pub(crate) DefId); + #[derive(Clone, Debug)] pub struct GenericArgs(pub Vec); @@ -108,6 +112,12 @@ pub enum GenericArgKind { Const(Const), } +#[derive(Clone, Debug)] +pub enum TermKind { + Type(Ty), + Const(Const), +} + #[derive(Clone, Debug)] pub enum AliasKind { Projection, @@ -192,3 +202,29 @@ pub enum BoundRegionKind { BrNamed(BrNamedDef, String), BrEnv, } + +#[derive(Clone, Debug)] +pub enum DynKind { + Dyn, + DynStar, +} + +#[derive(Clone, Debug)] +pub enum ExistentialPredicate { + Trait(ExistentialTraitRef), + Projection(ExistentialProjection), + AutoTrait(TraitDef), +} + +#[derive(Clone, Debug)] +pub struct ExistentialTraitRef { + pub def_id: TraitDef, + pub generic_args: GenericArgs, +} + +#[derive(Clone, Debug)] +pub struct ExistentialProjection { + pub def_id: TraitDef, + pub generic_args: GenericArgs, + pub term: TermKind, +} From bb1ad0a2f1f07b2c21e6b2bf24013f81dd585a6c Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 24 Jul 2023 00:18:52 -0400 Subject: [PATCH 051/105] Optimize format usage Per #112156, using `&` in `format!` may cause a small perf delay, so I tried to clean up one module at a time format usage. This PR includes a few removals of the ref in format (they do compile locally without the ref), as well as a few format inlining for consistency. --- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/validate_attr.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 769415b614b0..b54cb8c5a0c1 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -238,7 +238,7 @@ impl<'a> Parser<'a> { _ => unreachable!(), } .into(); - let invalid = format!("{}=", &sugg); + let invalid = format!("{sugg}="); self.sess.emit_err(errors::InvalidComparisonOperator { span: sp, invalid: invalid.clone(), diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 928fdce313d9..2011083019c8 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -157,15 +157,15 @@ fn emit_malformed_attribute( matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench) }; - let error_msg = format!("malformed `{}` attribute input", name); + let error_msg = format!("malformed `{name}` attribute input"); let mut msg = "attribute must be of the form ".to_owned(); let mut suggestions = vec![]; let mut first = true; let inner = if style == ast::AttrStyle::Inner { "!" } else { "" }; if template.word { first = false; - let code = format!("#{}[{}]", inner, name); - msg.push_str(&format!("`{}`", &code)); + let code = format!("#{inner}[{name}]"); + msg.push_str(&format!("`{code}`")); suggestions.push(code); } if let Some(descr) = template.list { @@ -173,16 +173,16 @@ fn emit_malformed_attribute( msg.push_str(" or "); } first = false; - let code = format!("#{}[{}({})]", inner, name, descr); - msg.push_str(&format!("`{}`", &code)); + let code = format!("#{inner}[{name}({descr})]"); + msg.push_str(&format!("`{code}`")); suggestions.push(code); } if let Some(descr) = template.name_value_str { if !first { msg.push_str(" or "); } - let code = format!("#{}[{} = \"{}\"]", inner, name, descr); - msg.push_str(&format!("`{}`", &code)); + let code = format!("#{inner}[{name} = \"{descr}\"]"); + msg.push_str(&format!("`{code}`")); suggestions.push(code); } if should_warn(name) { From 2676637666a221bc4d4876245c607141e7840e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sat, 22 Jul 2023 12:16:42 +0200 Subject: [PATCH 052/105] compiler: Hermit targets: Remove pre-link args. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These pre-link args are remains from Hermit's old C version. We don't need them and we have no reason to override the defaults here. See ld [1] for details. [1]: https://sourceware.org/binutils/docs/ld/Options.html Signed-off-by: Martin Kröning --- compiler/rustc_target/src/spec/hermit_base.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index dd9991381e7b..b373a1630ece 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -1,17 +1,11 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { - let pre_link_args = TargetOptions::link_args( - LinkerFlavor::Gnu(Cc::No, Lld::No), - &["--build-id", "--hash-style=gnu", "--Bstatic"], - ); - TargetOptions { os: "hermit".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), has_thread_local: true, - pre_link_args, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, static_position_independent_executables: true, From a4e1bf416d7dc2e6ed156ed09f7f3294c8eec48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sat, 22 Jul 2023 13:45:50 +0200 Subject: [PATCH 053/105] compiler: Hermit targets: Sort base fields by declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs | 2 +- compiler/rustc_target/src/spec/hermit_base.rs | 8 ++++---- compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs index 87e8d6270269..2dba928f0bb5 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs @@ -8,8 +8,8 @@ pub fn target() -> Target { Target { llvm_target: "aarch64-unknown-hermit".into(), pointer_width: 64, - data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: "aarch64".into(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), options: base, } } diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index b373a1630ece..c6e98fc1a115 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -3,13 +3,13 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, TargetOptions, TlsModel} pub fn opts() -> TargetOptions { TargetOptions { os: "hermit".into(), - linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), - has_thread_local: true, - panic_strategy: PanicStrategy::Abort, + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + tls_model: TlsModel::InitialExec, position_independent_executables: true, static_position_independent_executables: true, - tls_model: TlsModel::InitialExec, + has_thread_local: true, + panic_strategy: PanicStrategy::Abort, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs index 74ef2527c36c..84692d63d071 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs @@ -11,9 +11,9 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-hermit".into(), pointer_width: 64, + arch: "x86_64".into(), data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .into(), - arch: "x86_64".into(), options: base, } } From 636804e03288fc1d73c977d212f7f69125c9e473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sat, 22 Jul 2023 12:53:11 +0200 Subject: [PATCH 054/105] compiler: Hermit targets: Use functional update syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit instead of mutating the base. Signed-off-by: Martin Kröning --- .../src/spec/aarch64_unknown_hermit.rs | 12 ++++++------ .../src/spec/x86_64_unknown_hermit.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs index 2dba928f0bb5..2414867be552 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs @@ -1,15 +1,15 @@ -use crate::spec::Target; +use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { - let mut base = super::hermit_base::opts(); - base.max_atomic_width = Some(128); - base.features = "+v8a,+strict-align,+neon,+fp-armv8".into(); - Target { llvm_target: "aarch64-unknown-hermit".into(), pointer_width: 64, arch: "aarch64".into(), data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), - options: base, + options: TargetOptions { + features: "+v8a,+strict-align,+neon,+fp-armv8".into(), + max_atomic_width: Some(128), + ..super::hermit_base::opts() + }, } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs index 84692d63d071..1eb069301117 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs @@ -1,19 +1,19 @@ -use crate::spec::{StackProbeType, Target}; +use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { - let mut base = super::hermit_base::opts(); - base.cpu = "x86-64".into(); - base.plt_by_default = false; - base.max_atomic_width = Some(64); - base.features = "+rdrnd,+rdseed".into(); - base.stack_probes = StackProbeType::X86; - Target { llvm_target: "x86_64-unknown-hermit".into(), pointer_width: 64, arch: "x86_64".into(), data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .into(), - options: base, + options: TargetOptions { + cpu: "x86-64".into(), + features: "+rdrnd,+rdseed".into(), + plt_by_default: false, + max_atomic_width: Some(64), + stack_probes: StackProbeType::X86, + ..super::hermit_base::opts() + }, } } From 3003fe2d80b571e655519fa58b222f92dd0dd480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sch=C3=B6ning?= Date: Fri, 21 May 2021 12:20:59 +0200 Subject: [PATCH 055/105] compiler: Add `riscv64gc-unknown-hermit` target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin Kröning Signed-off-by: Martin Kröning --- compiler/rustc_target/src/spec/mod.rs | 1 + .../src/spec/riscv64gc_unknown_hermit.rs | 20 +++++++++++++++++++ src/tools/build-manifest/src/main.rs | 1 + 3 files changed, 22 insertions(+) create mode 100644 compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 6ae07f45f4a4..aab31a4c8fef 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1418,6 +1418,7 @@ supported_targets! { ("msp430-none-elf", msp430_none_elf), ("aarch64-unknown-hermit", aarch64_unknown_hermit), + ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs new file mode 100644 index 000000000000..1f6a34c0cacc --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs @@ -0,0 +1,20 @@ +use crate::spec::{CodeModel, RelocModel, Target, TargetOptions, TlsModel}; + +pub fn target() -> Target { + Target { + llvm_target: "riscv64-unknown-hermit".into(), + pointer_width: 64, + arch: "riscv64".into(), + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + options: TargetOptions { + cpu: "generic-rv64".into(), + features: "+m,+a,+f,+d,+c".into(), + relocation_model: RelocModel::Pic, + code_model: Some(CodeModel::Medium), + tls_model: TlsModel::LocalExec, + max_atomic_width: Some(64), + llvm_abiname: "lp64d".into(), + ..super::hermit_base::opts() + }, + } +} diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 14618f89aedb..bf46bd2f8a23 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -122,6 +122,7 @@ static TARGETS: &[&str] = &[ "riscv32imac-unknown-none-elf", "riscv32gc-unknown-linux-gnu", "riscv64imac-unknown-none-elf", + "riscv64gc-unknown-hermit", "riscv64gc-unknown-none-elf", "riscv64gc-unknown-linux-gnu", "s390x-unknown-linux-gnu", From ab4d65e11d14ca05264ffae7d5ca4adb12505532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sun, 23 Jul 2023 10:30:13 +0200 Subject: [PATCH 056/105] compiler: Add `*-unknown-hermit` documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 5 +- src/doc/rustc/src/platform-support/hermit.md | 75 ++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/doc/rustc/src/platform-support/hermit.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 96ee9a586dee..e5af8526bfc8 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -42,6 +42,7 @@ - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) + - [*-unknown-hermit](platform-support/hermit.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - [*-unknown-openbsd](platform-support/openbsd.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 351b03b9337b..959f61741925 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -223,7 +223,7 @@ target | std | host | notes [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | ARM64 OpenHarmony | [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD -`aarch64-unknown-hermit` | ✓ | | ARM64 HermitCore +[`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) [`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD @@ -303,6 +303,7 @@ target | std | host | notes [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF +[`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia `riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0) @@ -327,7 +328,7 @@ target | std | host | notes `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku -`x86_64-unknown-hermit` | ✓ | | HermitCore +[`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit `x86_64-unknown-l4re-uclibc` | ? | | [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | x86_64 OpenHarmony | [`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD diff --git a/src/doc/rustc/src/platform-support/hermit.md b/src/doc/rustc/src/platform-support/hermit.md new file mode 100644 index 000000000000..146079e36f44 --- /dev/null +++ b/src/doc/rustc/src/platform-support/hermit.md @@ -0,0 +1,75 @@ +# `*-unknown-hermit` + +**Tier: 3** + +The [Hermit] unikernel target allows compiling your applications into self-contained, specialized unikernel images that can be run in small virtual machines. + +[Hermit]: https://github.com/hermitcore + +Target triplets available so far: + +- `x86_64-unknown-hermit` +- `aarch64-unknown-hermit` +- `riscv64gc-unknown-hermit` + +## Target maintainers + +- Stefan Lankes ([@stlankes](https://github.com/stlankes)) +- Martin Kröning ([@mkroening](https://github.com/mkroening)) + +## Requirements + +These targets only support cross-compilation. +The targets do support std. + +When building binaries for this target, the Hermit unikernel is built from scratch. +The application developer themselves specializes the target and sets corresponding expectations. + +The Hermit targets follow Linux's `extern "C"` calling convention. + +Hermit binaries have the ELF format. + +## Building the target + +You can build Rust with support for the targets by adding it to the `target` list in `config.toml`. +To run the Hermit build scripts, you also have to enable your host target. +The build scripts rely on `llvm-tools` and binaries are linked using `rust-lld`, so those have to be enabled as well. + +```toml +[build] +build-stage = 1 +target = [ + "", + "x86_64-unknown-hermit", + "aarch64-unknown-hermit", + "riscv64gc-unknown-hermit", +] + +[rust] +lld = true +llvm-tools = true +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for these targets. +To compile for these targets, you will either need to build Rust with the targets enabled +(see “Building the targets” above), or build your own copy of `core` by using `build-std` or similar. + +Building Rust programs can be done by following the tutorial in our starter application [rusty-demo]. + +[rusty-demo]: https://github.com/hermitcore/rusty-demo + +## Testing + +The targets support running binaries in the form of self-contained unikernel images. +These images can be chainloaded by Hermit's [loader] or hypervisor ([Uhyve]). +QEMU can be used to boot Hermit binaries using the loader on any architecture. +The targets do not support running the Rust test suite. + +[loader]: https://github.com/hermitcore/rusty-loader +[Uhyve]: https://github.com/hermitcore/uhyve + +## Cross-compilation toolchains and C code + +The targets do not yet support C code and Rust code at the same time. From b787fc0ea61fd0c666037a48239691603a2a9446 Mon Sep 17 00:00:00 2001 From: "Jonathan Pallant (Ferrous Systems)" Date: Mon, 17 Jul 2023 13:22:29 +0100 Subject: [PATCH 057/105] Remove redundant note. This came from x86_64-unknown-none and doesn't make sense here. --- .../rustc/src/platform-support/sparc-unknown-none-elf.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md index efd58e8302fd..3e59717dfd93 100644 --- a/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md @@ -17,13 +17,6 @@ Rust for bare-metal 32-bit SPARC V7 and V8 systems, e.g. the Gaisler LEON3. This target is cross-compiled. There is no support for `std`. There is no default allocator, but it's possible to use `alloc` by supplying an allocator. -This allows the generated code to run in environments, such as kernels, which -may need to avoid the use of such registers or which may have special -considerations about the use of such registers (e.g. saving and restoring them -to avoid breaking userspace code using the same registers). You can change code -generation to use additional CPU features via the `-C target-feature=` codegen -options to rustc, or via the `#[target_feature]` mechanism within Rust code. - By default, code generated with this target should run on any `SPARC` hardware; enabling additional target features may raise this baseline. From 34e01d5f32bf4467bb030db8fd6a151755b4fda2 Mon Sep 17 00:00:00 2001 From: "Jonathan Pallant (Ferrous Systems)" Date: Mon, 17 Jul 2023 13:49:06 +0100 Subject: [PATCH 058/105] Add clarification about build-std and using newer instructions. --- .../sparc-unknown-none-elf.md | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md index 3e59717dfd93..f579b1fb8d48 100644 --- a/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md @@ -39,20 +39,31 @@ list in `config.toml`: ```toml [build] build-stage = 1 -target = ["sparc-unknown-none-elf"] +host = [""] +target = ["", "sparc-unknown-none-elf"] ``` +Replace `` with `x86_64-unknown-linux-gnu` or whatever +else is appropriate for your host machine. + ## Building Rust programs -```text +To build with this target, pass it to the `--target` argument, like: + +```console cargo build --target sparc-unknown-none-elf ``` This target uses GCC as a linker, and so you will need an appropriate GCC -compatible `sparc-unknown-none` toolchain. +compatible `sparc-unknown-none` toolchain. The default linker binary is +`sparc-elf-gcc`, but you can override this in your project configuration, as +follows: -The default linker name is `sparc-elf-gcc`, but you can override this in your -project configuration. +`.cargo/config.toml`: +```toml +[target.sparc-unknown-none-elf] +linker = "sparc-custom-elf-gcc" +``` ## Testing @@ -74,6 +85,26 @@ something like: linker = "sparc-gaisler-elf-gcc" runner = "tsim-leon3" +[build] +target = ["sparc-unknown-none-elf"] +rustflags = "-Ctarget-cpu=leon3" +``` + +With this configuration, running `cargo run` will compile your code for the +SPARC V8 compatible Gaisler Leon3 processor and then start the `tsim-leon3` +simulator. The `libcore` was pre-compiled as part of the `rustc` compilation +process using the SPARC V7 baseline, but if you are using a nightly toolchain +you can use the +[`-Z build-std=core`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std) +option to rebuild `libcore` from source. This may be useful if you want to +compile it for SPARC V8 and take advantage of the extra instructions. + +`.cargo/config.toml`: +```toml +[target.sparc-unknown-none-elf] +linker = "sparc-gaisler-elf-gcc" +runner = "tsim-leon3" + [build] target = ["sparc-unknown-none-elf"] rustflags = "-Ctarget-cpu=leon3" @@ -82,16 +113,16 @@ rustflags = "-Ctarget-cpu=leon3" build-std = ["core"] ``` -With this configuration, running `cargo run` will compile your code for the -SPARC V8 compatible Gaisler Leon3 processor and then start the `tsim-leon3` -simulator. Once the simulator is running, simply enter the command -`run` to start the code executing in the simulator. +Either way, once the simulator is running, simply enter the command `run` to +start the code executing in the simulator. The default C toolchain libraries are linked in, so with the Gaisler [BCC2] toolchain, and using its default Leon3 BSP, you can use call the C `putchar` -function and friends to output to the simulator console. +function and friends to output to the simulator console. The default linker +script is also appropriate for the Leon3 simulator, so no linker script is +required. -Here's a complete example: +Here's a complete example using the above config file: ```rust,ignore (cannot-test-this-because-it-assumes-special-libc-functions) #![no_std] From 8df39667dcc802a152ca0fe0b85e51a7b6788b82 Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Thu, 20 Jul 2023 16:47:42 +0100 Subject: [PATCH 059/105] new unstable option: -Zwrite-long-types-to-disk This option guards the logic of writing long type names in files and instead using short forms in error messages in rustc_middle/ty/error behind a flag. The main motivation for this change is to disable this behaviour when running ui tests. This logic can be triggered by running tests in a directory that has a long enough path, e.g. /my/very-long-path/where/rust-codebase/exists/ This means ui tests can fail depending on how long the path to their file is. Some ui tests actually rely on this behaviour for their assertions, so for those we enable the flag manually. --- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_middle/src/ty/error.rs | 9 +++++++-- compiler/rustc_session/src/options.rs | 2 ++ src/tools/compiletest/src/runtest.rs | 1 + tests/ui/diagnostic-width/E0271.stderr | 3 +-- tests/ui/diagnostic-width/long-E0308.rs | 2 +- tests/ui/error-codes/E0271.rs | 1 + tests/ui/error-codes/E0271.stderr | 6 +++--- tests/ui/error-codes/E0275.rs | 1 + tests/ui/error-codes/E0275.stderr | 4 ++-- .../trait-bounds/hang-on-deeply-nested-dyn.rs | 1 + .../hang-on-deeply-nested-dyn.stderr | 2 +- .../trait-bounds/issue-30786.stderr | 1 - tests/ui/issues/issue-20413.rs | 1 + tests/ui/issues/issue-20413.stderr | 18 +++++++++--------- tests/ui/issues/issue-23122-2.rs | 1 + tests/ui/issues/issue-23122-2.stderr | 4 ++-- .../issue-37311.rs | 1 + .../issue-37311.stderr | 4 ++-- tests/ui/methods/inherent-bound-in-probe.rs | 1 + .../ui/methods/inherent-bound-in-probe.stderr | 8 ++++---- tests/ui/recursion/issue-83150.rs | 2 +- tests/ui/regions/issue-102374.rs | 1 + tests/ui/regions/issue-102374.stderr | 2 +- .../traits/issue-91949-hangs-on-recursion.rs | 2 +- 25 files changed, 47 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 09141afd1370..20171f72c410 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -738,6 +738,7 @@ fn test_unstable_options_tracking_hash() { untracked!(unstable_options, true); untracked!(validate_mir, true); untracked!(verbose, true); + untracked!(write_long_types_to_disk, true); // tidy-alphabetical-end macro_rules! tracked { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index c794c3faded1..3fdbaf5e9477 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -339,12 +339,17 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option) { - let width = self.sess.diagnostic_width(); - let length_limit = width.saturating_sub(30); let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS) .pretty_print_type(ty) .expect("could not write to `String`") .into_buffer(); + + if !self.sess.opts.unstable_opts.write_long_types_to_disk { + return (regular, None); + } + + let width = self.sess.diagnostic_width(); + let length_limit = width.saturating_sub(30); if regular.len() <= width { return (regular, None); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 87d67c099ced..a317cc2c1345 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1875,6 +1875,8 @@ written to standard error output)"), Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), + write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], + "whether long type names should be written to files instead of being printed in errors"), // tidy-alphabetical-end // If you add a new option, please update: diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ac19fe078f09..2b4c1183ee7a 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2330,6 +2330,7 @@ impl<'test> TestCx<'test> { // Hide line numbers to reduce churn rustc.arg("-Zui-testing"); rustc.arg("-Zdeduplicate-diagnostics=no"); + rustc.arg("-Zwrite-long-types-to-disk=no"); // FIXME: use this for other modes too, for perf? rustc.arg("-Cstrip=debuginfo"); } diff --git a/tests/ui/diagnostic-width/E0271.stderr b/tests/ui/diagnostic-width/E0271.stderr index 52f415037d35..c1b8b32071ce 100644 --- a/tests/ui/diagnostic-width/E0271.stderr +++ b/tests/ui/diagnostic-width/E0271.stderr @@ -15,8 +15,7 @@ note: expected this to be `Foo` | LL | type Error = E; | ^ - = note: required for the cast from `Box>` to `Box<(dyn Future + 'static)>` - = note: the full name for the source type has been written to '$TEST_BUILD_DIR/diagnostic-width/E0271/E0271.long-type-hash.txt' + = note: required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>` error: aborting due to previous error diff --git a/tests/ui/diagnostic-width/long-E0308.rs b/tests/ui/diagnostic-width/long-E0308.rs index f021f1029334..0ae5e19ab2af 100644 --- a/tests/ui/diagnostic-width/long-E0308.rs +++ b/tests/ui/diagnostic-width/long-E0308.rs @@ -1,4 +1,4 @@ -// compile-flags: --diagnostic-width=60 +// compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" mod a { diff --git a/tests/ui/error-codes/E0271.rs b/tests/ui/error-codes/E0271.rs index f27190754348..ec6b8b5532e6 100644 --- a/tests/ui/error-codes/E0271.rs +++ b/tests/ui/error-codes/E0271.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zwrite-long-types-to-disk=yes trait Trait { type AssociatedType; } fn foo(t: T) where T: Trait { diff --git a/tests/ui/error-codes/E0271.stderr b/tests/ui/error-codes/E0271.stderr index 1e2f4383459e..98b5aa83da61 100644 --- a/tests/ui/error-codes/E0271.stderr +++ b/tests/ui/error-codes/E0271.stderr @@ -1,5 +1,5 @@ error[E0271]: type mismatch resolving `::AssociatedType == u32` - --> $DIR/E0271.rs:10:9 + --> $DIR/E0271.rs:11:9 | LL | foo(3_i8); | --- ^^^^ type mismatch resolving `::AssociatedType == u32` @@ -7,12 +7,12 @@ LL | foo(3_i8); | required by a bound introduced by this call | note: expected this to be `u32` - --> $DIR/E0271.rs:7:43 + --> $DIR/E0271.rs:8:43 | LL | impl Trait for i8 { type AssociatedType = &'static str; } | ^^^^^^^^^^^^ note: required by a bound in `foo` - --> $DIR/E0271.rs:3:32 + --> $DIR/E0271.rs:4:32 | LL | fn foo(t: T) where T: Trait { | ^^^^^^^^^^^^^^^^^^ required by this bound in `foo` diff --git a/tests/ui/error-codes/E0275.rs b/tests/ui/error-codes/E0275.rs index 95d7f85f1054..5119544e29fe 100644 --- a/tests/ui/error-codes/E0275.rs +++ b/tests/ui/error-codes/E0275.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" trait Foo {} diff --git a/tests/ui/error-codes/E0275.stderr b/tests/ui/error-codes/E0275.stderr index 03c37d6f0e1d..1ddb058d0fd1 100644 --- a/tests/ui/error-codes/E0275.stderr +++ b/tests/ui/error-codes/E0275.stderr @@ -1,12 +1,12 @@ error[E0275]: overflow evaluating the requirement `Bar>>>>>>: Foo` - --> $DIR/E0275.rs:6:33 + --> $DIR/E0275.rs:7:33 | LL | impl Foo for T where Bar: Foo {} | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`E0275`) note: required for `Bar>>>>>>>>>>>>>>>>>>>>` to implement `Foo` - --> $DIR/E0275.rs:6:9 + --> $DIR/E0275.rs:7:9 | LL | impl Foo for T where Bar: Foo {} | ^^^ ^ --- unsatisfied trait bound introduced here diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs index d34b7a29623c..3ee2669a15d1 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" fn id( diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr index a9d649b82852..f162806cca3d 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/hang-on-deeply-nested-dyn.rs:12:5 + --> $DIR/hang-on-deeply-nested-dyn.rs:13:5 | LL | ) -> &dyn Fn( | ______- diff --git a/tests/ui/higher-ranked/trait-bounds/issue-30786.stderr b/tests/ui/higher-ranked/trait-bounds/issue-30786.stderr index f32ba57200d5..957082b6010c 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-30786.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-30786.stderr @@ -31,7 +31,6 @@ LL | pub struct Filter { LL | let count = filter.countx(); | ^^^^^^ method cannot be called due to unsatisfied trait bounds | - = note: the full type name has been written to '$TEST_BUILD_DIR/higher-ranked/trait-bounds/issue-30786/issue-30786.long-type-hash.txt' note: the following trait bounds were not satisfied: `&'a mut &Filter fn(&'a u64) -> &'a u64 {identity::}>, [closure@$DIR/issue-30786.rs:131:30: 131:37]>: Stream` `&'a mut &mut Filter fn(&'a u64) -> &'a u64 {identity::}>, [closure@$DIR/issue-30786.rs:131:30: 131:37]>: Stream` diff --git a/tests/ui/issues/issue-20413.rs b/tests/ui/issues/issue-20413.rs index 4de22f0c9177..64d8cdc3e6f1 100644 --- a/tests/ui/issues/issue-20413.rs +++ b/tests/ui/issues/issue-20413.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" trait Foo { fn answer(self); diff --git a/tests/ui/issues/issue-20413.stderr b/tests/ui/issues/issue-20413.stderr index 8891a26784e4..8d660478a31e 100644 --- a/tests/ui/issues/issue-20413.stderr +++ b/tests/ui/issues/issue-20413.stderr @@ -1,5 +1,5 @@ error[E0392]: parameter `T` is never used - --> $DIR/issue-20413.rs:6:15 + --> $DIR/issue-20413.rs:7:15 | LL | struct NoData; | ^ unused parameter @@ -8,14 +8,14 @@ LL | struct NoData; = help: if you intended `T` to be a const parameter, use `const T: usize` instead error[E0275]: overflow evaluating the requirement `NoData>>>>>>: Foo` - --> $DIR/issue-20413.rs:9:36 + --> $DIR/issue-20413.rs:10:36 | LL | impl Foo for T where NoData: Foo { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) note: required for `NoData>>>>>>>>>>>>` to implement `Foo` - --> $DIR/issue-20413.rs:9:9 + --> $DIR/issue-20413.rs:10:9 | LL | impl Foo for T where NoData: Foo { | ^^^ ^ --- unsatisfied trait bound introduced here @@ -24,20 +24,20 @@ LL | impl Foo for T where NoData: Foo { = note: required for `NoData` to implement `Foo` error[E0275]: overflow evaluating the requirement `AlmostNoData>>>>>>: Bar` - --> $DIR/issue-20413.rs:28:42 + --> $DIR/issue-20413.rs:29:42 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) note: required for `EvenLessData>>>>>>` to implement `Baz` - --> $DIR/issue-20413.rs:35:9 + --> $DIR/issue-20413.rs:36:9 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ ^ --- unsatisfied trait bound introduced here = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt' note: required for `AlmostNoData>>>>>>` to implement `Bar` - --> $DIR/issue-20413.rs:28:9 + --> $DIR/issue-20413.rs:29:9 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ ^ --- unsatisfied trait bound introduced here @@ -46,20 +46,20 @@ LL | impl Bar for T where EvenLessData: Baz { = note: required for `EvenLessData` to implement `Baz` error[E0275]: overflow evaluating the requirement `EvenLessData>>>>>>: Baz` - --> $DIR/issue-20413.rs:35:42 + --> $DIR/issue-20413.rs:36:42 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) note: required for `AlmostNoData>>>>>>` to implement `Bar` - --> $DIR/issue-20413.rs:28:9 + --> $DIR/issue-20413.rs:29:9 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ ^ --- unsatisfied trait bound introduced here = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt' note: required for `EvenLessData>>>>>>` to implement `Baz` - --> $DIR/issue-20413.rs:35:9 + --> $DIR/issue-20413.rs:36:9 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ ^ --- unsatisfied trait bound introduced here diff --git a/tests/ui/issues/issue-23122-2.rs b/tests/ui/issues/issue-23122-2.rs index 338789c2e871..06cd17ad1ead 100644 --- a/tests/ui/issues/issue-23122-2.rs +++ b/tests/ui/issues/issue-23122-2.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" trait Next { type Next: Next; diff --git a/tests/ui/issues/issue-23122-2.stderr b/tests/ui/issues/issue-23122-2.stderr index 06e5b711a822..fe6b44de85b3 100644 --- a/tests/ui/issues/issue-23122-2.stderr +++ b/tests/ui/issues/issue-23122-2.stderr @@ -1,12 +1,12 @@ error[E0275]: overflow evaluating the requirement `<<<<<<<... as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` - --> $DIR/issue-23122-2.rs:11:17 + --> $DIR/issue-23122-2.rs:12:17 | LL | type Next = as Next>::Next; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`) note: required for `GetNext<<<<<<<... as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` to implement `Next` - --> $DIR/issue-23122-2.rs:10:15 + --> $DIR/issue-23122-2.rs:11:15 | LL | impl Next for GetNext { | - ^^^^ ^^^^^^^^^^ diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs index c109be005238..372a641c5338 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: ".nll/" -> "/" // ignore-compare-mode-next-solver (hangs) diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 87832dd29b28..494da0ffa66d 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,11 +1,11 @@ error: reached the recursion limit while instantiating `<(&(&(..., ...), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:18:9 + --> $DIR/issue-37311.rs:19:9 | LL | (self, self).recurse(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `::recurse` defined here - --> $DIR/issue-37311.rs:17:5 + --> $DIR/issue-37311.rs:18:5 | LL | fn recurse(&self) { | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/methods/inherent-bound-in-probe.rs b/tests/ui/methods/inherent-bound-in-probe.rs index 81a99ca010e5..26be90202e68 100644 --- a/tests/ui/methods/inherent-bound-in-probe.rs +++ b/tests/ui/methods/inherent-bound-in-probe.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" // Fixes #110131 diff --git a/tests/ui/methods/inherent-bound-in-probe.stderr b/tests/ui/methods/inherent-bound-in-probe.stderr index ff03a7edb054..7632373eafe6 100644 --- a/tests/ui/methods/inherent-bound-in-probe.stderr +++ b/tests/ui/methods/inherent-bound-in-probe.stderr @@ -1,5 +1,5 @@ error[E0277]: `Helper<'a, T>` is not an iterator - --> $DIR/inherent-bound-in-probe.rs:40:21 + --> $DIR/inherent-bound-in-probe.rs:41:21 | LL | type IntoIter = Helper<'a, T>; | ^^^^^^^^^^^^^ `Helper<'a, T>` is not an iterator @@ -9,14 +9,14 @@ note: required by a bound in `std::iter::IntoIterator::IntoIter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL error[E0275]: overflow evaluating the requirement `&_: IntoIterator` - --> $DIR/inherent-bound-in-probe.rs:44:17 + --> $DIR/inherent-bound-in-probe.rs:45:17 | LL | Helper::new(&self.0) | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_bound_in_probe`) note: required for `&BitReaderWrapper<_>` to implement `IntoIterator` - --> $DIR/inherent-bound-in-probe.rs:34:13 + --> $DIR/inherent-bound-in-probe.rs:35:13 | LL | impl<'a, T> IntoIterator for &'a BitReaderWrapper | ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | &'a T: IntoIterator, = note: required for `&BitReaderWrapper>>>>` to implement `IntoIterator` = note: the full type name has been written to '$TEST_BUILD_DIR/methods/inherent-bound-in-probe/inherent-bound-in-probe.long-type-hash.txt' note: required by a bound in `Helper<'a, T>` - --> $DIR/inherent-bound-in-probe.rs:25:25 + --> $DIR/inherent-bound-in-probe.rs:26:25 | LL | &'a T: IntoIterator, | ^^^^^^^^^^^^^ required by this bound in `Helper<'a, T>` diff --git a/tests/ui/recursion/issue-83150.rs b/tests/ui/recursion/issue-83150.rs index 75dcdc59f21c..67c3780ba20d 100644 --- a/tests/ui/recursion/issue-83150.rs +++ b/tests/ui/recursion/issue-83150.rs @@ -1,5 +1,5 @@ // build-fail -// compile-flags: -Copt-level=0 +// compile-flags: -Copt-level=0 -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" //~^^^ ERROR overflow evaluating the requirement // ignore-compare-mode-next-solver (hangs) diff --git a/tests/ui/regions/issue-102374.rs b/tests/ui/regions/issue-102374.rs index fd71248d9cb4..c8c22c48af1e 100644 --- a/tests/ui/regions/issue-102374.rs +++ b/tests/ui/regions/issue-102374.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" use std::cell::Cell; diff --git a/tests/ui/regions/issue-102374.stderr b/tests/ui/regions/issue-102374.stderr index af64b6867bb0..7a09ba05239d 100644 --- a/tests/ui/regions/issue-102374.stderr +++ b/tests/ui/regions/issue-102374.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-102374.rs:17:5 + --> $DIR/issue-102374.rs:18:5 | LL | ) -> i32 { | --- expected `i32` because of return type diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.rs b/tests/ui/traits/issue-91949-hangs-on-recursion.rs index 312d5d08c7df..4a18ae5053f9 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.rs +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.rs @@ -1,5 +1,5 @@ // build-fail -// compile-flags: -Zinline-mir=no +// compile-flags: -Zinline-mir=no -Zwrite-long-types-to-disk=yes // error-pattern: overflow evaluating the requirement ` as Iterator>::Item == ()` // error-pattern: function cannot return without recursing // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" From 01f3cc127227e607c2699fcd5fb2d17f1217a0cd Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 24 Jul 2023 21:15:11 +1000 Subject: [PATCH 060/105] coverage: Obtain the `__llvm_covfun` section name outside a per-function loop This section name is always constant for a given target, but obtaining it from LLVM requires a few intermediate allocations. There's no need to do so repeatedly from inside a per-function loop. --- .../src/coverageinfo/mapgen.rs | 11 +++++++- .../src/coverageinfo/mod.rs | 28 ++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 6e815ab3ff51..9149b06886b3 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -100,9 +100,11 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // Generate the LLVM IR representation of the coverage map and store it in a well-known global let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val); + let covfun_section_name = coverageinfo::covfun_section_name(cx); for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data { save_function_record( cx, + &covfun_section_name, mangled_function_name, source_hash, filenames_ref, @@ -228,6 +230,7 @@ impl CoverageMapGenerator { /// specific, well-known section and name. fn save_function_record( cx: &CodegenCx<'_, '_>, + covfun_section_name: &str, mangled_function_name: &str, source_hash: u64, filenames_ref: u64, @@ -254,7 +257,13 @@ fn save_function_record( /*packed=*/ true, ); - coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used); + coverageinfo::save_func_record_to_mod( + cx, + covfun_section_name, + func_name_hash, + func_record_val, + is_used, + ); } /// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 1f8f40339313..60cbb3d3d9d6 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -408,6 +408,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, + covfun_section_name: &str, func_name_hash: u64, func_record_val: &'ll llvm::Value, is_used: bool, @@ -423,20 +424,33 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( let func_record_var_name = format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }); debug!("function record var name: {:?}", func_record_var_name); - - let func_record_section_name = llvm::build_string(|s| unsafe { - llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage function record section name failed UTF-8 conversion"); - debug!("function record section name: {:?}", func_record_section_name); + debug!("function record section name: {:?}", covfun_section_name); let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name); llvm::set_initializer(llglobal, func_record_val); llvm::set_global_constant(llglobal, true); llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); llvm::set_visibility(llglobal, llvm::Visibility::Hidden); - llvm::set_section(llglobal, &func_record_section_name); + llvm::set_section(llglobal, covfun_section_name); llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); cx.add_used_global(llglobal); } + +/// Returns the section name string to pass through to the linker when embedding +/// per-function coverage information in the object file, according to the target +/// platform's object file format. +/// +/// LLVM's coverage tools read coverage mapping details from this section when +/// producing coverage reports. +/// +/// Typical values are: +/// - `__llvm_covfun` on Linux +/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix) +/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix) +pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String { + llvm::build_string(|s| unsafe { + llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s); + }) + .expect("Rust Coverage function record section name failed UTF-8 conversion") +} From a593de4fab309968d305f9c6812c2203d4431464 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 23 Jul 2023 21:35:54 +0200 Subject: [PATCH 061/105] interpret: support projecting into Place::Local without force_allocation --- compiler/rustc_const_eval/messages.ftl | 2 - compiler/rustc_const_eval/src/errors.rs | 4 +- .../src/interpret/eval_context.rs | 5 +- .../rustc_const_eval/src/interpret/operand.rs | 109 ++++-- .../rustc_const_eval/src/interpret/place.rs | 225 ++++++++---- .../src/interpret/projection.rs | 326 +++++++++--------- .../src/interpret/terminator.rs | 8 +- .../rustc_const_eval/src/interpret/visitor.rs | 18 +- .../rustc_middle/src/mir/interpret/error.rs | 5 +- .../src/borrow_tracker/tree_borrows/mod.rs | 2 +- src/tools/miri/src/helpers.rs | 4 +- tests/ui/consts/const-eval/ub-ref-ptr.stderr | 2 +- 12 files changed, 431 insertions(+), 279 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index d8eade5bd2a0..86e47b2dbce0 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -423,8 +423,6 @@ const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but e const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str` -const_eval_uninit_unsized_local = - unsized local is used while uninitialized const_eval_unreachable = entering unreachable code const_eval_unreachable_unwind = unwinding past a stack frame that does not allow unwinding diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index ca38cce710e6..201b9425aff2 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -835,7 +835,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { rustc_middle::error::middle_adjust_for_foreign_abi_error } InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized, - InvalidProgramInfo::UninitUnsizedLocal => const_eval_uninit_unsized_local, + InvalidProgramInfo::ConstPropNonsense => panic!("We had const-prop nonsense, this should never be printed"), } } fn add_args( @@ -846,7 +846,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { match self { InvalidProgramInfo::TooGeneric | InvalidProgramInfo::AlreadyReported(_) - | InvalidProgramInfo::UninitUnsizedLocal => {} + | InvalidProgramInfo::ConstPropNonsense => {} InvalidProgramInfo::Layout(e) => { let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler); for (name, val) in diag.args() { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 04e046fbda35..0e6125388a60 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1014,9 +1014,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.place { - Place::Local { frame, local } => { + Place::Local { frame, local, offset } => { let mut allocs = Vec::new(); write!(fmt, "{:?}", local)?; + if let Some(offset) = offset { + write!(fmt, "+{:#x}", offset.bytes())?; + } if frame != self.ecx.frame_idx() { write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?; } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 47383c0eabc3..e6d0a73c521e 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -13,7 +13,7 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size}; use super::{ alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, - InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer, + InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, Provenance, Scalar, }; @@ -240,37 +240,69 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { let int = self.to_scalar().assert_int(); ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral()) } + + /// Compute the "sub-immediate" that is located within the `base` at the given offset with the + /// given layout. + pub(super) fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> Self { + // This makes several assumptions about what layouts we will encounter; we match what + // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). + let inner_val: Immediate<_> = match (**self, self.layout.abi) { + // if the entire value is uninit, then so is the field (can happen in ConstProp) + (Immediate::Uninit, _) => Immediate::Uninit, + // the field contains no information, can be left uninit + _ if layout.is_zst() => Immediate::Uninit, + // the field covers the entire type + _ if layout.size == self.layout.size => { + assert!(match (self.layout.abi, layout.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + _ => false, + }); + assert!(offset.bytes() == 0); + **self + } + // extract fields from types with `ScalarPair` ABI + (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { + assert!(matches!(layout.abi, Abi::Scalar(..))); + Immediate::from(if offset.bytes() == 0 { + debug_assert_eq!(layout.size, a.size(cx)); + a_val + } else { + debug_assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi)); + debug_assert_eq!(layout.size, b.size(cx)); + b_val + }) + } + // everything else is a bug + _ => bug!("invalid field access on immediate {}, layout {:#?}", self, self.layout), + }; + + ImmTy::from_immediate(inner_val, layout) + } } impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { - pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - if self.layout.is_unsized() { - if matches!(self.op, Operand::Immediate(Immediate::Uninit)) { - // Uninit unsized places shouldn't occur. In the interpreter we have them - // temporarily for unsized arguments before their value is put in; in ConstProp they - // remain uninit and this code can actually be reached. - throw_inval!(UninitUnsizedLocal); + pub(super) fn meta(&self) -> InterpResult<'tcx, MemPlaceMeta> { + Ok(if self.layout.is_unsized() { + if matches!(self.op, Operand::Immediate(_)) { + // Unsized immediate OpTy cannot occur. We create a MemPlace for all unsized locals during argument passing. + // However, ConstProp doesn't do that, so we can run into this nonsense situation. + throw_inval!(ConstPropNonsense); } // There are no unsized immediates. - self.assert_mem_place().len(cx) + self.assert_mem_place().meta } else { - match self.layout.fields { - abi::FieldsShape::Array { count, .. } => Ok(count), - _ => bug!("len not supported on sized type {:?}", self.layout.ty), - } - } + MemPlaceMeta::None + }) } - /// Replace the layout of this operand. There's basically no sanity check that this makes sense, - /// you better know what you are doing! If this is an immediate, applying the wrong layout can - /// not just lead to invalid data, it can actually *shift the data around* since the offsets of - /// a ScalarPair are entirely determined by the layout, not the data. - pub fn transmute(&self, layout: TyAndLayout<'tcx>) -> Self { - assert_eq!( - self.layout.size, layout.size, - "transmuting with a size change, that doesn't seem right" - ); - OpTy { layout, ..*self } + pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { + self.meta()?.len(self.layout, cx) } /// Offset the operand in memory (if possible) and change its metadata. @@ -286,13 +318,9 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { match self.as_mplace_or_imm() { Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()), Right(imm) => { - assert!( - matches!(*imm, Immediate::Uninit), - "Scalar/ScalarPair cannot be offset into" - ); assert!(!meta.has_meta()); // no place to store metadata here // Every part of an uninit is uninit. - Ok(ImmTy::uninit(layout).into()) + Ok(imm.offset(offset, layout, cx).into()) } } } @@ -502,13 +530,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let op = match **place { - Place::Ptr(mplace) => Operand::Indirect(mplace), - Place::Local { frame, local } => { - *self.local_to_op(&self.stack()[frame], local, None)? + match place.as_mplace_or_local() { + Left(mplace) => Ok(mplace.into()), + Right((frame, local, offset)) => { + let base = self.local_to_op(&self.stack()[frame], local, None)?; + let mut field = if let Some(offset) = offset { + // This got offset. We can be sure that the field is sized. + base.offset(offset, place.layout, self)? + } else { + assert_eq!(place.layout, base.layout); + // Unsized cases are possible here since an unsized local will be a + // `Place::Local` until the first projection calls `place_to_op` to extract the + // underlying mplace. + base + }; + field.align = Some(place.align); + Ok(field) } - }; - Ok(OpTy { op, layout: place.layout, align: Some(place.align) }) + } } /// Evaluate a place with the goal of reading from it. This lets us sometimes diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index a9b2b43f1e6f..dd07c5fa8776 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -2,11 +2,14 @@ //! into a place. //! All high-level functions to write to memory work on places as destinations. +use std::assert_matches::assert_matches; + use either::{Either, Left, Right}; use rustc_ast::Mutability; use rustc_index::IndexSlice; use rustc_middle::mir; +use rustc_middle::mir::interpret::PointerArithmetic; use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; @@ -44,6 +47,27 @@ impl MemPlaceMeta { Self::None => false, } } + + pub(crate) fn len<'tcx>( + &self, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, u64> { + if layout.is_unsized() { + // We need to consult `meta` metadata + match layout.ty.kind() { + ty::Slice(..) | ty::Str => self.unwrap_meta().to_target_usize(cx), + _ => bug!("len not supported on unsized type {:?}", layout.ty), + } + } else { + // Go through the layout. There are lots of types that support a length, + // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) + match layout.fields { + abi::FieldsShape::Array { count, .. } => Ok(count), + _ => bug!("len not supported on sized type {:?}", layout.ty), + } + } + } } #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -73,9 +97,13 @@ pub enum Place { /// A place referring to a value allocated in the `Memory` system. Ptr(MemPlace), - /// To support alloc-free locals, we are able to write directly to a local. + /// To support alloc-free locals, we are able to write directly to a local. The offset indicates + /// where in the local this place is located; if it is `None`, no projection has been applied. + /// Such projections are meaningful even if the offset is 0, since they can change layouts. /// (Without that optimization, we'd just always be a `MemPlace`.) - Local { frame: usize, local: mir::Local }, + /// Note that this only stores the frame index, not the thread this frame belongs to -- that is + /// implicit. This means a `Place` must never be moved across interpreter thread boundaries! + Local { frame: usize, local: mir::Local, offset: Option }, } #[derive(Clone, Debug)] @@ -132,6 +160,11 @@ impl MemPlace { MemPlace { ptr, meta: MemPlaceMeta::None } } + #[inline(always)] + pub fn from_ptr_with_meta(ptr: Pointer>, meta: MemPlaceMeta) -> Self { + MemPlace { ptr, meta } + } + /// Adjust the provenance of the main pointer (metadata is unaffected). pub fn map_provenance(self, f: impl FnOnce(Option) -> Option) -> Self { MemPlace { ptr: self.ptr.map_provenance(f), ..self } @@ -150,7 +183,7 @@ impl MemPlace { } #[inline] - pub(super) fn offset_with_meta<'tcx>( + fn offset_with_meta<'tcx>( self, offset: Size, meta: MemPlaceMeta, @@ -162,18 +195,10 @@ impl MemPlace { ); Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta }) } -} -impl Place { - /// Asserts that this points to some local variable. - /// Returns the frame idx and the variable idx. #[inline] - #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn assert_local(&self) -> (usize, mir::Local) { - match self { - Place::Local { frame, local } => (*frame, *local), - _ => bug!("assert_local: expected Place::Local, got {:?}", self), - } + fn offset<'tcx>(&self, offset: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { + self.offset_with_meta(offset, MemPlaceMeta::None, cx) } } @@ -231,28 +256,16 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { layout: TyAndLayout<'tcx>, meta: MemPlaceMeta, ) -> Self { - let mut mplace = MemPlace::from_ptr(ptr); - mplace.meta = meta; - - MPlaceTy { mplace, layout, align: layout.align.abi } + MPlaceTy { + mplace: MemPlace::from_ptr_with_meta(ptr, meta), + layout, + align: layout.align.abi, + } } #[inline] pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - if self.layout.is_unsized() { - // We need to consult `meta` metadata - match self.layout.ty.kind() { - ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_target_usize(cx), - _ => bug!("len not supported on unsized type {:?}", self.layout.ty), - } - } else { - // Go through the layout. There are lots of types that support a length, - // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) - match self.layout.fields { - abi::FieldsShape::Array { count, .. } => Ok(count), - _ => bug!("len not supported on sized type {:?}", self.layout.ty), - } - } + self.mplace.meta.len(self.layout, cx) } } @@ -283,10 +296,12 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { /// A place is either an mplace or some local. #[inline] - pub fn as_mplace_or_local(&self) -> Either, (usize, mir::Local)> { + pub fn as_mplace_or_local( + &self, + ) -> Either, (usize, mir::Local, Option)> { match **self { Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }), - Place::Local { frame, local } => Right((frame, local)), + Place::Local { frame, local, offset } => Right((frame, local, offset)), } } @@ -300,6 +315,49 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { ) }) } + + /// Offset the place in memory and change its metadata. + /// + /// This can go wrong very easily if you give the wrong layout for the new place! + pub(crate) fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + Ok(match self.as_mplace_or_local() { + Left(mplace) => mplace.offset_with_meta(offset, meta, layout, cx)?.into(), + Right((frame, local, old_offset)) => { + assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway... + let new_offset = cx + .data_layout() + .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?; + PlaceTy { + place: Place::Local { + frame, + local, + offset: Some(Size::from_bytes(new_offset)), + }, + align: self.align.restrict_for_offset(offset), + layout, + } + } + }) + } + + /// Offset the place in memory. + /// + /// This can go wrong very easily if you give the wrong layout for the new place! + pub fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert!(layout.is_sized()); + self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + } } // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 @@ -308,6 +366,20 @@ where Prov: Provenance + 'static, M: Machine<'mir, 'tcx, Provenance = Prov>, { + /// Get the metadata of the given place. + pub(super) fn place_meta( + &self, + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MemPlaceMeta> { + if place.layout.is_unsized() { + // For `Place::Local`, the metadata is stored with the local, not the place. So we have + // to look that up first. + self.place_to_op(place)?.meta() + } else { + Ok(MemPlaceMeta::None) + } + } + /// Take a value, which represents a (thin or wide) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. /// @@ -327,11 +399,9 @@ where Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; - let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta }; // `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced; // we hence can't call `size_and_align_of` since that asserts more validity than we want. - let align = layout.align.abi; - Ok(MPlaceTy { mplace, layout, align }) + Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta)) } /// Take an operand, representing a pointer, and dereference it to a place. @@ -422,7 +492,7 @@ where local: mir::Local, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let layout = self.layout_of_local(&self.stack()[frame], local, None)?; - let place = Place::Local { frame, local }; + let place = Place::Local { frame, local, offset: None }; Ok(PlaceTy { place, layout, align: layout.align.abi }) } @@ -430,7 +500,7 @@ where /// place; for reading, a more efficient alternative is `eval_place_to_op`. #[instrument(skip(self), level = "debug")] pub fn eval_place( - &mut self, + &self, mir_place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; @@ -509,16 +579,23 @@ where // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, // but not factored as a separate function. let mplace = match dest.place { - Place::Local { frame, local } => { - match M::access_local_mut(self, frame, local)? { - Operand::Immediate(local) => { - // Local can be updated in-place. - *local = src; - return Ok(()); - } - Operand::Indirect(mplace) => { - // The local is in memory, go on below. - *mplace + Place::Local { frame, local, offset } => { + if offset.is_some() { + // This has been projected to a part of this local. We could have complicated + // logic to still keep this local as an `Operand`... but it's much easier to + // just fall back to the indirect path. + *self.force_allocation(dest)? + } else { + match M::access_local_mut(self, frame, local)? { + Operand::Immediate(local) => { + // Local can be updated in-place. + *local = src; + return Ok(()); + } + Operand::Indirect(mplace) => { + // The local is in memory, go on below. + *mplace + } } } } @@ -593,15 +670,23 @@ where pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { let mplace = match dest.as_mplace_or_local() { Left(mplace) => mplace, - Right((frame, local)) => { - match M::access_local_mut(self, frame, local)? { - Operand::Immediate(local) => { - *local = Immediate::Uninit; - return Ok(()); - } - Operand::Indirect(mplace) => { - // The local is in memory, go on below. - MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align } + Right((frame, local, offset)) => { + if offset.is_some() { + // This has been projected to a part of this local. We could have complicated + // logic to still keep this local as an `Operand`... but it's much easier to + // just fall back to the indirect path. + // FIXME: share the logic with `write_immediate_no_validate`. + self.force_allocation(dest)? + } else { + match M::access_local_mut(self, frame, local)? { + Operand::Immediate(local) => { + *local = Immediate::Uninit; + return Ok(()); + } + Operand::Indirect(mplace) => { + // The local is in memory, go on below. + MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align } + } } } } @@ -728,8 +813,8 @@ where place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let mplace = match place.place { - Place::Local { frame, local } => { - match M::access_local_mut(self, frame, local)? { + Place::Local { frame, local, offset } => { + let whole_local = match M::access_local_mut(self, frame, local)? { &mut Operand::Immediate(local_val) => { // We need to make an allocation. @@ -742,10 +827,11 @@ where throw_unsup_format!("unsized locals are not supported"); } let mplace = *self.allocate(local_layout, MemoryKind::Stack)?; + // Preserve old value. (As an optimization, we can skip this if it was uninit.) if !matches!(local_val, Immediate::Uninit) { - // Preserve old value. (As an optimization, we can skip this if it was uninit.) - // We don't have to validate as we can assume the local - // was already valid for its type. + // We don't have to validate as we can assume the local was already + // valid for its type. We must not use any part of `place` here, that + // could be a projection to a part of the local! self.write_immediate_to_mplace_no_validate( local_val, local_layout, @@ -753,18 +839,25 @@ where mplace, )?; } - // Now we can call `access_mut` again, asserting it goes well, - // and actually overwrite things. + // Now we can call `access_mut` again, asserting it goes well, and actually + // overwrite things. This points to the entire allocation, not just the part + // the place refers to, i.e. we do this before we apply `offset`. *M::access_local_mut(self, frame, local).unwrap() = Operand::Indirect(mplace); mplace } &mut Operand::Indirect(mplace) => mplace, // this already was an indirect local + }; + if let Some(offset) = offset { + whole_local.offset(offset, self)? + } else { + // Preserve wide place metadata, do not call `offset`. + whole_local } } Place::Ptr(mplace) => mplace, }; - // Return with the original layout, so that the caller can go on + // Return with the original layout and align, so that the caller can go on Ok(MPlaceTy { mplace, layout: place.layout, align: place.align }) } @@ -874,7 +967,7 @@ where let vtable = self.read_pointer(&vtable)?; let (ty, _) = self.get_ptr_vtable(vtable)?; let layout = self.layout_of(ty)?; - let data = data.transmute(layout); + let data = data.offset(Size::ZERO, layout, self)?; Ok((data, vtable)) } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index d7d31fe18875..5a3f19e5dc9b 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -7,17 +7,15 @@ //! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually //! implement the logic on OpTy, and MPlaceTy calls that. -use either::{Left, Right}; - use rustc_middle::mir; use rustc_middle::ty; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; -use rustc_target::abi::{self, Abi, VariantIdx}; +use rustc_target::abi::Size; +use rustc_target::abi::{self, VariantIdx}; use super::{ - ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy, - Provenance, Scalar, + InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy, Provenance, Scalar, }; // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 @@ -28,6 +26,43 @@ where { //# Field access + fn project_field( + &self, + base_layout: TyAndLayout<'tcx>, + base_meta: MemPlaceMeta, + field: usize, + ) -> InterpResult<'tcx, (Size, MemPlaceMeta, TyAndLayout<'tcx>)> { + let offset = base_layout.fields.offset(field); + let field_layout = base_layout.field(self, field); + + // Offset may need adjustment for unsized fields. + let (meta, offset) = if field_layout.is_unsized() { + if base_layout.is_sized() { + // An unsized field of a sized type? Sure... + // But const-prop actually feeds us such nonsense MIR! + throw_inval!(ConstPropNonsense); + } + // Re-use parent metadata to determine dynamic field layout. + // With custom DSTS, this *will* execute user-defined code, but the same + // happens at run-time so that's okay. + match self.size_and_align_of(&base_meta, &field_layout)? { + Some((_, align)) => (base_meta, offset.align_to(align)), + None => { + // For unsized types with an extern type tail we perform no adjustments. + // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. + assert!(matches!(base_meta, MemPlaceMeta::None)); + (base_meta, offset) + } + } + } else { + // base_meta could be present; we might be accessing a sized field of an unsized + // struct. + (MemPlaceMeta::None, offset) + }; + + Ok((offset, meta, field_layout)) + } + /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is /// always possible without allocating, so it can take `&self`. Also return the field's layout. /// This supports both struct and array fields. @@ -39,28 +74,7 @@ where base: &MPlaceTy<'tcx, M::Provenance>, field: usize, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let offset = base.layout.fields.offset(field); - let field_layout = base.layout.field(self, field); - - // Offset may need adjustment for unsized fields. - let (meta, offset) = if field_layout.is_unsized() { - // Re-use parent metadata to determine dynamic field layout. - // With custom DSTS, this *will* execute user-defined code, but the same - // happens at run-time so that's okay. - match self.size_and_align_of(&base.meta, &field_layout)? { - Some((_, align)) => (base.meta, offset.align_to(align)), - None => { - // For unsized types with an extern type tail we perform no adjustments. - // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. - assert!(matches!(base.meta, MemPlaceMeta::None)); - (base.meta, offset) - } - } - } else { - // base.meta could be present; we might be accessing a sized field of an unsized - // struct. - (MemPlaceMeta::None, offset) - }; + let (offset, meta, field_layout) = self.project_field(base.layout, base.meta, field)?; // We do not look at `base.layout.align` nor `field_layout.align`, unlike // codegen -- mostly to see if we can get away with that @@ -68,18 +82,14 @@ where } /// Gets the place of a field inside the place, and also the field's type. - /// Just a convenience function, but used quite a bit. - /// This is the only projection that might have a side-effect: We cannot project - /// into the field of a local `ScalarPair`, we have to first allocate it. pub fn place_field( - &mut self, + &self, base: &PlaceTy<'tcx, M::Provenance>, field: usize, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - // FIXME: We could try to be smarter and avoid allocation for fields that span the - // entire place. - let base = self.force_allocation(base)?; - Ok(self.mplace_field(&base, field)?.into()) + let (offset, meta, field_layout) = + self.project_field(base.layout, self.place_meta(base)?, field)?; + base.offset_with_meta(offset, meta, field_layout, self) } pub fn operand_field( @@ -87,56 +97,8 @@ where base: &OpTy<'tcx, M::Provenance>, field: usize, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let base = match base.as_mplace_or_imm() { - Left(ref mplace) => { - // We can reuse the mplace field computation logic for indirect operands. - let field = self.mplace_field(mplace, field)?; - return Ok(field.into()); - } - Right(value) => value, - }; - - let field_layout = base.layout.field(self, field); - let offset = base.layout.fields.offset(field); - // This makes several assumptions about what layouts we will encounter; we match what - // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). - let field_val: Immediate<_> = match (*base, base.layout.abi) { - // if the entire value is uninit, then so is the field (can happen in ConstProp) - (Immediate::Uninit, _) => Immediate::Uninit, - // the field contains no information, can be left uninit - _ if field_layout.is_zst() => Immediate::Uninit, - // the field covers the entire type - _ if field_layout.size == base.layout.size => { - assert!(match (base.layout.abi, field_layout.abi) { - (Abi::Scalar(..), Abi::Scalar(..)) => true, - (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, - _ => false, - }); - assert!(offset.bytes() == 0); - *base - } - // extract fields from types with `ScalarPair` ABI - (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert!(matches!(field_layout.abi, Abi::Scalar(..))); - Immediate::from(if offset.bytes() == 0 { - debug_assert_eq!(field_layout.size, a.size(self)); - a_val - } else { - debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); - debug_assert_eq!(field_layout.size, b.size(self)); - b_val - }) - } - // everything else is a bug - _ => span_bug!( - self.cur_span(), - "invalid field access on immediate {}, layout {:#?}", - base, - base.layout - ), - }; - - Ok(ImmTy::from_immediate(field_val, field_layout).into()) + let (offset, meta, field_layout) = self.project_field(base.layout, base.meta()?, field)?; + base.offset_with_meta(offset, meta, field_layout, self) } //# Downcasting @@ -177,7 +139,36 @@ where Ok(base) } - //# Slice indexing + //# Slice and array indexing + + /// Compute the offset and field layout for accessing the given index. + fn project_index( + &self, + base_layout: TyAndLayout<'tcx>, + base_meta: MemPlaceMeta, + index: u64, + ) -> InterpResult<'tcx, (Size, TyAndLayout<'tcx>)> { + // Not using the layout method because we want to compute on u64 + match base_layout.fields { + abi::FieldsShape::Array { stride, count: _ } => { + // `count` is nonsense for slices, use the dynamic length instead. + let len = base_meta.len(base_layout, self)?; + if index >= len { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len, index }); + } + let offset = stride * index; // `Size` multiplication + // All fields have the same layout. + let field_layout = base_layout.field(self, 0); + Ok((offset, field_layout)) + } + _ => span_bug!( + self.cur_span(), + "`mplace_index` called on non-array type {:?}", + base_layout.ty + ), + } + } #[inline(always)] pub fn operand_index( @@ -185,42 +176,8 @@ where base: &OpTy<'tcx, M::Provenance>, index: u64, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // Not using the layout method because we want to compute on u64 - match base.layout.fields { - abi::FieldsShape::Array { stride, count: _ } => { - // `count` is nonsense for slices, use the dynamic length instead. - let len = base.len(self)?; - if index >= len { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len, index }); - } - let offset = stride * index; // `Size` multiplication - // All fields have the same layout. - let field_layout = base.layout.field(self, 0); - base.offset(offset, field_layout, self) - } - _ => span_bug!( - self.cur_span(), - "`mplace_index` called on non-array type {:?}", - base.layout.ty - ), - } - } - - /// Iterates over all fields of an array. Much more efficient than doing the - /// same by repeatedly calling `operand_index`. - pub fn operand_array_fields<'a>( - &self, - base: &'a OpTy<'tcx, Prov>, - ) -> InterpResult<'tcx, impl Iterator>> + 'a> { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { - span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); - }; - let field_layout = base.layout.field(self, 0); - let dl = &self.tcx.data_layout; - // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) + let (offset, field_layout) = self.project_index(base.layout, base.meta()?, index)?; + base.offset(offset, field_layout, self) } /// Index into an array. @@ -229,31 +186,63 @@ where base: &MPlaceTy<'tcx, M::Provenance>, index: u64, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - Ok(self.operand_index(&base.into(), index)?.assert_mem_place()) + let (offset, field_layout) = self.project_index(base.layout, base.meta, index)?; + base.offset(offset, field_layout, self) } pub fn place_index( - &mut self, + &self, base: &PlaceTy<'tcx, M::Provenance>, index: u64, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - // There's not a lot we can do here, since we cannot have a place to a part of a local. If - // we are accessing the only element of a 1-element array, it's still the entire local... - // that doesn't seem worth it. - let base = self.force_allocation(base)?; - Ok(self.mplace_index(&base, index)?.into()) + let (offset, field_layout) = + self.project_index(base.layout, self.place_meta(base)?, index)?; + base.offset(offset, field_layout, self) + } + + /// Iterates over all fields of an array. Much more efficient than doing the + /// same by repeatedly calling `operand_index`. + pub fn operand_array_fields<'a>( + &self, + base: &'a OpTy<'tcx, Prov>, + ) -> InterpResult<'tcx, impl Iterator>> + 'a> { + let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { + span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); + }; + let len = base.len(self)?; + let field_layout = base.layout.field(self, 0); + let dl = &self.tcx.data_layout; + // `Size` multiplication + Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) + } + + /// Iterates over all fields of an array. Much more efficient than doing the + /// same by repeatedly calling `place_index`. + pub fn place_array_fields<'a>( + &self, + base: &'a PlaceTy<'tcx, Prov>, + ) -> InterpResult<'tcx, impl Iterator>> + 'a> { + let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { + span_bug!(self.cur_span(), "place_array_fields: expected an array layout"); + }; + let len = self.place_meta(base)?.len(base.layout, self)?; + let field_layout = base.layout.field(self, 0); + let dl = &self.tcx.data_layout; + // `Size` multiplication + Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) } //# ConstantIndex support - fn operand_constant_index( + fn project_constant_index( &self, - base: &OpTy<'tcx, M::Provenance>, + base_layout: TyAndLayout<'tcx>, + base_meta: MemPlaceMeta, offset: u64, min_length: u64, from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let n = base.len(self)?; + ) -> InterpResult<'tcx, (Size, TyAndLayout<'tcx>)> { + let n = base_meta.len(base_layout, self)?; if n < min_length { // This can only be reached in ConstProp and non-rustc-MIR. throw_ub!(BoundsCheckFailed { len: min_length, index: n }); @@ -267,33 +256,49 @@ where offset }; - self.operand_index(base, index) + self.project_index(base_layout, base_meta, index) + } + + fn operand_constant_index( + &self, + base: &OpTy<'tcx, M::Provenance>, + offset: u64, + min_length: u64, + from_end: bool, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let (offset, layout) = + self.project_constant_index(base.layout, base.meta()?, offset, min_length, from_end)?; + base.offset(offset, layout, self) } fn place_constant_index( - &mut self, + &self, base: &PlaceTy<'tcx, M::Provenance>, offset: u64, min_length: u64, from_end: bool, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let base = self.force_allocation(base)?; - Ok(self - .operand_constant_index(&base.into(), offset, min_length, from_end)? - .assert_mem_place() - .into()) + let (offset, layout) = self.project_constant_index( + base.layout, + self.place_meta(base)?, + offset, + min_length, + from_end, + )?; + base.offset(offset, layout, self) } //# Subslicing - fn operand_subslice( + fn project_subslice( &self, - base: &OpTy<'tcx, M::Provenance>, + base_layout: TyAndLayout<'tcx>, + base_meta: MemPlaceMeta, from: u64, to: u64, from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let len = base.len(self)?; // also asserts that we have a type where this makes sense + ) -> InterpResult<'tcx, (Size, MemPlaceMeta, TyAndLayout<'tcx>)> { + let len = base_meta.len(base_layout, self)?; // also asserts that we have a type where this makes sense let actual_to = if from_end { if from.checked_add(to).map_or(true, |to| to > len) { // This can only be reached in ConstProp and non-rustc-MIR. @@ -306,16 +311,16 @@ where // Not using layout method because that works with usize, and does not work with slices // (that have count 0 in their layout). - let from_offset = match base.layout.fields { + let from_offset = match base_layout.fields { abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked _ => { - span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) + span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base_layout) } }; // Compute meta and new layout let inner_len = actual_to.checked_sub(from).unwrap(); - let (meta, ty) = match base.layout.ty.kind() { + let (meta, ty) = match base_layout.ty.kind() { // It is not nice to match on the type, but that seems to be the only way to // implement this. ty::Array(inner, _) => { @@ -323,25 +328,38 @@ where } ty::Slice(..) => { let len = Scalar::from_target_usize(inner_len, self); - (MemPlaceMeta::Meta(len), base.layout.ty) + (MemPlaceMeta::Meta(len), base_layout.ty) } _ => { - span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) + span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base_layout.ty) } }; let layout = self.layout_of(ty)?; + Ok((from_offset, meta, layout)) + } + + fn operand_subslice( + &self, + base: &OpTy<'tcx, M::Provenance>, + from: u64, + to: u64, + from_end: bool, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let (from_offset, meta, layout) = + self.project_subslice(base.layout, base.meta()?, from, to, from_end)?; base.offset_with_meta(from_offset, meta, layout, self) } pub fn place_subslice( - &mut self, + &self, base: &PlaceTy<'tcx, M::Provenance>, from: u64, to: u64, from_end: bool, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let base = self.force_allocation(base)?; - Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into()) + let (from_offset, meta, layout) = + self.project_subslice(base.layout, self.place_meta(base)?, from, to, from_end)?; + base.offset_with_meta(from_offset, meta, layout, self) } //# Applying a general projection @@ -349,7 +367,7 @@ where /// Projects into a place. #[instrument(skip(self), level = "trace")] pub fn place_projection( - &mut self, + &self, base: &PlaceTy<'tcx, M::Provenance>, proj_elem: mir::PlaceElem<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 7964c6be008c..f5e385703300 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -60,7 +60,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } pub fn fn_arg_field( - &mut self, + &self, arg: &FnArg<'tcx, M::Provenance>, field: usize, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { @@ -239,7 +239,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Evaluate the arguments of a function call pub(super) fn eval_fn_call_arguments( - &mut self, + &self, ops: &[mir::Operand<'tcx>], ) -> InterpResult<'tcx, Vec>> { ops.iter() @@ -382,12 +382,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // This all has to be in memory, there are no immediate unsized values. let src = caller_arg_copy.assert_mem_place(); // The destination cannot be one of these "spread args". - let (dest_frame, dest_local) = callee_arg.assert_local(); + let (dest_frame, dest_local, dest_offset) = + callee_arg.as_mplace_or_local().right().expect("calee fn arguments must be locals"); // We are just initializing things, so there can't be anything here yet. assert!(matches!( *self.local_to_op(&self.stack()[dest_frame], dest_local, None)?, Operand::Immediate(Immediate::Uninit) )); + assert_eq!(dest_offset, None); // Allocate enough memory to hold `src`. let Some((size, align)) = self.size_and_align_of_mplace(&src)? else { span_bug!( diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 879ae198f7e5..8d7c1953abe4 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -78,14 +78,14 @@ pub trait ValueMut<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { /// Projects to the given enum variant. fn project_downcast( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, variant: VariantIdx, ) -> InterpResult<'tcx, Self>; /// Projects to the n-th field. fn project_field( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, field: usize, ) -> InterpResult<'tcx, Self>; } @@ -165,7 +165,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> #[inline(always)] fn project_downcast( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, variant: VariantIdx, ) -> InterpResult<'tcx, Self> { ecx.operand_downcast(self, variant) @@ -174,7 +174,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> #[inline(always)] fn project_field( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, field: usize, ) -> InterpResult<'tcx, Self> { ecx.operand_field(self, field) @@ -255,7 +255,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> #[inline(always)] fn project_downcast( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, variant: VariantIdx, ) -> InterpResult<'tcx, Self> { ecx.mplace_downcast(self, variant) @@ -264,7 +264,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> #[inline(always)] fn project_field( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, field: usize, ) -> InterpResult<'tcx, Self> { ecx.mplace_field(self, field) @@ -306,7 +306,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> #[inline(always)] fn project_downcast( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, variant: VariantIdx, ) -> InterpResult<'tcx, Self> { ecx.place_downcast(self, variant) @@ -315,7 +315,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> #[inline(always)] fn project_field( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'mir, 'tcx, M>, field: usize, ) -> InterpResult<'tcx, Self> { ecx.place_field(self, field) @@ -508,7 +508,7 @@ macro_rules! make_value_visitor { } FieldsShape::Array { .. } => { // Let's get an mplace (or immediate) first. - // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway. + // FIXME: This might `force_allocate` if `v` is a `PlaceTy`! let op = v.to_op_for_proj(self.ecx())?; // Now we can go over all the fields. // This uses the *run-time length*, i.e., if we are a slice, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 372452ea29a8..c4803448e2bf 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -191,9 +191,8 @@ pub enum InvalidProgramInfo<'tcx> { FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError), /// SizeOf of unsized type was requested. SizeOfUnsizedType(Ty<'tcx>), - /// An unsized local was accessed without having been initialized. - /// This is not meaningful as we can't even have backing memory for such locals. - UninitUnsizedLocal, + /// We are runnning into a nonsense situation due to ConstProp violating our invariants. + ConstPropNonsense, } /// Details of why a pointer had to be in-bounds. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 2afd45829bd8..e7e8fa27f82f 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -578,7 +578,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// I.e. input is what you get from the visitor upon encountering an `adt` that is `Unique`, /// and output can be used by `retag_ptr_inplace`. fn inner_ptr_of_unique<'tcx>( - ecx: &mut MiriInterpCx<'_, 'tcx>, + ecx: &MiriInterpCx<'_, 'tcx>, place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> { // Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index c19d691ede6a..ab143cff7fa9 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -301,8 +301,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Get the `Place` for a local - fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> { - let this = self.eval_context_mut(); + fn local_place(&self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> { + let this = self.eval_context_ref(); let place = mir::Place { local, projection: List::empty() }; this.eval_place(place) } diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index 1d19dfff50b3..d1644f8a4dc1 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -141,7 +141,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:59:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered alloc41, but expected a function pointer + | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered alloc39, but expected a function pointer | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { From 10d0ff975cead8a533a729734ad4918d943b69e7 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 21 Jul 2023 14:28:22 +0000 Subject: [PATCH 062/105] Explain what the heck is going on with this lifetime remapping business --- compiler/rustc_hir_analysis/src/check/check.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c3e052605491..727eeafc4913 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -504,12 +504,18 @@ fn sanity_check_found_hidden_type<'tcx>( _ => l, }, }); - // Get the hidden type, and in case it is in a nested opaque type, find that opaque type's - // usage in the function signature and use the generic arguments from the usage site. + // Get the hidden type. let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args); + // In case it is in a nested opaque type, find that opaque type's + // usage in the function signature and use the generic arguments from the usage site. + // We need to do because RPITs ignore the lifetimes of the function, + // as they have their own copies of all the lifetimes they capture. + // So the only way to get the lifetimes represented in terms of the function, + // is to look how they are used in the function signature (or do some other fancy + // recording of this mapping at ast -> hir lowering time). if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { if hidden_ty != ty.ty { - hidden_ty = find_and_apply_rpit_substs( + hidden_ty = find_and_apply_rpit_args( tcx, hidden_ty, defining_use_anchor.to_def_id(), @@ -517,6 +523,7 @@ fn sanity_check_found_hidden_type<'tcx>( )?; } } + // If the hidden types differ, emit a type mismatch diagnostic. if hidden_ty == ty.ty { Ok(()) @@ -527,13 +534,13 @@ fn sanity_check_found_hidden_type<'tcx>( } } -fn find_and_apply_rpit_substs<'tcx>( +fn find_and_apply_rpit_args<'tcx>( tcx: TyCtxt<'tcx>, mut hidden_ty: Ty<'tcx>, function: DefId, opaque: DefId, ) -> Result, ErrorGuaranteed> { - // Find use of the RPIT in the function signature and thus find the right substs to + // Find use of the RPIT in the function signature and thus find the right args to // convert it into the parameter space of the function signature. This is needed, // because that's what `type_of` returns, against which we compare later. let ret = tcx.fn_sig(function).instantiate_identity().output(); From c06a7eb2a6dcbc7649de76472f6845e30f2fb97f Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 24 Jul 2023 15:04:29 +0100 Subject: [PATCH 063/105] builtin_macros: expect raw strings too `expr_to_string` allows raw strings through so this code should be expected to handle those. Signed-off-by: David Wood --- compiler/rustc_builtin_macros/src/env.rs | 2 +- tests/ui/macros/builtin-env-issue-114010.rs | 6 ++++++ tests/ui/macros/builtin-env-issue-114010.stderr | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/ui/macros/builtin-env-issue-114010.rs create mode 100644 tests/ui/macros/builtin-env-issue-114010.stderr diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index bcff475f6267..30d74ba593c9 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -84,7 +84,7 @@ pub fn expand_env<'cx>( // Use the string literal in the code in the diagnostic to avoid confusing diagnostics, // e.g. when the literal contains escape sequences. let ast::ExprKind::Lit(ast::token::Lit { - kind: ast::token::LitKind::Str, + kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..), symbol: original_var, .. }) = &var_expr.kind diff --git a/tests/ui/macros/builtin-env-issue-114010.rs b/tests/ui/macros/builtin-env-issue-114010.rs new file mode 100644 index 000000000000..eb97c767fa2e --- /dev/null +++ b/tests/ui/macros/builtin-env-issue-114010.rs @@ -0,0 +1,6 @@ +// unset-rustc-env:oopsie + +env![r#"oopsie"#]; +//~^ ERROR environment variable `oopsie` not defined at compile time + +fn main() {} diff --git a/tests/ui/macros/builtin-env-issue-114010.stderr b/tests/ui/macros/builtin-env-issue-114010.stderr new file mode 100644 index 000000000000..7934ce1b7909 --- /dev/null +++ b/tests/ui/macros/builtin-env-issue-114010.stderr @@ -0,0 +1,11 @@ +error: environment variable `oopsie` not defined at compile time + --> $DIR/builtin-env-issue-114010.rs:3:1 + | +LL | env![r#"oopsie"#]; + | ^^^^^^^^^^^^^^^^^ + | + = help: use `std::env::var("oopsie")` to read the variable at run time + = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + From 5b4549dd13778c6546ccb9d89c32a8fe9eb3b7b7 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 14:01:19 +0000 Subject: [PATCH 064/105] Some documentation nits --- .../rustc_hir_analysis/src/check/check.rs | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 727eeafc4913..4379d7b77b11 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -506,13 +506,6 @@ fn sanity_check_found_hidden_type<'tcx>( }); // Get the hidden type. let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args); - // In case it is in a nested opaque type, find that opaque type's - // usage in the function signature and use the generic arguments from the usage site. - // We need to do because RPITs ignore the lifetimes of the function, - // as they have their own copies of all the lifetimes they capture. - // So the only way to get the lifetimes represented in terms of the function, - // is to look how they are used in the function signature (or do some other fancy - // recording of this mapping at ast -> hir lowering time). if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { if hidden_ty != ty.ty { hidden_ty = find_and_apply_rpit_args( @@ -534,6 +527,36 @@ fn sanity_check_found_hidden_type<'tcx>( } } +/// In case it is in a nested opaque type, find that opaque type's +/// usage in the function signature and use the generic arguments from the usage site. +/// We need to do because RPITs ignore the lifetimes of the function, +/// as they have their own copies of all the lifetimes they capture. +/// So the only way to get the lifetimes represented in terms of the function, +/// is to look how they are used in the function signature (or do some other fancy +/// recording of this mapping at ast -> hir lowering time). +/// +/// As an example: +/// ```text +/// trait Id { +/// type Assoc; +/// } +/// impl<'a> Id for &'a () { +/// type Assoc = &'a (); +/// } +/// fn func<'a>(x: &'a ()) -> impl Id { x } +/// // desugared to +/// +/// // hidden type is `&'bDup ()` +/// // During wfcheck the hidden type of `Inner` is `&'a ()`, but +/// // `typeof(Inner<'b, 'bDup>) = &'bDup ()`. +/// // So we walk the signature of `func` to find the use of `Inner<'static, 'a>` +/// // and then use that to replace the lifetimes in the hidden type, obtaining +/// // `&'a ()`. +/// type Outer<'b, 'bDup> = impl Id>; +/// // hidden type is `&'cDup ()` +/// type Inner<'c, 'cDup> = impl Sized + 'cDup; +/// fn func<'a>(x: &'a () -> Outer<'static, 'a> { x } +/// ``` fn find_and_apply_rpit_args<'tcx>( tcx: TyCtxt<'tcx>, mut hidden_ty: Ty<'tcx>, From 3911a63b7777e19dad4043542f908018e70c0bdd Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Mon, 24 Jul 2023 23:37:35 +0900 Subject: [PATCH 065/105] Hide `ToString` implementations that specialize the default ones The status quo is highly confusing, since the overlap is not apparent, and specialization is not a feature of Rust. This addresses #87545; I'm not certain if it closes it, since that issue might also be trackign a *general* solution for hiding specializing impls automatically. --- library/alloc/src/string.rs | 9 +++++++++ library/proc_macro/src/lib.rs | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 0019f51b3d70..9254786d1295 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2527,6 +2527,7 @@ impl ToString for T { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[unstable(feature = "ascii_char", issue = "110998")] impl ToString for core::ascii::Char { @@ -2536,6 +2537,7 @@ impl ToString for core::ascii::Char { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "char_to_string_specialization", since = "1.46.0")] impl ToString for char { @@ -2545,6 +2547,7 @@ impl ToString for char { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "bool_to_string_specialization", since = "1.68.0")] impl ToString for bool { @@ -2554,6 +2557,7 @@ impl ToString for bool { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "u8_to_string_specialization", since = "1.54.0")] impl ToString for u8 { @@ -2574,6 +2578,7 @@ impl ToString for u8 { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "i8_to_string_specialization", since = "1.54.0")] impl ToString for i8 { @@ -2597,6 +2602,7 @@ impl ToString for i8 { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "str_to_string_specialization", since = "1.9.0")] impl ToString for str { @@ -2606,6 +2612,7 @@ impl ToString for str { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "cow_str_to_string_specialization", since = "1.17.0")] impl ToString for Cow<'_, str> { @@ -2615,6 +2622,7 @@ impl ToString for Cow<'_, str> { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "string_to_string_specialization", since = "1.17.0")] impl ToString for String { @@ -2624,6 +2632,7 @@ impl ToString for String { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "fmt_arguments_to_string_specialization", since = "1.71.0")] impl ToString for fmt::Arguments<'_> { diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 7fb0d989cdba..2c0d113dbc58 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -177,6 +177,7 @@ impl FromStr for TokenStream { // N.B., the bridge only provides `to_string`, implement `fmt::Display` // based on it (the reverse of the usual relationship between the two). +#[doc(hidden)] #[stable(feature = "proc_macro_lib", since = "1.15.0")] impl ToString for TokenStream { fn to_string(&self) -> String { @@ -738,6 +739,7 @@ impl From for TokenTree { // N.B., the bridge only provides `to_string`, implement `fmt::Display` // based on it (the reverse of the usual relationship between the two). +#[doc(hidden)] #[stable(feature = "proc_macro_lib", since = "1.15.0")] impl ToString for TokenTree { fn to_string(&self) -> String { @@ -872,6 +874,7 @@ impl Group { // N.B., the bridge only provides `to_string`, implement `fmt::Display` // based on it (the reverse of the usual relationship between the two). +#[doc(hidden)] #[stable(feature = "proc_macro_lib", since = "1.15.0")] impl ToString for Group { fn to_string(&self) -> String { @@ -981,6 +984,7 @@ impl Punct { } } +#[doc(hidden)] #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl ToString for Punct { fn to_string(&self) -> String { @@ -1083,8 +1087,7 @@ impl Ident { } } -/// Converts the identifier to a string that should be losslessly convertible -/// back into the same identifier. +#[doc(hidden)] #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl ToString for Ident { fn to_string(&self) -> String { @@ -1423,6 +1426,7 @@ impl FromStr for Literal { } } +#[doc(hidden)] #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl ToString for Literal { fn to_string(&self) -> String { From 3857d9c2da1250546ccb87ab7ddcc68904f45dc5 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 24 Jul 2023 15:47:03 +0100 Subject: [PATCH 066/105] borrowck/errors: fix i18n error in delayed bug During borrowck, the `MultiSpan` from a buffered diagnostic is cloned and used to emit a delayed bug indicating a diagnostic was buffered - when the buffered diagnostic is translated, then the cloned `MultiSpan` may contain labels which can only render with the diagnostic's arguments, but the delayed bug being emitted won't have those arguments. Adds a function which clones `MultiSpan` without also cloning the contained labels, and use this function when creating the buffered diagnostic delayed bug. Signed-off-by: David Wood --- compiler/rustc_borrowck/src/lib.rs | 9 ++++----- compiler/rustc_error_messages/src/lib.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ea32506fc898..2fd42e2f56a6 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2306,11 +2306,10 @@ mod error { pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) { if let None = self.tainted_by_errors { - self.tainted_by_errors = Some( - self.tcx - .sess - .delay_span_bug(t.span.clone(), "diagnostic buffered but not emitted"), - ) + self.tainted_by_errors = Some(self.tcx.sess.delay_span_bug( + t.span.clone_ignoring_labels(), + "diagnostic buffered but not emitted", + )) } t.buffer(&mut self.buffered); } diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 1879ece59e32..a49b842d9ffe 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -533,6 +533,14 @@ impl MultiSpan { pub fn has_span_labels(&self) -> bool { self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) } + + /// Clone this `MultiSpan` without keeping any of the span labels - sometimes a `MultiSpan` is + /// to be re-used in another diagnostic, but includes `span_labels` which have translated + /// messages. These translated messages would fail to translate without their diagnostic + /// arguments which are unlikely to be cloned alongside the `Span`. + pub fn clone_ignoring_labels(&self) -> Self { + Self { primary_spans: self.primary_spans.clone(), ..MultiSpan::new() } + } } impl From for MultiSpan { From 8cab95ef9b56a90a17437fd8ac040c22535f8f4d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 24 Jul 2023 16:50:25 +0200 Subject: [PATCH 067/105] Add missing documentation for `Session::time` --- compiler/rustc_session/src/utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 1d15e2c28d83..71f2591fe665 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -7,6 +7,7 @@ impl Session { pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> { self.prof.verbose_generic_activity(what) } + /// Used by `-Z self-profile`. pub fn time(&self, what: &'static str, f: impl FnOnce() -> R) -> R { self.prof.verbose_generic_activity(what).run(f) } From 30f787800a62077ebb211391320de4aad432b4a6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 15:34:36 +0000 Subject: [PATCH 068/105] Explain RPITs in the way they actually work --- .../rustc_hir_analysis/src/check/check.rs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4379d7b77b11..96eb83f37682 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -545,17 +545,23 @@ fn sanity_check_found_hidden_type<'tcx>( /// } /// fn func<'a>(x: &'a ()) -> impl Id { x } /// // desugared to +/// fn func<'a>(x: &'a () -> Outer<'a, Assoc = Inner<'a>> { +/// // Note that in contrast to other nested items, RPIT type aliases can +/// // access their parents' generics. /// -/// // hidden type is `&'bDup ()` -/// // During wfcheck the hidden type of `Inner` is `&'a ()`, but -/// // `typeof(Inner<'b, 'bDup>) = &'bDup ()`. -/// // So we walk the signature of `func` to find the use of `Inner<'static, 'a>` -/// // and then use that to replace the lifetimes in the hidden type, obtaining -/// // `&'a ()`. -/// type Outer<'b, 'bDup> = impl Id>; -/// // hidden type is `&'cDup ()` -/// type Inner<'c, 'cDup> = impl Sized + 'cDup; -/// fn func<'a>(x: &'a () -> Outer<'static, 'a> { x } +/// // hidden type is `&'aDupOuter ()` +/// // During wfcheck the hidden type of `Inner<'aDupOuter>` is `&'a ()`, but +/// // `typeof(Inner<'aDupOuter>) = &'aDupOuter ()`. +/// // So we walk the signature of `func` to find the use of `Inner<'a>` +/// // and then use that to replace the lifetimes in the hidden type, obtaining +/// // `&'a ()`. +/// type Outer<'aDupOuter> = impl Id>; +/// +/// // hidden type is `&'aDupInner ()` +/// type Inner<'aDupInner> = impl Sized + 'aDupInner; +/// +/// x +/// } /// ``` fn find_and_apply_rpit_args<'tcx>( tcx: TyCtxt<'tcx>, From 3ad3bb64d97cb3e6b596f5c29ec87a572037d31a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 24 Jul 2023 08:37:40 -0700 Subject: [PATCH 069/105] lcnr's suggestions Co-authored-by: lcnr --- compiler/rustc_trait_selection/src/solve/alias_relate.rs | 5 +++-- compiler/rustc_trait_selection/src/solve/mod.rs | 2 +- compiler/rustc_trait_selection/src/solve/weak_types.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index c46b4df11251..a751fefc88b4 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -1,5 +1,6 @@ -//! Implements the `AliasRelate` goal, which is used to unify two aliases in the -//! new solver, which uses "lazy normalization". +//! Implements the `AliasRelate` goal, which is used when unifying aliases. +//! Doing this via a separate goal is called "deferred alias relation" and part +//! of our more general approach to "lazy normalization". //! //! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches: //! * normalizes-to: If `A` is a projection, we can prove the equivalent diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index cc59dd5df03d..63e48c94a867 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -3,7 +3,7 @@ //! As a user of rust, you can use `-Ztrait-solver=next` or `next-coherence` //! to enable the new trait solver always, or just within coherence, respectively. //! -//! As a developer of rustc, you probably shouldn't be using the new trait +//! As a developer of rustc, you shouldn't be using the new trait //! solver without asking the trait-system-refactor-initiative, but it can //! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will //! ensure that trait solving using that inference context will be routed diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs index fea634a2ad2f..54de32cf6189 100644 --- a/compiler/rustc_trait_selection/src/solve/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs @@ -2,7 +2,7 @@ //! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`. //! //! Since a weak alias is not ambiguous, this just computes the `type_of` of -//! the alias and registers any where-clause predicates on the type alias. +//! the alias and registers the where-clauses of the type alias. use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; From 637ea3f746236611fc5451cfc97c976650c20218 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 21 Jul 2023 22:31:43 +0200 Subject: [PATCH 070/105] validate `doc(masked)` --- compiler/rustc_passes/messages.ftl | 11 +++ compiler/rustc_passes/src/check_attr.rs | 49 ++++++++++++ compiler/rustc_passes/src/errors.rs | 19 +++++ tests/rustdoc-ui/lints/invalid-doc-attr.rs | 15 ++++ .../rustdoc-ui/lints/invalid-doc-attr.stderr | 49 ++++++++++-- tests/ui/attributes/invalid-doc-attr.rs | 32 -------- tests/ui/attributes/invalid-doc-attr.stderr | 78 ------------------- 7 files changed, 136 insertions(+), 117 deletions(-) delete mode 100644 tests/ui/attributes/invalid-doc-attr.rs delete mode 100644 tests/ui/attributes/invalid-doc-attr.stderr diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 0aa3d6265dce..5b3cc5d99cc7 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -211,6 +211,17 @@ passes_doc_keyword_not_mod = passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks +passes_doc_masked_not_extern_crate_self = + this attribute cannot be applied to an `extern crate self` item + .label = not applicable on `extern crate self` items + .extern_crate_self_label = `extern crate self` defined here + +passes_doc_masked_only_extern_crate = + this attribute can only be applied to an `extern crate` item + .label = only applicable on `extern crate` items + .not_an_extern_crate_label = not an `extern crate` item + .note = read for more information + passes_doc_test_literal = `#![doc(test(...)]` does not take a literal passes_doc_test_takes_list = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4d7ebe3fefef..cbb030958c69 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -878,6 +878,44 @@ impl CheckAttrVisitor<'_> { } } + fn check_doc_masked( + &self, + attr: &Attribute, + meta: &NestedMetaItem, + hir_id: HirId, + target: Target, + ) -> bool { + if target != Target::ExternCrate { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocMaskedOnlyExternCrate { + attr_span: meta.span(), + item_span: (attr.style == AttrStyle::Outer) + .then(|| self.tcx.hir().span(hir_id)), + }, + ); + return false; + } + + if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocMaskedNotExternCrateSelf { + attr_span: meta.span(), + item_span: (attr.style == AttrStyle::Outer) + .then(|| self.tcx.hir().span(hir_id)), + }, + ); + return false; + } + + true + } + /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid. fn check_attr_not_crate_level( &self, @@ -1048,6 +1086,17 @@ impl CheckAttrVisitor<'_> { is_valid = false; } + sym::masked + if !self.check_doc_masked( + attr, + meta, + hir_id, + target, + ) => + { + is_valid = false; + } + // no_default_passes: deprecated // passes: deprecated // plugins: removed, but rustdoc warns about it itself diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index eae13f86049a..4f5514372d1e 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -267,6 +267,25 @@ pub struct DocInlineOnlyUse { pub item_span: Option, } +#[derive(LintDiagnostic)] +#[diag(passes_doc_masked_only_extern_crate)] +#[note] +pub struct DocMaskedOnlyExternCrate { + #[label] + pub attr_span: Span, + #[label(passes_not_an_extern_crate_label)] + pub item_span: Option, +} + +#[derive(LintDiagnostic)] +#[diag(passes_doc_masked_not_extern_crate_self)] +pub struct DocMaskedNotExternCrateSelf { + #[label] + pub attr_span: Span, + #[label(passes_extern_crate_self_label)] + pub item_span: Option, +} + #[derive(Diagnostic)] #[diag(passes_doc_attr_not_crate_level)] pub struct DocAttrNotCrateLevel<'a> { diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.rs b/tests/rustdoc-ui/lints/invalid-doc-attr.rs index c231e43b35ca..16b12cca5a0b 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr.rs +++ b/tests/rustdoc-ui/lints/invalid-doc-attr.rs @@ -1,5 +1,10 @@ #![crate_type = "lib"] #![deny(warnings)] +#![feature(doc_masked)] + +#![doc(masked)] +//~^ ERROR this attribute can only be applied to an `extern crate` item +//~| WARN is being phased out #[doc(test(no_crate_inject))] //~^ ERROR can only be applied at the crate level @@ -30,3 +35,13 @@ pub mod bar { //~^^ ERROR conflicting doc inlining attributes //~| HELP remove one of the conflicting attributes pub use bar::baz; + +#[doc(masked)] +//~^ ERROR this attribute can only be applied to an `extern crate` item +//~| WARN is being phased out +pub struct Masked; + +#[doc(masked)] +//~^ ERROR this attribute cannot be applied to an `extern crate self` item +//~| WARN is being phased out +pub extern crate self as reexport; diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr.stderr index b23b8ded8674..82ea33e1d895 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr.stderr +++ b/tests/rustdoc-ui/lints/invalid-doc-attr.stderr @@ -1,5 +1,5 @@ error: this attribute can only be applied at the crate level - --> $DIR/invalid-doc-attr.rs:4:7 + --> $DIR/invalid-doc-attr.rs:9:7 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | #![doc(test(no_crate_inject))] | + error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:9:7 + --> $DIR/invalid-doc-attr.rs:14:7 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items @@ -32,7 +32,7 @@ LL | pub fn foo() {} = note: read for more information error: this attribute can only be applied at the crate level - --> $DIR/invalid-doc-attr.rs:15:12 + --> $DIR/invalid-doc-attr.rs:20:12 | LL | #![doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL | #![doc(test(no_crate_inject))] = note: read for more information error: conflicting doc inlining attributes - --> $DIR/invalid-doc-attr.rs:28:7 + --> $DIR/invalid-doc-attr.rs:33:7 | LL | #[doc(inline)] | ^^^^^^ this attribute... @@ -51,8 +51,43 @@ LL | #[doc(no_inline)] | = help: remove one of the conflicting attributes +error: this attribute can only be applied to an `extern crate` item + --> $DIR/invalid-doc-attr.rs:39:7 + | +LL | #[doc(masked)] + | ^^^^^^ only applicable on `extern crate` items +... +LL | pub struct Masked; + | ----------------- not an `extern crate` item + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + = note: read for more information + +error: this attribute cannot be applied to an `extern crate self` item + --> $DIR/invalid-doc-attr.rs:44:7 + | +LL | #[doc(masked)] + | ^^^^^^ not applicable on `extern crate self` items +... +LL | pub extern crate self as reexport; + | --------------------------------- `extern crate self` defined here + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: this attribute can only be applied to an `extern crate` item + --> $DIR/invalid-doc-attr.rs:5:8 + | +LL | #![doc(masked)] + | ^^^^^^ only applicable on `extern crate` items + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + = note: read for more information + error: this attribute can only be applied at the crate level - --> $DIR/invalid-doc-attr.rs:19:11 + --> $DIR/invalid-doc-attr.rs:24:11 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +97,7 @@ LL | #[doc(test(no_crate_inject))] = note: read for more information error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:22:11 + --> $DIR/invalid-doc-attr.rs:27:11 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items @@ -74,5 +109,5 @@ LL | pub fn baz() {} = note: for more information, see issue #82730 = note: read for more information -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/attributes/invalid-doc-attr.rs b/tests/ui/attributes/invalid-doc-attr.rs deleted file mode 100644 index c231e43b35ca..000000000000 --- a/tests/ui/attributes/invalid-doc-attr.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![crate_type = "lib"] -#![deny(warnings)] - -#[doc(test(no_crate_inject))] -//~^ ERROR can only be applied at the crate level -//~| WARN is being phased out -//~| HELP to apply to the crate, use an inner attribute -//~| SUGGESTION ! -#[doc(inline)] -//~^ ERROR can only be applied to a `use` item -//~| WARN is being phased out -pub fn foo() {} - -pub mod bar { - #![doc(test(no_crate_inject))] - //~^ ERROR can only be applied at the crate level - //~| WARN is being phased out - - #[doc(test(no_crate_inject))] - //~^ ERROR can only be applied at the crate level - //~| WARN is being phased out - #[doc(inline)] - //~^ ERROR can only be applied to a `use` item - //~| WARN is being phased out - pub fn baz() {} -} - -#[doc(inline)] -#[doc(no_inline)] -//~^^ ERROR conflicting doc inlining attributes -//~| HELP remove one of the conflicting attributes -pub use bar::baz; diff --git a/tests/ui/attributes/invalid-doc-attr.stderr b/tests/ui/attributes/invalid-doc-attr.stderr deleted file mode 100644 index b23b8ded8674..000000000000 --- a/tests/ui/attributes/invalid-doc-attr.stderr +++ /dev/null @@ -1,78 +0,0 @@ -error: this attribute can only be applied at the crate level - --> $DIR/invalid-doc-attr.rs:4:7 - | -LL | #[doc(test(no_crate_inject))] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82730 - = note: read for more information -note: the lint level is defined here - --> $DIR/invalid-doc-attr.rs:2:9 - | -LL | #![deny(warnings)] - | ^^^^^^^^ - = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` -help: to apply to the crate, use an inner attribute - | -LL | #![doc(test(no_crate_inject))] - | + - -error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:9:7 - | -LL | #[doc(inline)] - | ^^^^^^ only applicable on `use` items -... -LL | pub fn foo() {} - | ------------ not a `use` item - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82730 - = note: read for more information - -error: this attribute can only be applied at the crate level - --> $DIR/invalid-doc-attr.rs:15:12 - | -LL | #![doc(test(no_crate_inject))] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82730 - = note: read for more information - -error: conflicting doc inlining attributes - --> $DIR/invalid-doc-attr.rs:28:7 - | -LL | #[doc(inline)] - | ^^^^^^ this attribute... -LL | #[doc(no_inline)] - | ^^^^^^^^^ ...conflicts with this attribute - | - = help: remove one of the conflicting attributes - -error: this attribute can only be applied at the crate level - --> $DIR/invalid-doc-attr.rs:19:11 - | -LL | #[doc(test(no_crate_inject))] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82730 - = note: read for more information - -error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:22:11 - | -LL | #[doc(inline)] - | ^^^^^^ only applicable on `use` items -... -LL | pub fn baz() {} - | ------------ not a `use` item - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82730 - = note: read for more information - -error: aborting due to 6 previous errors - From bb77aa845b3a420860d05b2a1380ffe14220625c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 6 Jul 2023 11:59:14 +0200 Subject: [PATCH 071/105] compiler: Add `x86_64-unikraft-linux-musl` target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- compiler/rustc_target/src/spec/mod.rs | 3 +++ .../src/spec/unikraft_linux_musl_base.rs | 15 +++++++++++++++ .../src/spec/x86_64_unikraft_linux_musl.rs | 19 +++++++++++++++++++ src/bootstrap/lib.rs | 2 ++ src/tools/build-manifest/src/main.rs | 1 + 5 files changed, 40 insertions(+) create mode 100644 compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs create mode 100644 compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index aab31a4c8fef..7114c243ea14 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -85,6 +85,7 @@ mod solaris_base; mod solid_base; mod thumb_base; mod uefi_msvc_base; +mod unikraft_linux_musl_base; mod vxworks_base; mod wasm_base; mod windows_gnu_base; @@ -1421,6 +1422,8 @@ supported_targets! { ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl), + ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), diff --git a/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs b/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs new file mode 100644 index 000000000000..9ccd0a1e7cae --- /dev/null +++ b/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs @@ -0,0 +1,15 @@ +use crate::spec::{cvs, PanicStrategy, RelocModel, TargetOptions}; + +pub fn opts() -> TargetOptions { + TargetOptions { + os: "linux".into(), + env: "musl".into(), + vendor: "unikraft".into(), + linker: Some("kraftld".into()), + relocation_model: RelocModel::Static, + families: cvs!["unix"], + has_thread_local: true, + panic_strategy: PanicStrategy::Abort, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs new file mode 100644 index 000000000000..2aa093b131f5 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs @@ -0,0 +1,19 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "x86_64-unknown-linux-musl".into(), + pointer_width: 64, + arch: "x86_64".into(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .into(), + options: TargetOptions { + cpu: "x86-64".into(), + plt_by_default: false, + pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]), + max_atomic_width: Some(64), + stack_probes: StackProbeType::X86, + ..super::unikraft_linux_musl_base::opts() + }, + } +} diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3dd4d8d6617b..365deb8e155d 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -131,6 +131,8 @@ const EXTRA_CHECK_CFGS: &[(Option, &'static str, Option<&[&'static str]>)] (Some(Mode::Std), "freebsd13", None), (Some(Mode::Std), "backtrace_in_libstd", None), /* Extra values not defined in the built-in targets yet, but used in std */ + // #[cfg(bootstrap)] + (Some(Mode::Std), "target_vendor", Some(&["unikraft"])), (Some(Mode::Std), "target_env", Some(&["libnx"])), // (Some(Mode::Std), "target_os", Some(&[])), // #[cfg(bootstrap)] mips32r6, mips64r6 diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index bf46bd2f8a23..a02ce68903d1 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -148,6 +148,7 @@ static TARGETS: &[&str] = &[ "x86_64-pc-windows-msvc", "x86_64-sun-solaris", "x86_64-pc-solaris", + "x86_64-unikraft-linux-musl", "x86_64-unknown-freebsd", "x86_64-unknown-illumos", "x86_64-unknown-linux-gnu", From 71ad5587a1f2730053279408d1dc70da1b094a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 6 Jul 2023 12:00:41 +0200 Subject: [PATCH 072/105] compiler: Add `*-unikraft-linux-musl` documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../platform-support/unikraft-linux-musl.md | 67 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 src/doc/rustc/src/platform-support/unikraft-linux-musl.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index e5af8526bfc8..f108831138d8 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -42,6 +42,7 @@ - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) + - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - [*-unknown-hermit](platform-support/hermit.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - [*-unknown-openbsd](platform-support/openbsd.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 959f61741925..8c5dc11eeef3 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -326,6 +326,7 @@ target | std | host | notes [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | `x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos +[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku [`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit diff --git a/src/doc/rustc/src/platform-support/unikraft-linux-musl.md b/src/doc/rustc/src/platform-support/unikraft-linux-musl.md new file mode 100644 index 000000000000..90fa18b98578 --- /dev/null +++ b/src/doc/rustc/src/platform-support/unikraft-linux-musl.md @@ -0,0 +1,67 @@ +# `*-unikraft-linux-musl` + +**Tier: 3** + +Targets for the [Unikraft] Unikernel Development Kit (with musl). + +[Unikraft]: https://unikraft.org/ + +Target triplets available so far: + +- `x86_64-unikraft-linux-musl` + +## Target maintainers + +- Martin Kröning ([@mkroening](https://github.com/mkroening)) + +## Requirements + +These targets only support cross-compilation. +The targets do support std. + +Unikraft pretends to behave exactly like Linux. +How much of that functionality is available depends on the individual unikernel configuration. +For example, the basic Unikraft + musl config does not support `poll` or networking out of the box. +That functionality requires enabling [`LIBPOSIX_EVENT`] or [lwIP] respectively. + +[`LIBPOSIX_EVENT`]: https://github.com/unikraft/unikraft/blob/RELEASE-0.13.1/lib/posix-event/Config.uk +[lwIP]: https://github.com/unikraft/lib-lwip + +The Unikraft targets follow Linux's `extern "C"` calling convention. + +For these targets, `rustc` does not perform the final linking step. +Instead, the Unikraft build system will produce the final Unikernel image for the selected platform (e.g., KVM, Linux user space, and Xen). + +## Building the targets + +You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: + +```toml +[build] +build-stage = 1 +target = [ "x86_64-unikraft-linux-musl" ] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for these targets. +To compile for these targets, you will either need to build Rust with the targets enabled +(see “Building the targets” above), or build your own copy of `core` by using `build-std` or similar. + +Linking requires a [KraftKit] shim. +See [unikraft/kraftkit#612] for more information. + +[KraftKit]: https://github.com/unikraft/kraftkit +[unikraft/kraftkit#612]: https://github.com/unikraft/kraftkit/issues/612 + +## Testing + +The targets do support running binaries in the form of unikernel images. +How the unikernel image is run depends on the specific platform (e.g., KVM, Linux user space, and Xen). +The targets do not support running the Rust test suite. + +## Cross-compilation toolchains and C code + +The targets do support C code. +To build compatible C code, you have to use the same compiler and flags as does the Unikraft build system for your specific configuration. +The easiest way to achieve that, is to build the C code with the Unikraft build system when building your unikernel image. From 2b8a284d78b40da55733d56f3229274aaa6f9716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 5 Jul 2023 17:01:01 +0200 Subject: [PATCH 073/105] bootstrap: Don't bundle musl on Unikraft MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- src/bootstrap/compile.rs | 2 +- src/bootstrap/sanity.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index f3d95b57a76d..400b07b1882c 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -261,7 +261,7 @@ fn copy_self_contained_objects( // to using gcc from a glibc-targeting toolchain for linking. // To do that we have to distribute musl startup objects as a part of Rust toolchain // and link with them manually in the self-contained mode. - if target.contains("musl") { + if target.contains("musl") && !target.contains("unikraft") { let srcdir = builder.musl_libdir(target).unwrap_or_else(|| { panic!("Target {:?} does not have a \"musl-libdir\" key", target.triple) }); diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 9321fc1bcb8b..145ae6f44da3 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -206,7 +206,7 @@ than building it. } // Make sure musl-root is valid - if target.contains("musl") { + if target.contains("musl") && !target.contains("unikraft") { // If this is a native target (host is also musl) and no musl-root is given, // fall back to the system toolchain in /usr before giving up if build.musl_root(*target).is_none() && build.config.build == *target { From 7485e9c9652c8add0a10302b4a4076148f3b626c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 5 Jul 2023 12:49:35 +0200 Subject: [PATCH 074/105] `unix::init`: Handle `ENOSYS` from `poll` on Unikraft. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- library/std/src/sys/unix/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 326f1481e191..9d52f414574a 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -110,6 +110,11 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 { match errno() { libc::EINTR => continue, + #[cfg(target_vendor = "unikraft")] + libc::ENOSYS => { + // Not all configurations of Unikraft enable `LIBPOSIX_EVENT`. + break 'poll; + } libc::EINVAL | libc::EAGAIN | libc::ENOMEM => { // RLIMIT_NOFILE or temporary allocation failures // may be preventing use of poll(), fall back to fcntl From 553804754a3031cf98394d67966257196aabe724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sat, 1 Jul 2023 17:48:40 +0200 Subject: [PATCH 075/105] `unix::init`: Don't use `signal` on Unikraft. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- library/std/src/sys/unix/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 9d52f414574a..77ef086f29b5 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -170,7 +170,14 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] + #[cfg(not(any( + target_os = "emscripten", + target_os = "fuchsia", + target_os = "horizon", + // Unikraft's `signal` implementation is currently broken: + // https://github.com/unikraft/lib-musl/issues/57 + target_vendor = "unikraft", + )))] { // We don't want to add this as a public type to std, nor do we // want to `include!` a file from the compiler (which would break From cad9cd84dc5313add7ada46c3eb8cc5c857194bc Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Mon, 24 Jul 2023 12:16:48 -0500 Subject: [PATCH 076/105] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 1b15556767f4..7ac9416d82cd 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 1b15556767f4b78a64e868eedf4073c423f02b93 +Subproject commit 7ac9416d82cd4fc5e707c9ec3574d22dff6466e5 From 02f1f6a8a893bbaabc87079a61815685b0f88d75 Mon Sep 17 00:00:00 2001 From: bohan Date: Sun, 23 Jul 2023 20:21:53 +0800 Subject: [PATCH 077/105] fix(resolve): skip panic when resolution is dummy --- compiler/rustc_resolve/src/imports.rs | 5 ++++- tests/ui/imports/issue-113953.rs | 6 ++++++ tests/ui/imports/issue-113953.stderr | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/ui/imports/issue-113953.rs create mode 100644 tests/ui/imports/issue-113953.stderr diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 526fc9c3aa51..f18aedd190fd 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -990,7 +990,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }); let res = binding.res(); if let Ok(initial_res) = initial_res { - if res != initial_res && this.ambiguity_errors.is_empty() { + if res != initial_res + && this.ambiguity_errors.is_empty() + && res != Res::Err + { span_bug!(import.span, "inconsistent resolution for an import"); } } else if res != Res::Err diff --git a/tests/ui/imports/issue-113953.rs b/tests/ui/imports/issue-113953.rs new file mode 100644 index 000000000000..449a074f4b5d --- /dev/null +++ b/tests/ui/imports/issue-113953.rs @@ -0,0 +1,6 @@ +// edition: 2021 +use u8 as imported_u8; +use unresolved as u8; +//~^ ERROR unresolved import `unresolved` + +fn main() {} diff --git a/tests/ui/imports/issue-113953.stderr b/tests/ui/imports/issue-113953.stderr new file mode 100644 index 000000000000..70f91bd3c5bf --- /dev/null +++ b/tests/ui/imports/issue-113953.stderr @@ -0,0 +1,9 @@ +error[E0432]: unresolved import `unresolved` + --> $DIR/issue-113953.rs:3:5 + | +LL | use unresolved as u8; + | ^^^^^^^^^^^^^^^^ no external crate `unresolved` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0432`. From 4cc3834a5ced7ed5bc5c63d283c8a5b23fb9ccf6 Mon Sep 17 00:00:00 2001 From: bohan Date: Tue, 25 Jul 2023 01:57:12 +0800 Subject: [PATCH 078/105] resolve: ensure compile failed when has dummy or ambiguous --- compiler/rustc_resolve/src/imports.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f18aedd190fd..f3cf61c5b932 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -989,17 +989,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { initial_binding.res() }); let res = binding.res(); + if res == Res::Err || !this.ambiguity_errors.is_empty() { + this.tcx + .sess + .delay_span_bug(import.span, "some error happened for an import"); + return; + } if let Ok(initial_res) = initial_res { - if res != initial_res - && this.ambiguity_errors.is_empty() - && res != Res::Err - { + if res != initial_res { span_bug!(import.span, "inconsistent resolution for an import"); } - } else if res != Res::Err - && this.ambiguity_errors.is_empty() - && this.privacy_errors.is_empty() - { + } else if this.privacy_errors.is_empty() { this.tcx .sess .create_err(CannotDetermineImportResolution { span: import.span }) From fe0ef9a689d6298d0aa58dc3e491fc0066132fa7 Mon Sep 17 00:00:00 2001 From: James Dietz Date: Mon, 8 May 2023 13:18:57 -0400 Subject: [PATCH 079/105] delete [allow(...)] from issue #74838 --- library/std/src/sys/common/thread_local/fast_local.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index bc5da1a18967..c0a9619bf7bf 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -87,10 +87,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::<$t>::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { From db4a1534402a163f9296347d31796f070ae7c4e1 Mon Sep 17 00:00:00 2001 From: James Dietz Date: Fri, 16 Jun 2023 14:00:30 -0400 Subject: [PATCH 080/105] remove additional [allow(unused_unsafe)] --- library/alloc/src/alloc.rs | 1 - library/core/src/lib.rs | 2 +- library/std/src/alloc.rs | 1 - library/std/src/lib.rs | 2 +- library/std/src/sys/common/thread_local/os_local.rs | 5 ----- library/std/src/sys/common/thread_local/static_local.rs | 4 ---- library/std/src/sys/windows/compat.rs | 5 +---- library/std/src/sys_common/thread_info.rs | 1 - 8 files changed, 3 insertions(+), 18 deletions(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index c8751ba49117..5205ed9fb50a 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -392,7 +392,6 @@ pub mod __alloc_error_handler { static __rust_alloc_error_handler_should_panic: u8; } - #[allow(unused_unsafe)] if unsafe { __rust_alloc_error_handler_should_panic != 0 } { panic!("memory allocation of {size} bytes failed") } else { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9510e9efcdc3..741de3221ee8 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -418,7 +418,7 @@ pub mod arch; // set up in such a way that directly pulling it here works such that the // crate uses this crate as its core. #[path = "../../portable-simd/crates/core_simd/src/mod.rs"] -#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)] +#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn)] #[allow(rustdoc::bare_urls)] #[unstable(feature = "portable_simd", issue = "86656")] mod core_simd; diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index ec774e62debb..1eae7fa6a954 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -336,7 +336,6 @@ fn default_alloc_error_hook(layout: Layout) { static __rust_alloc_error_handler_should_panic: u8; } - #[allow(unused_unsafe)] if unsafe { __rust_alloc_error_handler_should_panic != 0 } { panic!("memory allocation of {} bytes failed", layout.size()); } else { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5bf66850f03d..4cd251d0ac26 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -542,7 +542,7 @@ pub mod time; // Pull in `std_float` crate into std. The contents of // `std_float` are in a different repository: rust-lang/portable-simd. #[path = "../../portable-simd/crates/std_float/src/lib.rs"] -#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)] +#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn)] #[allow(rustdoc::bare_urls)] #[unstable(feature = "portable_simd", issue = "86656")] mod std_float; diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs index 5d48ce1e03bc..7cf291921228 100644 --- a/library/std/src/sys/common/thread_local/os_local.rs +++ b/library/std/src/sys/common/thread_local/os_local.rs @@ -24,7 +24,6 @@ pub macro thread_local_inner { const fn __init() -> $t { INIT_EXPR } static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = _init { @@ -59,10 +58,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs index 80322a978646..5cb6c541a0ec 100644 --- a/library/std/src/sys/common/thread_local/static_local.rs +++ b/library/std/src/sys/common/thread_local/static_local.rs @@ -43,10 +43,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index 4fe95d41116b..e28dd4935367 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -69,10 +69,7 @@ unsafe extern "C" fn init() { /// Helper macro for creating CStrs from literals and symbol names. macro_rules! ansi_str { - (sym $ident:ident) => {{ - #[allow(unused_unsafe)] - crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes()) - }}; + (sym $ident:ident) => {{ crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes()) }}; ($lit:literal) => {{ crate::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes()) }}; } diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs index 38c9e50009af..88d937a7db15 100644 --- a/library/std/src/sys_common/thread_info.rs +++ b/library/std/src/sys_common/thread_info.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] // stack_guard isn't used right now on all platforms -#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny use crate::cell::RefCell; use crate::sys::thread::guard::Guard; From 40dd5a337cf8e565ae8c364e69e4e4e2d83ea4a7 Mon Sep 17 00:00:00 2001 From: DragonBillow Date: Tue, 25 Jul 2023 12:21:30 +0800 Subject: [PATCH 081/105] docs(LazyLock): add example pass local LazyLock variable to struct Signed-off-by: DragonBillow --- library/std/src/sync/lazy_lock.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index a6bc468b0928..37bdec5abcc7 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -25,6 +25,8 @@ union Data { /// /// # Examples /// +/// Initialize static variables with `LazyLock`. +/// /// ``` /// #![feature(lazy_cell)] /// @@ -54,6 +56,24 @@ union Data { /// // Some("Hoyten") /// } /// ``` +/// Initialize fields with `LazyLock`. +/// ``` +/// #![feature(lazy_cell)] +/// +/// use std::sync::LazyLock; +/// +/// #[derive(Debug)] +/// struct UseCellLock { +/// number: LazyLock, +/// } +/// fn main() { +/// let lock: LazyLock = LazyLock::new(|| 0u32); +/// +/// let data = UseCellLock { number: lock }; +/// println!("{}", *data.number); +/// } +/// ``` + #[unstable(feature = "lazy_cell", issue = "109736")] pub struct LazyLock T> { once: Once, From 0a0ce4905d9a557f9731a5c502016c0baf7e49c8 Mon Sep 17 00:00:00 2001 From: Eric Mark Martin Date: Tue, 25 Jul 2023 00:49:49 -0400 Subject: [PATCH 082/105] factor out more stable impls --- compiler/rustc_smir/src/rustc_smir/mod.rs | 163 +++++++++++++++------- 1 file changed, 109 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 0805f4f4d5b5..044e2f8f3256 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -575,19 +575,22 @@ impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> { impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { type T = stable_mir::ty::GenericArgs; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - use stable_mir::ty::{GenericArgKind, GenericArgs}; + use stable_mir::ty::GenericArgs; - GenericArgs( - self.iter() - .map(|arg| match arg.unpack() { - ty::GenericArgKind::Lifetime(region) => { - GenericArgKind::Lifetime(opaque(®ion)) - } - ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(ty)), - ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)), - }) - .collect(), - ) + GenericArgs(self.iter().map(|arg| arg.unpack().stable(tables)).collect()) + } +} + +impl<'tcx> Stable<'tcx> for ty::GenericArgKind<'tcx> { + type T = stable_mir::ty::GenericArgKind; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::GenericArgKind; + match self { + ty::GenericArgKind::Lifetime(region) => GenericArgKind::Lifetime(opaque(region)), + ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(*ty)), + ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)), + } } } @@ -659,63 +662,118 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { } } +impl<'tcx> Stable<'tcx> for ty::BoundTyKind { + type T = stable_mir::ty::BoundTyKind; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::BoundTyKind; + + match self { + ty::BoundTyKind::Anon => BoundTyKind::Anon, + ty::BoundTyKind::Param(def_id, symbol) => { + BoundTyKind::Param(rustc_internal::param_def(*def_id), symbol.to_string()) + } + } + } +} + +impl<'tcx> Stable<'tcx> for ty::BoundRegionKind { + type T = stable_mir::ty::BoundRegionKind; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::BoundRegionKind; + + match self { + ty::BoundRegionKind::BrAnon(option_span) => { + BoundRegionKind::BrAnon(option_span.map(|span| opaque(&span))) + } + ty::BoundRegionKind::BrNamed(def_id, symbol) => { + BoundRegionKind::BrNamed(rustc_internal::br_named_def(*def_id), symbol.to_string()) + } + ty::BoundRegionKind::BrEnv => BoundRegionKind::BrEnv, + } + } +} + impl<'tcx> Stable<'tcx> for ty::BoundVariableKind { type T = stable_mir::ty::BoundVariableKind; - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - use stable_mir::ty::{BoundRegionKind, BoundTyKind, BoundVariableKind}; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::BoundVariableKind; match self { ty::BoundVariableKind::Ty(bound_ty_kind) => { - BoundVariableKind::Ty(match bound_ty_kind { - ty::BoundTyKind::Anon => BoundTyKind::Anon, - ty::BoundTyKind::Param(def_id, symbol) => { - BoundTyKind::Param(rustc_internal::param_def(*def_id), symbol.to_string()) - } - }) + BoundVariableKind::Ty(bound_ty_kind.stable(tables)) } ty::BoundVariableKind::Region(bound_region_kind) => { - BoundVariableKind::Region(match bound_region_kind { - ty::BoundRegionKind::BrAnon(option_span) => { - BoundRegionKind::BrAnon(option_span.map(|span| opaque(&span))) - } - ty::BoundRegionKind::BrNamed(def_id, symbol) => BoundRegionKind::BrNamed( - rustc_internal::br_named_def(*def_id), - symbol.to_string(), - ), - ty::BoundRegionKind::BrEnv => BoundRegionKind::BrEnv, - }) + BoundVariableKind::Region(bound_region_kind.stable(tables)) } ty::BoundVariableKind::Const => BoundVariableKind::Const, } } } +impl<'tcx> Stable<'tcx> for ty::IntTy { + type T = IntTy; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + ty::IntTy::Isize => IntTy::Isize, + ty::IntTy::I8 => IntTy::I8, + ty::IntTy::I16 => IntTy::I16, + ty::IntTy::I32 => IntTy::I32, + ty::IntTy::I64 => IntTy::I64, + ty::IntTy::I128 => IntTy::I128, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::UintTy { + type T = UintTy; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + ty::UintTy::Usize => UintTy::Usize, + ty::UintTy::U8 => UintTy::U8, + ty::UintTy::U16 => UintTy::U16, + ty::UintTy::U32 => UintTy::U32, + ty::UintTy::U64 => UintTy::U64, + ty::UintTy::U128 => UintTy::U128, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::FloatTy { + type T = FloatTy; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + ty::FloatTy::F32 => FloatTy::F32, + ty::FloatTy::F64 => FloatTy::F64, + } + } +} + +impl<'tcx> Stable<'tcx> for hir::Movability { + type T = Movability; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + hir::Movability::Static => Movability::Static, + hir::Movability::Movable => Movability::Movable, + } + } +} + impl<'tcx> Stable<'tcx> for Ty<'tcx> { type T = stable_mir::ty::TyKind; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { match self.kind() { ty::Bool => TyKind::RigidTy(RigidTy::Bool), ty::Char => TyKind::RigidTy(RigidTy::Char), - ty::Int(int_ty) => match int_ty { - ty::IntTy::Isize => TyKind::RigidTy(RigidTy::Int(IntTy::Isize)), - ty::IntTy::I8 => TyKind::RigidTy(RigidTy::Int(IntTy::I8)), - ty::IntTy::I16 => TyKind::RigidTy(RigidTy::Int(IntTy::I16)), - ty::IntTy::I32 => TyKind::RigidTy(RigidTy::Int(IntTy::I32)), - ty::IntTy::I64 => TyKind::RigidTy(RigidTy::Int(IntTy::I64)), - ty::IntTy::I128 => TyKind::RigidTy(RigidTy::Int(IntTy::I128)), - }, - ty::Uint(uint_ty) => match uint_ty { - ty::UintTy::Usize => TyKind::RigidTy(RigidTy::Uint(UintTy::Usize)), - ty::UintTy::U8 => TyKind::RigidTy(RigidTy::Uint(UintTy::U8)), - ty::UintTy::U16 => TyKind::RigidTy(RigidTy::Uint(UintTy::U16)), - ty::UintTy::U32 => TyKind::RigidTy(RigidTy::Uint(UintTy::U32)), - ty::UintTy::U64 => TyKind::RigidTy(RigidTy::Uint(UintTy::U64)), - ty::UintTy::U128 => TyKind::RigidTy(RigidTy::Uint(UintTy::U128)), - }, - ty::Float(float_ty) => match float_ty { - ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)), - ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)), - }, + ty::Int(int_ty) => TyKind::RigidTy(RigidTy::Int(int_ty.stable(tables))), + ty::Uint(uint_ty) => TyKind::RigidTy(RigidTy::Uint(uint_ty.stable(tables))), + ty::Float(float_ty) => TyKind::RigidTy(RigidTy::Float(float_ty.stable(tables))), ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( rustc_internal::adt_def(adt_def.did()), generic_args.stable(tables), @@ -758,10 +816,7 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( rustc_internal::generator_def(*def_id), generic_args.stable(tables), - match movability { - hir::Movability::Static => Movability::Static, - hir::Movability::Movable => Movability::Movable, - }, + movability.stable(tables), )), ty::Never => TyKind::RigidTy(RigidTy::Never), ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( From 25db1fac81103832519bd219877e7f756e690e77 Mon Sep 17 00:00:00 2001 From: Steven Trotter Date: Fri, 14 Jul 2023 21:33:45 +0100 Subject: [PATCH 083/105] Added recursive checking back up to see if a `Clone` suggestion would be helpful. --- .../src/fn_ctxt/suggestions.rs | 84 +++++++ tests/ui/typeck/explain_clone_autoref.rs | 116 ++++++++++ tests/ui/typeck/explain_clone_autoref.stderr | 215 +++++++++++++++++- 3 files changed, 414 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ec19d017c259..77914a1c16cd 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1512,9 +1512,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found_ty: Ty<'tcx>, expr: &hir::Expr<'_>, ) { + // When `expr` is `x` in something like `let x = foo.clone(); x`, need to recurse up to get + // `foo` and `clone`. + let expr = self.note_type_is_not_clone_inner_expr(expr); + + // If we've recursed to an `expr` of `foo.clone()`, get `foo` and `clone`. let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; }; + let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; @@ -1567,6 +1573,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Given a type mismatch error caused by `&T` being cloned instead of `T`, and + /// the `expr` as the source of this type mismatch, try to find the method call + /// as the source of this error and return that instead. Otherwise, return the + /// original expression. + fn note_type_is_not_clone_inner_expr<'b>( + &'b self, + expr: &'b hir::Expr<'b>, + ) -> &'b hir::Expr<'b> { + match expr.peel_blocks().kind { + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { segments: [_], res: crate::Res::Local(binding), .. }, + )) => { + let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding) + else { + return expr; + }; + let Some(parent) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id)) else { + return expr; + }; + + match parent { + // foo.clone() + hir::Node::Local(hir::Local { init: Some(init), .. }) => { + self.note_type_is_not_clone_inner_expr(init) + } + // When `expr` is more complex like a tuple + hir::Node::Pat(hir::Pat { + hir_id: pat_hir_id, + kind: hir::PatKind::Tuple(pats, ..), + .. + }) => { + let Some(hir::Node::Local(hir::Local { init: Some(init), .. })) = + self.tcx.hir().find(self.tcx.hir().parent_id(*pat_hir_id)) else { + return expr; + }; + + match init.peel_blocks().kind { + ExprKind::Tup(init_tup) => { + if let Some(init) = pats + .iter() + .enumerate() + .filter(|x| x.1.hir_id == *hir_id) + .map(|(i, _)| init_tup.get(i).unwrap()) + .next() + { + self.note_type_is_not_clone_inner_expr(init) + } else { + expr + } + } + _ => expr, + } + } + _ => expr, + } + } + // If we're calling into a closure that may not be typed recurse into that call. no need + // to worry if it's a call to a typed function or closure as this would ne handled + // previously. + hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) = call_expr_kind + && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path + && let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding) + && let Some(closure) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id)) + && let hir::Node::Local(hir::Local { init: Some(init), .. }) = closure + && let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), ..} = init + { + let hir::Body { value: body_expr, .. } = self.tcx.hir().body(*body_id); + self.note_type_is_not_clone_inner_expr(body_expr) + } else { + expr + } + } + _ => expr, + } + } + /// A common error is to add an extra semicolon: /// /// ```compile_fail,E0308 diff --git a/tests/ui/typeck/explain_clone_autoref.rs b/tests/ui/typeck/explain_clone_autoref.rs index 4d21574700ad..88aaac469b21 100644 --- a/tests/ui/typeck/explain_clone_autoref.rs +++ b/tests/ui/typeck/explain_clone_autoref.rs @@ -11,3 +11,119 @@ fn clone_thing(nc: &NotClone) -> NotClone { //~| NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead //~| NOTE expected `NotClone`, found `&NotClone` } + +fn clone_thing2(nc: &NotClone) -> NotClone { + let nc: NotClone = nc.clone(); + //~^ ERROR mismatched type + //~| NOTE expected due to this + //~| NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + //~| NOTE expected `NotClone`, found `&NotClone` + nc +} + +fn clone_thing3(nc: &NotClone) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let nc = nc.clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + nc + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} + +fn clone_thing4(nc: &NotClone) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let nc = nc.clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + let nc2 = nc; + nc2 + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} + +impl NotClone { + fn other_fn(&self) {} + fn get_ref_notclone(&self) -> &Self { + self + } +} + +fn clone_thing5(nc: &NotClone) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let nc = nc.clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + let nc2 = nc; + nc2.other_fn(); + let nc3 = nc2; + nc3 + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} + +fn clone_thing6(nc: &NotClone) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let (ret, _) = (nc.clone(), 1); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + let _ = nc.clone(); + ret + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} + +fn clone_thing7(nc: Vec<&NotClone>) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let ret = nc[0].clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + ret + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} + +fn clone_thing8(nc: &NotClone) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let ret = { + let a = nc.clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + a + }; + ret + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} + +fn clone_thing9(nc: &NotClone) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let cl = || nc.clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + let ret = cl(); + ret + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} + +fn clone_thing10(nc: &NotClone) -> (NotClone, NotClone) { + let (a, b) = { + let a = nc.clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + (a, nc.clone()) + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + }; + (a, b) + //~^ ERROR mismatched type + //~| ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` + //~| NOTE expected `NotClone`, found `&NotClone` +} + +fn clone_thing11(nc: &NotClone) -> NotClone { + //~^ NOTE expected `NotClone` because of return type + let a = { + let nothing = nc.clone(); + let a = nc.clone(); + //~^ NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + let nothing = nc.clone(); + a + }; + a + //~^ ERROR mismatched type + //~| NOTE expected `NotClone`, found `&NotClone` +} diff --git a/tests/ui/typeck/explain_clone_autoref.stderr b/tests/ui/typeck/explain_clone_autoref.stderr index 38cb7fe55184..40d3df301190 100644 --- a/tests/ui/typeck/explain_clone_autoref.stderr +++ b/tests/ui/typeck/explain_clone_autoref.stderr @@ -18,6 +18,219 @@ LL + #[derive(Clone)] LL | struct NotClone; | -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:16:24 + | +LL | let nc: NotClone = nc.clone(); + | -------- ^^^^^^^^^^ expected `NotClone`, found `&NotClone` + | | + | expected due to this + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:16:24 + | +LL | let nc: NotClone = nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:28:5 + | +LL | fn clone_thing3(nc: &NotClone) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | nc + | ^^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:26:14 + | +LL | let nc = nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:38:5 + | +LL | fn clone_thing4(nc: &NotClone) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | nc2 + | ^^^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:35:14 + | +LL | let nc = nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:57:5 + | +LL | fn clone_thing5(nc: &NotClone) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | nc3 + | ^^^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:52:14 + | +LL | let nc = nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:67:5 + | +LL | fn clone_thing6(nc: &NotClone) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | ret + | ^^^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:64:21 + | +LL | let (ret, _) = (nc.clone(), 1); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:76:5 + | +LL | fn clone_thing7(nc: Vec<&NotClone>) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | ret + | ^^^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:74:15 + | +LL | let ret = nc[0].clone(); + | ^^^^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:88:5 + | +LL | fn clone_thing8(nc: &NotClone) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | ret + | ^^^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:84:17 + | +LL | let a = nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:98:5 + | +LL | fn clone_thing9(nc: &NotClone) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | ret + | ^^^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:95:17 + | +LL | let cl = || nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:110:6 + | +LL | (a, b) + | ^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:105:17 + | +LL | let a = nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:110:9 + | +LL | (a, b) + | ^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:107:13 + | +LL | (a, nc.clone()) + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0308]: mismatched types + --> $DIR/explain_clone_autoref.rs:126:5 + | +LL | fn clone_thing11(nc: &NotClone) -> NotClone { + | -------- expected `NotClone` because of return type +... +LL | a + | ^ expected `NotClone`, found `&NotClone` + | +note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead + --> $DIR/explain_clone_autoref.rs:121:17 + | +LL | let a = nc.clone(); + | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0308`. From a0376e9ec2af2d996b828f6a6153c7f53b100b0b Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 25 Jul 2023 09:24:12 +0000 Subject: [PATCH 084/105] extract common code --- compiler/rustc_parse/src/lexer/mod.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index cfcc2ec42fac..1931ee5e528d 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -166,10 +166,7 @@ impl<'a> StringReader<'a> { continue; } rustc_lexer::TokenKind::Ident => { - let sym = nfc_normalize(self.str_from(start)); - let span = self.mk_sp(start, self.pos); - self.sess.symbol_gallery.insert(sym, span); - token::Ident(sym, false) + self.ident(start) } rustc_lexer::TokenKind::RawIdent => { let sym = nfc_normalize(self.str_from(start + BytePos(2))); @@ -183,10 +180,7 @@ impl<'a> StringReader<'a> { } rustc_lexer::TokenKind::UnknownPrefix => { self.report_unknown_prefix(start); - let sym = nfc_normalize(self.str_from(start)); - let span = self.mk_sp(start, self.pos); - self.sess.symbol_gallery.insert(sym, span); - token::Ident(sym, false) + self.ident(start) } rustc_lexer::TokenKind::InvalidIdent // Do not recover an identifier with emoji if the codepoint is a confusable @@ -222,10 +216,8 @@ impl<'a> StringReader<'a> { self.cursor = Cursor::new(&str_before[prefix_len as usize..]); self.report_unknown_prefix(start); - let sym = nfc_normalize(self.str_from(start)); let prefix_span = self.mk_sp(start, lit_start); - self.sess.symbol_gallery.insert(sym, prefix_span); - return (Token::new(token::Ident(sym, false), prefix_span), preceded_by_whitespace); + return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace); } rustc_lexer::TokenKind::Literal { kind, suffix_start } => { let suffix_start = start + BytePos(suffix_start); @@ -341,6 +333,13 @@ impl<'a> StringReader<'a> { } } + fn ident(&self, start: BytePos) -> TokenKind { + let sym = nfc_normalize(self.str_from(start)); + let span = self.mk_sp(start, self.pos); + self.sess.symbol_gallery.insert(sym, span); + token::Ident(sym, false) + } + fn struct_fatal_span_char( &self, from_pos: BytePos, From 75df62d4a2c45175b8cb1eb4bdc10fa423c11c4c Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 25 Jul 2023 11:12:52 +0100 Subject: [PATCH 085/105] builtin_macros: raw str in diagnostic output If a raw string was used in the `env!` invocation, then it should also be shown in the diagnostic messages as a raw string. Signed-off-by: David Wood --- compiler/rustc_builtin_macros/messages.ftl | 4 +- compiler/rustc_builtin_macros/src/env.rs | 41 ++++++++------- compiler/rustc_builtin_macros/src/errors.rs | 52 +++++++++---------- compiler/rustc_errors/src/diagnostic_impls.rs | 6 +++ tests/ui/macros/builtin-env-issue-114010.rs | 4 ++ .../ui/macros/builtin-env-issue-114010.stderr | 15 ++++-- 6 files changed, 73 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 322222ae3303..79fae7f92f1e 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -109,8 +109,8 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept .suggestion = remove the value builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time - .cargo = Cargo sets build script variables at run time. Use `std::env::var("{$var}")` instead - .other = use `std::env::var("{$var}")` to read the variable at run time + .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead + .custom = use `std::env::var({$var_expr})` to read the variable at run time builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 30d74ba593c9..92da0c069e51 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -4,7 +4,7 @@ // use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{self as ast, GenericArg}; +use rustc_ast::{self as ast, AstDeref, GenericArg}; use rustc_expand::base::{self, *}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -76,27 +76,36 @@ pub fn expand_env<'cx>( }, }; - let sp = cx.with_def_site_ctxt(sp); + let span = cx.with_def_site_ctxt(sp); let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern); cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { None => { - // Use the string literal in the code in the diagnostic to avoid confusing diagnostics, - // e.g. when the literal contains escape sequences. let ast::ExprKind::Lit(ast::token::Lit { kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..), - symbol: original_var, + symbol, .. }) = &var_expr.kind else { unreachable!("`expr_to_string` ensures this is a string lit") }; - cx.emit_err(errors::EnvNotDefined { - span: sp, - msg: custom_msg, - var: *original_var, - help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())), - }); + + if let Some(msg_from_user) = custom_msg { + cx.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }); + } else if is_cargo_env_var(var.as_str()) { + cx.emit_err(errors::EnvNotDefined::CargoEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }); + } else { + cx.emit_err(errors::EnvNotDefined::CustomEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }); + } + return DummyResult::any(sp); } Some(value) => cx.expr_str(sp, value), @@ -104,13 +113,9 @@ pub fn expand_env<'cx>( MacEager::expr(e) } -fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp { - if var.starts_with("CARGO_") +/// Returns `true` if an environment variable from `env!` is one used by Cargo. +fn is_cargo_env_var(var: &str) -> bool { + var.starts_with("CARGO_") || var.starts_with("DEP_") || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET") - { - errors::EnvNotDefinedHelp::CargoVar - } else { - errors::EnvNotDefinedHelp::Other - } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 7b2a375a822b..fbf0395bb05a 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -440,43 +440,43 @@ pub(crate) struct EnvTakesArgs { pub(crate) span: Span, } -//#[derive(Diagnostic)] -//#[diag(builtin_macros_env_not_defined)] -pub(crate) struct EnvNotDefined { +pub(crate) struct EnvNotDefinedWithUserMessage { pub(crate) span: Span, - pub(crate) msg: Option, - pub(crate) var: Symbol, - pub(crate) help: Option, + pub(crate) msg_from_user: Symbol, } -// Hand-written implementation to support custom user messages -impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined { +// Hand-written implementation to support custom user messages. +impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage { #[track_caller] fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> { - let mut diag = if let Some(msg) = self.msg { - #[expect( - rustc::untranslatable_diagnostic, - reason = "cannot translate user-provided messages" - )] - handler.struct_diagnostic(msg.to_string()) - } else { - handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined) - }; - diag.set_arg("var", self.var); + #[expect( + rustc::untranslatable_diagnostic, + reason = "cannot translate user-provided messages" + )] + let mut diag = handler.struct_diagnostic(self.msg_from_user.to_string()); diag.set_span(self.span); - if let Some(help) = self.help { - diag.subdiagnostic(help); - } diag } } -#[derive(Subdiagnostic)] -pub(crate) enum EnvNotDefinedHelp { +#[derive(Diagnostic)] +pub(crate) enum EnvNotDefined<'a> { + #[diag(builtin_macros_env_not_defined)] #[help(builtin_macros_cargo)] - CargoVar, - #[help(builtin_macros_other)] - Other, + CargoEnvVar { + #[primary_span] + span: Span, + var: Symbol, + var_expr: &'a rustc_ast::Expr, + }, + #[diag(builtin_macros_env_not_defined)] + #[help(builtin_macros_custom)] + CustomEnvVar { + #[primary_span] + span: Span, + var: Symbol, + var_expr: &'a rustc_ast::Expr, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 10fe7fc74a87..fb2e84bb7a13 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -164,6 +164,12 @@ impl IntoDiagnosticArg for hir::ConstContext { } } +impl IntoDiagnosticArg for ast::Expr { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) + } +} + impl IntoDiagnosticArg for ast::Path { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) diff --git a/tests/ui/macros/builtin-env-issue-114010.rs b/tests/ui/macros/builtin-env-issue-114010.rs index eb97c767fa2e..819b8b1e8abe 100644 --- a/tests/ui/macros/builtin-env-issue-114010.rs +++ b/tests/ui/macros/builtin-env-issue-114010.rs @@ -1,6 +1,10 @@ // unset-rustc-env:oopsie +// unset-rustc-env:a""a env![r#"oopsie"#]; //~^ ERROR environment variable `oopsie` not defined at compile time +env![r#"a""a"#]; +//~^ ERROR environment variable `a""a` not defined at compile time + fn main() {} diff --git a/tests/ui/macros/builtin-env-issue-114010.stderr b/tests/ui/macros/builtin-env-issue-114010.stderr index 7934ce1b7909..0da42089cce7 100644 --- a/tests/ui/macros/builtin-env-issue-114010.stderr +++ b/tests/ui/macros/builtin-env-issue-114010.stderr @@ -1,11 +1,20 @@ error: environment variable `oopsie` not defined at compile time - --> $DIR/builtin-env-issue-114010.rs:3:1 + --> $DIR/builtin-env-issue-114010.rs:4:1 | LL | env![r#"oopsie"#]; | ^^^^^^^^^^^^^^^^^ | - = help: use `std::env::var("oopsie")` to read the variable at run time + = help: use `std::env::var(r#"oopsie"#)` to read the variable at run time = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error: environment variable `a""a` not defined at compile time + --> $DIR/builtin-env-issue-114010.rs:7:1 + | +LL | env![r#"a""a"#]; + | ^^^^^^^^^^^^^^^ + | + = help: use `std::env::var(r#"a""a"#)` to read the variable at run time + = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors From 5d32fd1b0623651cca15acd71ecc901324f3170c Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 25 Jul 2023 12:42:54 +0200 Subject: [PATCH 086/105] Add regression test for invalid unused const in method The warning can be reproduced with 1.63 but not with 1.64. $ rustc +1.63 tests/ui/lint/unused/const-local-var.rs warning: constant `F` is never used --> tests/ui/lint/unused/const-local-var.rs:14:9 | 14 | const F: i32 = 2; | ^^^^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default $ rustc +1.64 tests/ui/lint/unused/const-local-var.rs Add a regression test to prevent the problem from re-appearing. --- tests/ui/lint/unused/const-local-var.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/ui/lint/unused/const-local-var.rs diff --git a/tests/ui/lint/unused/const-local-var.rs b/tests/ui/lint/unused/const-local-var.rs new file mode 100644 index 000000000000..89ca16fe003e --- /dev/null +++ b/tests/ui/lint/unused/const-local-var.rs @@ -0,0 +1,23 @@ +// regression test for https://github.com/rust-lang/rust/issues/69016 +// check-pass + +#![warn(unused)] +#![deny(warnings)] + +fn _unused1(x: i32) -> i32 { + const F: i32 = 2; + let g = 1; + x * F + g +} + +pub struct Foo {} + +impl Foo { + fn _unused2(x: i32) -> i32 { + const F: i32 = 2; + let g = 1; + x * F + g + } +} + +fn main() {} From 516a56581e4c72381991a09bf7c2da7ae85f7948 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 25 Jul 2023 11:56:54 +0100 Subject: [PATCH 087/105] clippy: `env!` invocations can't be b"" literals Signed-off-by: David Wood --- src/tools/clippy/clippy_lints/src/strings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 58724852b3c4..4d45091f4b5b 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -328,7 +328,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { { // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces // `&[u8]`. This change would prevent matching with different sized slices. - } else { + } else if !callsite.starts_with("env!") { span_lint_and_sugg( cx, STRING_LIT_AS_BYTES, From b2d052b22d3137214c6658f0450609427faa0c2f Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Tue, 25 Jul 2023 12:08:44 +0100 Subject: [PATCH 088/105] write-long-types-to-disk: update tests --- compiler/rustc_interface/src/tests.rs | 2 +- tests/ui/error-codes/E0271.rs | 1 - tests/ui/error-codes/E0271.stderr | 6 ++-- tests/ui/error-codes/E0275.rs | 1 - tests/ui/error-codes/E0275.stderr | 7 ++-- .../trait-bounds/hang-on-deeply-nested-dyn.rs | 1 - .../hang-on-deeply-nested-dyn.stderr | 5 ++- tests/ui/issues/issue-20413.rs | 1 - tests/ui/issues/issue-20413.stderr | 33 ++++++++----------- tests/ui/issues/issue-23122-2.rs | 1 - tests/ui/issues/issue-23122-2.stderr | 7 ++-- .../issue-37311.rs | 1 - .../issue-37311.stderr | 4 +-- tests/ui/methods/inherent-bound-in-probe.rs | 1 - .../ui/methods/inherent-bound-in-probe.stderr | 11 +++---- tests/ui/recursion/issue-83150.rs | 2 +- tests/ui/recursion/issue-83150.stderr | 3 +- tests/ui/regions/issue-102374.rs | 1 - tests/ui/regions/issue-102374.stderr | 5 ++- .../traits/issue-91949-hangs-on-recursion.rs | 2 +- .../issue-91949-hangs-on-recursion.stderr | 3 +- 21 files changed, 39 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 20171f72c410..dac63085e2e1 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -738,7 +738,7 @@ fn test_unstable_options_tracking_hash() { untracked!(unstable_options, true); untracked!(validate_mir, true); untracked!(verbose, true); - untracked!(write_long_types_to_disk, true); + untracked!(write_long_types_to_disk, false); // tidy-alphabetical-end macro_rules! tracked { diff --git a/tests/ui/error-codes/E0271.rs b/tests/ui/error-codes/E0271.rs index ec6b8b5532e6..f27190754348 100644 --- a/tests/ui/error-codes/E0271.rs +++ b/tests/ui/error-codes/E0271.rs @@ -1,4 +1,3 @@ -// compile-flags: -Zwrite-long-types-to-disk=yes trait Trait { type AssociatedType; } fn foo(t: T) where T: Trait { diff --git a/tests/ui/error-codes/E0271.stderr b/tests/ui/error-codes/E0271.stderr index 98b5aa83da61..1e2f4383459e 100644 --- a/tests/ui/error-codes/E0271.stderr +++ b/tests/ui/error-codes/E0271.stderr @@ -1,5 +1,5 @@ error[E0271]: type mismatch resolving `::AssociatedType == u32` - --> $DIR/E0271.rs:11:9 + --> $DIR/E0271.rs:10:9 | LL | foo(3_i8); | --- ^^^^ type mismatch resolving `::AssociatedType == u32` @@ -7,12 +7,12 @@ LL | foo(3_i8); | required by a bound introduced by this call | note: expected this to be `u32` - --> $DIR/E0271.rs:8:43 + --> $DIR/E0271.rs:7:43 | LL | impl Trait for i8 { type AssociatedType = &'static str; } | ^^^^^^^^^^^^ note: required by a bound in `foo` - --> $DIR/E0271.rs:4:32 + --> $DIR/E0271.rs:3:32 | LL | fn foo(t: T) where T: Trait { | ^^^^^^^^^^^^^^^^^^ required by this bound in `foo` diff --git a/tests/ui/error-codes/E0275.rs b/tests/ui/error-codes/E0275.rs index 5119544e29fe..95d7f85f1054 100644 --- a/tests/ui/error-codes/E0275.rs +++ b/tests/ui/error-codes/E0275.rs @@ -1,4 +1,3 @@ -// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" trait Foo {} diff --git a/tests/ui/error-codes/E0275.stderr b/tests/ui/error-codes/E0275.stderr index 1ddb058d0fd1..c702c3790a7c 100644 --- a/tests/ui/error-codes/E0275.stderr +++ b/tests/ui/error-codes/E0275.stderr @@ -1,16 +1,15 @@ error[E0275]: overflow evaluating the requirement `Bar>>>>>>: Foo` - --> $DIR/E0275.rs:7:33 + --> $DIR/E0275.rs:6:33 | LL | impl Foo for T where Bar: Foo {} | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`E0275`) -note: required for `Bar>>>>>>>>>>>>>>>>>>>>` to implement `Foo` - --> $DIR/E0275.rs:7:9 +note: required for `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Foo` + --> $DIR/E0275.rs:6:9 | LL | impl Foo for T where Bar: Foo {} | ^^^ ^ --- unsatisfied trait bound introduced here - = note: the full type name has been written to '$TEST_BUILD_DIR/error-codes/E0275/E0275.long-type-hash.txt' = note: 126 redundant requirements hidden = note: required for `Bar` to implement `Foo` diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs index 3ee2669a15d1..d34b7a29623c 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs @@ -1,4 +1,3 @@ -// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" fn id( diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr index f162806cca3d..139c844d8a22 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/hang-on-deeply-nested-dyn.rs:13:5 + --> $DIR/hang-on-deeply-nested-dyn.rs:12:5 | LL | ) -> &dyn Fn( | ______- @@ -13,8 +13,7 @@ LL | | ) { LL | f | ^ expected `&dyn Fn(&dyn Fn(&dyn Fn(&...)))`, found `&dyn Fn(u32)` | - = note: expected reference `&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&...)))))))))))` - the full type name has been written to '$TEST_BUILD_DIR/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn/hang-on-deeply-nested-dyn.long-type-hash.txt' + = note: expected reference `&dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn Fn(u32) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a))` found reference `&dyn Fn(u32)` error: aborting due to previous error diff --git a/tests/ui/issues/issue-20413.rs b/tests/ui/issues/issue-20413.rs index 64d8cdc3e6f1..4de22f0c9177 100644 --- a/tests/ui/issues/issue-20413.rs +++ b/tests/ui/issues/issue-20413.rs @@ -1,4 +1,3 @@ -// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" trait Foo { fn answer(self); diff --git a/tests/ui/issues/issue-20413.stderr b/tests/ui/issues/issue-20413.stderr index 8d660478a31e..8793029dd224 100644 --- a/tests/ui/issues/issue-20413.stderr +++ b/tests/ui/issues/issue-20413.stderr @@ -1,5 +1,5 @@ error[E0392]: parameter `T` is never used - --> $DIR/issue-20413.rs:7:15 + --> $DIR/issue-20413.rs:6:15 | LL | struct NoData; | ^ unused parameter @@ -8,62 +8,57 @@ LL | struct NoData; = help: if you intended `T` to be a const parameter, use `const T: usize` instead error[E0275]: overflow evaluating the requirement `NoData>>>>>>: Foo` - --> $DIR/issue-20413.rs:10:36 + --> $DIR/issue-20413.rs:9:36 | LL | impl Foo for T where NoData: Foo { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) -note: required for `NoData>>>>>>>>>>>>` to implement `Foo` - --> $DIR/issue-20413.rs:10:9 +note: required for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Foo` + --> $DIR/issue-20413.rs:9:9 | LL | impl Foo for T where NoData: Foo { | ^^^ ^ --- unsatisfied trait bound introduced here - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt' = note: 126 redundant requirements hidden = note: required for `NoData` to implement `Foo` error[E0275]: overflow evaluating the requirement `AlmostNoData>>>>>>: Bar` - --> $DIR/issue-20413.rs:29:42 + --> $DIR/issue-20413.rs:28:42 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) -note: required for `EvenLessData>>>>>>` to implement `Baz` - --> $DIR/issue-20413.rs:36:9 +note: required for `EvenLessData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Baz` + --> $DIR/issue-20413.rs:35:9 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ ^ --- unsatisfied trait bound introduced here - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt' -note: required for `AlmostNoData>>>>>>` to implement `Bar` - --> $DIR/issue-20413.rs:29:9 +note: required for `AlmostNoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Bar` + --> $DIR/issue-20413.rs:28:9 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ ^ --- unsatisfied trait bound introduced here - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt' = note: 125 redundant requirements hidden = note: required for `EvenLessData` to implement `Baz` error[E0275]: overflow evaluating the requirement `EvenLessData>>>>>>: Baz` - --> $DIR/issue-20413.rs:36:42 + --> $DIR/issue-20413.rs:35:42 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) -note: required for `AlmostNoData>>>>>>` to implement `Bar` - --> $DIR/issue-20413.rs:29:9 +note: required for `AlmostNoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Bar` + --> $DIR/issue-20413.rs:28:9 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ ^ --- unsatisfied trait bound introduced here - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt' -note: required for `EvenLessData>>>>>>` to implement `Baz` - --> $DIR/issue-20413.rs:36:9 +note: required for `EvenLessData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Baz` + --> $DIR/issue-20413.rs:35:9 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ ^ --- unsatisfied trait bound introduced here - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt' = note: 125 redundant requirements hidden = note: required for `AlmostNoData` to implement `Bar` diff --git a/tests/ui/issues/issue-23122-2.rs b/tests/ui/issues/issue-23122-2.rs index 06cd17ad1ead..338789c2e871 100644 --- a/tests/ui/issues/issue-23122-2.rs +++ b/tests/ui/issues/issue-23122-2.rs @@ -1,4 +1,3 @@ -// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" trait Next { type Next: Next; diff --git a/tests/ui/issues/issue-23122-2.stderr b/tests/ui/issues/issue-23122-2.stderr index fe6b44de85b3..af77e4b1a412 100644 --- a/tests/ui/issues/issue-23122-2.stderr +++ b/tests/ui/issues/issue-23122-2.stderr @@ -1,18 +1,17 @@ error[E0275]: overflow evaluating the requirement `<<<<<<<... as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` - --> $DIR/issue-23122-2.rs:12:17 + --> $DIR/issue-23122-2.rs:11:17 | LL | type Next = as Next>::Next; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`) -note: required for `GetNext<<<<<<<... as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` to implement `Next` - --> $DIR/issue-23122-2.rs:11:15 +note: required for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` to implement `Next` + --> $DIR/issue-23122-2.rs:10:15 | LL | impl Next for GetNext { | - ^^^^ ^^^^^^^^^^ | | | unsatisfied trait bound introduced here - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-23122-2/issue-23122-2.long-type-hash.txt' error: aborting due to previous error diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs index 372a641c5338..c109be005238 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs @@ -1,5 +1,4 @@ // build-fail -// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: ".nll/" -> "/" // ignore-compare-mode-next-solver (hangs) diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 494da0ffa66d..87832dd29b28 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,11 +1,11 @@ error: reached the recursion limit while instantiating `<(&(&(..., ...), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:19:9 + --> $DIR/issue-37311.rs:18:9 | LL | (self, self).recurse(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `::recurse` defined here - --> $DIR/issue-37311.rs:18:5 + --> $DIR/issue-37311.rs:17:5 | LL | fn recurse(&self) { | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/methods/inherent-bound-in-probe.rs b/tests/ui/methods/inherent-bound-in-probe.rs index 26be90202e68..81a99ca010e5 100644 --- a/tests/ui/methods/inherent-bound-in-probe.rs +++ b/tests/ui/methods/inherent-bound-in-probe.rs @@ -1,4 +1,3 @@ -// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" // Fixes #110131 diff --git a/tests/ui/methods/inherent-bound-in-probe.stderr b/tests/ui/methods/inherent-bound-in-probe.stderr index 7632373eafe6..8d7cc462280d 100644 --- a/tests/ui/methods/inherent-bound-in-probe.stderr +++ b/tests/ui/methods/inherent-bound-in-probe.stderr @@ -1,5 +1,5 @@ error[E0277]: `Helper<'a, T>` is not an iterator - --> $DIR/inherent-bound-in-probe.rs:41:21 + --> $DIR/inherent-bound-in-probe.rs:40:21 | LL | type IntoIter = Helper<'a, T>; | ^^^^^^^^^^^^^ `Helper<'a, T>` is not an iterator @@ -9,14 +9,14 @@ note: required by a bound in `std::iter::IntoIterator::IntoIter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL error[E0275]: overflow evaluating the requirement `&_: IntoIterator` - --> $DIR/inherent-bound-in-probe.rs:45:17 + --> $DIR/inherent-bound-in-probe.rs:44:17 | LL | Helper::new(&self.0) | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_bound_in_probe`) note: required for `&BitReaderWrapper<_>` to implement `IntoIterator` - --> $DIR/inherent-bound-in-probe.rs:35:13 + --> $DIR/inherent-bound-in-probe.rs:34:13 | LL | impl<'a, T> IntoIterator for &'a BitReaderWrapper | ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,10 +24,9 @@ LL | where LL | &'a T: IntoIterator, | ------------- unsatisfied trait bound introduced here = note: 126 redundant requirements hidden - = note: required for `&BitReaderWrapper>>>>` to implement `IntoIterator` - = note: the full type name has been written to '$TEST_BUILD_DIR/methods/inherent-bound-in-probe/inherent-bound-in-probe.long-type-hash.txt' + = note: required for `&BitReaderWrapper>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `IntoIterator` note: required by a bound in `Helper<'a, T>` - --> $DIR/inherent-bound-in-probe.rs:26:25 + --> $DIR/inherent-bound-in-probe.rs:25:25 | LL | &'a T: IntoIterator, | ^^^^^^^^^^^^^ required by this bound in `Helper<'a, T>` diff --git a/tests/ui/recursion/issue-83150.rs b/tests/ui/recursion/issue-83150.rs index 67c3780ba20d..75dcdc59f21c 100644 --- a/tests/ui/recursion/issue-83150.rs +++ b/tests/ui/recursion/issue-83150.rs @@ -1,5 +1,5 @@ // build-fail -// compile-flags: -Copt-level=0 -Zwrite-long-types-to-disk=yes +// compile-flags: -Copt-level=0 // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" //~^^^ ERROR overflow evaluating the requirement // ignore-compare-mode-next-solver (hangs) diff --git a/tests/ui/recursion/issue-83150.stderr b/tests/ui/recursion/issue-83150.stderr index eae58771a416..543b7308472f 100644 --- a/tests/ui/recursion/issue-83150.stderr +++ b/tests/ui/recursion/issue-83150.stderr @@ -14,8 +14,7 @@ error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`) = note: required for `&mut Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:13:24: 13:27]>` to implement `Iterator` = note: 65 redundant requirements hidden - = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<..., ...>, ...>, ...>, ...>, ...>, ...>, ...>` to implement `Iterator` - = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/issue-83150/issue-83150.long-type-hash.txt' + = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>, [closure@$DIR/issue-83150.rs:13:24: 13:27]>` to implement `Iterator` error: aborting due to previous error; 1 warning emitted diff --git a/tests/ui/regions/issue-102374.rs b/tests/ui/regions/issue-102374.rs index c8c22c48af1e..fd71248d9cb4 100644 --- a/tests/ui/regions/issue-102374.rs +++ b/tests/ui/regions/issue-102374.rs @@ -1,4 +1,3 @@ -// compile-flags: -Zwrite-long-types-to-disk=yes // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" use std::cell::Cell; diff --git a/tests/ui/regions/issue-102374.stderr b/tests/ui/regions/issue-102374.stderr index 7a09ba05239d..a4230cf8b5a2 100644 --- a/tests/ui/regions/issue-102374.stderr +++ b/tests/ui/regions/issue-102374.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-102374.rs:18:5 + --> $DIR/issue-102374.rs:17:5 | LL | ) -> i32 { | --- expected `i32` because of return type @@ -7,8 +7,7 @@ LL | f | ^ expected `i32`, found fn pointer | = note: expected type `i32` - found fn pointer `fn(Cell<...>)` - the full type name has been written to '$TEST_BUILD_DIR/regions/issue-102374/issue-102374.long-type-hash.txt' + found fn pointer `for<'z1, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z, 'z0> fn(Cell<(&'z1 i32, &'a i32, &'b i32, &'c i32, &'d i32, &'e i32, &'f i32, &'g i32, &'h i32, &'i i32, &'j i32, &'k i32, &'l i32, &'m i32, &'n i32, &'o i32, &'p i32, &'q i32, &'r i32, &'s i32, &'t i32, &'u i32, &'v i32, &'w i32, &'x i32, &'y i32, &'z i32, &'z0 i32)>)` error: aborting due to previous error diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.rs b/tests/ui/traits/issue-91949-hangs-on-recursion.rs index 4a18ae5053f9..312d5d08c7df 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.rs +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.rs @@ -1,5 +1,5 @@ // build-fail -// compile-flags: -Zinline-mir=no -Zwrite-long-types-to-disk=yes +// compile-flags: -Zinline-mir=no // error-pattern: overflow evaluating the requirement ` as Iterator>::Item == ()` // error-pattern: function cannot return without recursing // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr index c721dd41a2c1..c6fc7bb12c9b 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr @@ -23,8 +23,7 @@ LL | impl> Iterator for IteratorOfWrapped { | | | unsatisfied trait bound introduced here = note: 256 redundant requirements hidden - = note: required for `IteratorOfWrapped<(), Map>, ...>>, ...>>` to implement `Iterator` - = note: the full type name has been written to '$TEST_BUILD_DIR/traits/issue-91949-hangs-on-recursion/issue-91949-hangs-on-recursion.long-type-hash.txt' + = note: required for `IteratorOfWrapped<(), Map>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>, [closure@$DIR/issue-91949-hangs-on-recursion.rs:28:45: 28:48]>>` to implement `Iterator` error: aborting due to previous error; 1 warning emitted From a2bcafa500fa407fa77716ab78b353b7d7daac5d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Jul 2023 11:44:58 +0200 Subject: [PATCH 089/105] interpret: refactor projection code to work on a common trait, and use that for visitors --- compiler/rustc_abi/src/lib.rs | 2 +- compiler/rustc_const_eval/messages.ftl | 5 +- .../rustc_const_eval/src/const_eval/mod.rs | 4 +- .../src/const_eval/valtrees.rs | 20 +- compiler/rustc_const_eval/src/errors.rs | 14 +- .../rustc_const_eval/src/interpret/cast.rs | 4 +- .../src/interpret/discriminant.rs | 28 +- .../rustc_const_eval/src/interpret/intern.rs | 127 ++-- .../src/interpret/intrinsics.rs | 6 +- .../interpret/intrinsics/caller_location.rs | 6 +- .../rustc_const_eval/src/interpret/mod.rs | 3 +- .../rustc_const_eval/src/interpret/operand.rs | 128 +++- .../src/interpret/operator.rs | 4 +- .../rustc_const_eval/src/interpret/place.rs | 146 ++-- .../src/interpret/projection.rs | 426 ++++------- .../rustc_const_eval/src/interpret/step.rs | 4 +- .../src/interpret/terminator.rs | 19 +- .../src/interpret/validity.rs | 116 ++- .../rustc_const_eval/src/interpret/visitor.rs | 676 +++++------------- .../rustc_middle/src/mir/interpret/error.rs | 8 +- compiler/rustc_middle/src/ty/layout.rs | 4 +- compiler/rustc_middle/src/ty/sty.rs | 5 - .../src/borrow_tracker/stacked_borrows/mod.rs | 4 +- .../src/borrow_tracker/tree_borrows/mod.rs | 8 +- src/tools/miri/src/eval.rs | 4 +- src/tools/miri/src/helpers.rs | 65 +- src/tools/miri/src/shims/backtrace.rs | 16 +- src/tools/miri/src/shims/env.rs | 2 +- src/tools/miri/src/shims/foreign_items.rs | 4 +- src/tools/miri/src/shims/intrinsics/simd.rs | 66 +- .../miri/src/shims/unix/foreign_items.rs | 2 +- src/tools/miri/src/shims/unix/fs.rs | 10 +- src/tools/miri/src/shims/unix/linux/fd.rs | 4 +- src/tools/miri/src/shims/unix/linux/sync.rs | 3 +- .../miri/src/shims/windows/foreign_items.rs | 2 +- src/tools/miri/tests/pass/intrinsics.rs | 5 +- ..._allocation.main.ConstProp.after.32bit.mir | 16 +- ..._allocation.main.ConstProp.after.64bit.mir | 16 +- .../consts/const-eval/raw-bytes.64bit.stderr | 4 +- .../ui/consts/const-eval/ub-enum.64bit.stderr | 23 +- tests/ui/consts/const-eval/ub-enum.rs | 12 +- tests/ui/consts/const_discriminant.rs | 7 - .../consts/extra-const-ub/detect-extra-ub.rs | 29 + .../detect-extra-ub.with_flag.stderr | 16 +- 44 files changed, 863 insertions(+), 1210 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index ef0c763ac203..0fc1da27b8f2 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1189,7 +1189,7 @@ impl FieldsShape { } FieldsShape::Array { stride, count } => { let i = u64::try_from(i).unwrap(); - assert!(i < count); + assert!(i < count, "tried to access field {} of array with {} fields", i, count); stride * i } FieldsShape::Arbitrary { ref offsets, .. } => offsets[FieldIdx::from_usize(i)], diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 86e47b2dbce0..671c2be1de9d 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -408,8 +408,11 @@ const_eval_undefined_behavior = const_eval_undefined_behavior_note = The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. +const_eval_uninhabited_enum_tag = {$front_matter}: encountered an uninhabited enum variant +const_eval_uninhabited_enum_variant_read = + read discriminant of an uninhabited enum variant const_eval_uninhabited_enum_variant_written = - writing discriminant of an uninhabited enum + writing discriminant of an uninhabited enum variant const_eval_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}` const_eval_uninit = {$front_matter}: encountered uninitialized bytes const_eval_uninit_bool = {$front_matter}: encountered uninitialized memory, but expected a boolean diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 0a3c3914ff97..655c89cac222 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -102,7 +102,7 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>( } ty::Adt(def, _) => { let variant = ecx.read_discriminant(&op).ok()?.1; - let down = ecx.operand_downcast(&op, variant).ok()?; + let down = ecx.project_downcast(&op, variant).ok()?; (def.variants()[variant].fields.len(), Some(variant), down) } ty::Tuple(args) => (args.len(), None, op), @@ -111,7 +111,7 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>( let fields_iter = (0..field_count) .map(|i| { - let field_op = ecx.operand_field(&down, i).ok()?; + let field_op = ecx.project_field(&down, i).ok()?; let val = op_to_const(&ecx, &field_op); Some((val, field_op.layout.ty)) }) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 9531d973eb32..5f65cd6ec253 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -2,11 +2,11 @@ use super::eval_queries::{mk_eval_cx, op_to_const}; use super::machine::CompileTimeEvalContext; use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::const_eval::CanAccessStatics; +use crate::interpret::MPlaceTy; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, - MemoryKind, PlaceTy, Scalar, + MemoryKind, PlaceTy, Projectable, Scalar, }; -use crate::interpret::{MPlaceTy, Value}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::source_map::DUMMY_SP; use rustc_target::abi::{Align, FieldIdx, VariantIdx, FIRST_VARIANT}; @@ -20,7 +20,7 @@ fn branches<'tcx>( num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { let place = match variant { - Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), + Some(variant) => ecx.project_downcast(place, variant).unwrap(), None => *place, }; let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); @@ -28,7 +28,7 @@ fn branches<'tcx>( let mut fields = Vec::with_capacity(n); for i in 0..n { - let field = ecx.mplace_field(&place, i).unwrap(); + let field = ecx.project_field(&place, i).unwrap(); let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; fields.push(Some(valtree)); } @@ -55,13 +55,11 @@ fn slice_branches<'tcx>( place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { - let n = place - .len(&ecx.tcx.tcx) - .unwrap_or_else(|_| panic!("expected to use len of place {:?}", place)); + let n = place.len(ecx).unwrap_or_else(|_| panic!("expected to use len of place {:?}", place)); let mut elems = Vec::with_capacity(n as usize); for i in 0..n { - let place_elem = ecx.mplace_index(place, i).unwrap(); + let place_elem = ecx.project_index(place, i).unwrap(); let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; elems.push(valtree); } @@ -386,7 +384,7 @@ fn valtree_into_mplace<'tcx>( debug!(?variant); ( - place.project_downcast(ecx, variant_idx).unwrap(), + ecx.project_downcast(place, variant_idx).unwrap(), &branches[1..], Some(variant_idx), ) @@ -401,7 +399,7 @@ fn valtree_into_mplace<'tcx>( debug!(?i, ?inner_valtree); let mut place_inner = match ty.kind() { - ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(), + ty::Str | ty::Slice(_) => ecx.project_index(place, i as u64).unwrap(), _ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) && i == branches.len() - 1 => { @@ -441,7 +439,7 @@ fn valtree_into_mplace<'tcx>( ) .unwrap() } - _ => ecx.mplace_field(&place_adjusted, i).unwrap(), + _ => ecx.project_field(&place_adjusted, i).unwrap(), }; debug!(?place_inner); diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 201b9425aff2..e1109e584b7b 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -511,7 +511,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes, DeadLocal => const_eval_dead_local, ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch, - UninhabitedEnumVariantWritten => const_eval_uninhabited_enum_variant_written, + UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written, + UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read, Validation(e) => e.diagnostic_message(), Custom(x) => (x.msg)(), } @@ -535,7 +536,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { | InvalidMeta(InvalidMetaKind::TooBig) | InvalidUninitBytes(None) | DeadLocal - | UninhabitedEnumVariantWritten => {} + | UninhabitedEnumVariantWritten(_) + | UninhabitedEnumVariantRead(_) => {} BoundsCheckFailed { len, index } => { builder.set_arg("len", len); builder.set_arg("index", index); @@ -623,6 +625,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { UnsafeCell => const_eval_unsafe_cell, UninhabitedVal { .. } => const_eval_uninhabited_val, InvalidEnumTag { .. } => const_eval_invalid_enum_tag, + UninhabitedEnumTag => const_eval_uninhabited_enum_tag, UninitEnumTag => const_eval_uninit_enum_tag, UninitStr => const_eval_uninit_str, Uninit { expected: ExpectedKind::Bool } => const_eval_uninit_bool, @@ -760,7 +763,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { | InvalidMetaSliceTooLarge { .. } | InvalidMetaTooLarge { .. } | DanglingPtrUseAfterFree { .. } - | DanglingPtrOutOfBounds { .. } => {} + | DanglingPtrOutOfBounds { .. } + | UninhabitedEnumTag => {} } } } @@ -835,7 +839,9 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { rustc_middle::error::middle_adjust_for_foreign_abi_error } InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized, - InvalidProgramInfo::ConstPropNonsense => panic!("We had const-prop nonsense, this should never be printed"), + InvalidProgramInfo::ConstPropNonsense => { + panic!("We had const-prop nonsense, this should never be printed") + } } } fn add_args( diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index dd7a1fcc1659..977e49b63431 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -420,8 +420,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if cast_ty_field.is_zst() { continue; } - let src_field = self.operand_field(src, i)?; - let dst_field = self.place_field(dest, i)?; + let src_field = self.project_field(src, i)?; + let dst_field = self.project_field(dest, i)?; if src_field.layout.ty == cast_ty_field.ty { self.copy_op(&src_field, &dst_field, /*allow_transmute*/ false)?; } else { diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index f23a455c2ca3..9ea5e7cb1f97 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -22,7 +22,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // When evaluating we will always error before even getting here, but ConstProp 'executes' // dead code, so we cannot ICE here. if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() { - throw_ub!(UninhabitedEnumVariantWritten) + throw_ub!(UninhabitedEnumVariantWritten(variant_index)) } match dest.layout.variants { @@ -47,7 +47,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let size = tag_layout.size(self); let tag_val = size.truncate(discr_val); - let tag_dest = self.place_field(dest, tag_field)?; + let tag_dest = self.project_field(dest, tag_field)?; self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?; } abi::Variants::Multiple { @@ -78,7 +78,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &niche_start_val, )?; // Write result. - let niche_dest = self.place_field(dest, tag_field)?; + let niche_dest = self.project_field(dest, tag_field)?; self.write_immediate(*tag_val, &niche_dest)?; } } @@ -106,6 +106,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { Variants::Single { index } => { + // Hilariously, `Single` is used even for 0-variant enums. + // (See https://github.com/rust-lang/rust/issues/89765). + if matches!(op.layout.ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { + throw_ub!(UninhabitedEnumVariantRead(index)) + } let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { Some(discr) => { // This type actually has discriminants. @@ -118,6 +123,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Scalar::from_uint(index.as_u32(), discr_layout.size) } }; + // For consisteny with `write_discriminant`, and to make sure that + // `project_downcast` cannot fail due to strange layouts, we declare immediate UB + // for uninhabited variants. + if op.layout.ty.is_enum() && op.layout.for_variant(self, index).abi.is_uninhabited() { + throw_ub!(UninhabitedEnumVariantRead(index)) + } return Ok((discr, index)); } Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { @@ -138,13 +149,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?; // Read tag and sanity-check `tag_layout`. - let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?; + let tag_val = self.read_immediate(&self.project_field(op, tag_field)?)?; assert_eq!(tag_layout.size, tag_val.layout.size); assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); trace!("tag value: {}", tag_val); // Figure out which discriminant and variant this corresponds to. - Ok(match *tag_encoding { + let (discr, index) = match *tag_encoding { TagEncoding::Direct => { let scalar = tag_val.to_scalar(); // Generate a specific error if `tag_val` is not an integer. @@ -232,6 +243,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // encoded in the tag. (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) } - }) + }; + // For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants. + if op.layout.for_variant(self, index).abi.is_uninhabited() { + throw_ub!(UninhabitedEnumVariantRead(index)) + } + Ok((discr, index)) } } diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 107e5bec6141..3a7fe8bd4781 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -164,75 +164,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory &self.ecx } - fn visit_aggregate( - &mut self, - mplace: &MPlaceTy<'tcx>, - fields: impl Iterator>, - ) -> InterpResult<'tcx> { - // We want to walk the aggregate to look for references to intern. While doing that we - // also need to take special care of interior mutability. - // - // As an optimization, however, if the allocation does not contain any references: we don't - // need to do the walk. It can be costly for big arrays for example (e.g. issue #93215). - let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> { - // ZSTs cannot contain pointers, we can avoid the interning walk. - if mplace.layout.is_zst() { - return Ok(false); - } - - // Now, check whether this allocation could contain references. - // - // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like - // to avoid could be expensive: on the potentially larger types, arrays and slices, - // rather than on all aggregates unconditionally. - if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) { - let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else { - // We do the walk if we can't determine the size of the mplace: we may be - // dealing with extern types here in the future. - return Ok(true); - }; - - // If there is no provenance in this allocation, it does not contain references - // that point to another allocation, and we can avoid the interning walk. - if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? { - if !alloc.has_provenance() { - return Ok(false); - } - } else { - // We're encountering a ZST here, and can avoid the walk as well. - return Ok(false); - } - } - - // In the general case, we do the walk. - Ok(true) - }; - - // If this allocation contains no references to intern, we avoid the potentially costly - // walk. - // - // We can do this before the checks for interior mutability below, because only references - // are relevant in that situation, and we're checking if there are any here. - if !is_walk_needed(mplace)? { - return Ok(()); - } - - if let Some(def) = mplace.layout.ty.ty_adt_def() { - if def.is_unsafe_cell() { - // We are crossing over an `UnsafeCell`, we can mutate again. This means that - // References we encounter inside here are interned as pointing to mutable - // allocations. - // Remember the `old` value to handle nested `UnsafeCell`. - let old = std::mem::replace(&mut self.inside_unsafe_cell, true); - let walked = self.walk_aggregate(mplace, fields); - self.inside_unsafe_cell = old; - return walked; - } - } - - self.walk_aggregate(mplace, fields) - } - fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> { // Handle Reference types, as these are the only types with provenance supported by const eval. // Raw pointers (and boxes) are handled by the `leftover_allocations` logic. @@ -315,7 +246,63 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory } Ok(()) } else { - // Not a reference -- proceed recursively. + // Not a reference. Check if we want to recurse. + let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> { + // ZSTs cannot contain pointers, we can avoid the interning walk. + if mplace.layout.is_zst() { + return Ok(false); + } + + // Now, check whether this allocation could contain references. + // + // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like + // to avoid could be expensive: on the potentially larger types, arrays and slices, + // rather than on all aggregates unconditionally. + if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) { + let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else { + // We do the walk if we can't determine the size of the mplace: we may be + // dealing with extern types here in the future. + return Ok(true); + }; + + // If there is no provenance in this allocation, it does not contain references + // that point to another allocation, and we can avoid the interning walk. + if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? { + if !alloc.has_provenance() { + return Ok(false); + } + } else { + // We're encountering a ZST here, and can avoid the walk as well. + return Ok(false); + } + } + + // In the general case, we do the walk. + Ok(true) + }; + + // If this allocation contains no references to intern, we avoid the potentially costly + // walk. + // + // We can do this before the checks for interior mutability below, because only references + // are relevant in that situation, and we're checking if there are any here. + if !is_walk_needed(mplace)? { + return Ok(()); + } + + if let Some(def) = mplace.layout.ty.ty_adt_def() { + if def.is_unsafe_cell() { + // We are crossing over an `UnsafeCell`, we can mutate again. This means that + // References we encounter inside here are interned as pointing to mutable + // allocations. + // Remember the `old` value to handle nested `UnsafeCell`. + let old = std::mem::replace(&mut self.inside_unsafe_cell, true); + let walked = self.walk_value(mplace); + self.inside_unsafe_cell = old; + return walked; + } + } + self.walk_value(mplace) } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 04cae23f852a..5f981c9b9188 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -425,11 +425,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); for i in 0..dest_len { - let place = self.mplace_index(&dest, i)?; + let place = self.project_index(&dest, i)?; let value = if i == index { elem.clone() } else { - self.mplace_index(&input, i)?.into() + self.project_index(&input, i)?.into() }; self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?; } @@ -444,7 +444,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { input_len ); self.copy_op( - &self.mplace_index(&input, index)?.into(), + &self.project_index(&input, index)?.into(), dest, /*allow_transmute*/ false, )?; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs index c4fe293bfacc..44a12751743d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs @@ -101,11 +101,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); // Initialize fields. - self.write_immediate(file.to_ref(self), &self.mplace_field(&location, 0).unwrap().into()) + self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap().into()) .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(line, &self.mplace_field(&location, 1).unwrap().into()) + self.write_scalar(line, &self.project_field(&location, 1).unwrap().into()) .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(col, &self.mplace_field(&location, 2).unwrap().into()) + self.write_scalar(col, &self.project_field(&location, 2).unwrap().into()) .expect("writing to memory we just allocated cannot fail"); location diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index f657f954f9c6..7974920bc14d 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -26,9 +26,10 @@ pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackP pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; pub use self::operand::{ImmTy, Immediate, OpTy, Operand}; pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; +pub use self::projection::Projectable; pub use self::terminator::FnArg; pub use self::validity::{CtfeValidationMode, RefTracking}; -pub use self::visitor::{MutValueVisitor, Value, ValueVisitor}; +pub use self::visitor::ValueVisitor; pub(crate) use self::intrinsics::eval_nullary_intrinsic; use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index e6d0a73c521e..be39e63ab4f5 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -1,6 +1,8 @@ //! Functions concerning immediate values and operands, and reading from operands. //! All high-level functions to read from memory work on operands as sources. +use std::assert_matches::assert_matches; + use either::{Either, Left, Right}; use rustc_hir::def::Namespace; @@ -14,7 +16,7 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size}; use super::{ alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, - Provenance, Scalar, + Projectable, Provenance, Scalar, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -199,6 +201,20 @@ impl<'tcx, Prov: Provenance> From> for OpTy<'tcx, Prov> { } } +impl<'tcx, Prov: Provenance> From<&'_ ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { + #[inline(always)] + fn from(val: &ImmTy<'tcx, Prov>) -> Self { + OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } + } +} + +impl<'tcx, Prov: Provenance> From<&'_ mut ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { + #[inline(always)] + fn from(val: &mut ImmTy<'tcx, Prov>) -> Self { + OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } + } +} + impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar(val: Scalar, layout: TyAndLayout<'tcx>) -> Self { @@ -243,12 +259,8 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { /// Compute the "sub-immediate" that is located within the `base` at the given offset with the /// given layout. - pub(super) fn offset( - &self, - offset: Size, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> Self { + // Not called `offset` to avoid confusion with the trait method. + fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { // This makes several assumptions about what layouts we will encounter; we match what // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). let inner_val: Immediate<_> = match (**self, self.layout.abi) { @@ -256,14 +268,28 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { (Immediate::Uninit, _) => Immediate::Uninit, // the field contains no information, can be left uninit _ if layout.is_zst() => Immediate::Uninit, + // some fieldless enum variants can have non-zero size but still `Aggregate` ABI... try + // to detect those here and also give them no data + _ if matches!(layout.abi, Abi::Aggregate { .. }) + && matches!(&layout.fields, abi::FieldsShape::Arbitrary { offsets, .. } if offsets.len() == 0) => + { + Immediate::Uninit + } // the field covers the entire type _ if layout.size == self.layout.size => { - assert!(match (self.layout.abi, layout.abi) { - (Abi::Scalar(..), Abi::Scalar(..)) => true, - (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, - _ => false, - }); - assert!(offset.bytes() == 0); + assert_eq!(offset.bytes(), 0); + assert!( + match (self.layout.abi, layout.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + _ => false, + }, + "cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}", + self.layout.ty, + layout.ty, + self.layout.abi, + layout.abi, + ); **self } // extract fields from types with `ScalarPair` ABI @@ -286,8 +312,42 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { } } +impl<'mir, 'tcx: 'mir, Prov: Provenance> Projectable<'mir, 'tcx, Prov> for ImmTy<'tcx, Prov> { + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + fn meta>( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta> { + assert!(self.layout.is_sized()); // unsized ImmTy can only exist temporarily and should never reach this here + Ok(MemPlaceMeta::None) + } + + fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway + Ok(self.offset_(offset, layout, cx)) + } + + fn to_op>( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.into()) + } +} + impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { - pub(super) fn meta(&self) -> InterpResult<'tcx, MemPlaceMeta> { + // Provided as inherent method since it doesn't need the `ecx` of `Projectable::meta`. + pub fn meta(&self) -> InterpResult<'tcx, MemPlaceMeta> { Ok(if self.layout.is_unsized() { if matches!(self.op, Operand::Immediate(_)) { // Unsized immediate OpTy cannot occur. We create a MemPlace for all unsized locals during argument passing. @@ -300,15 +360,24 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { MemPlaceMeta::None }) } +} - pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - self.meta()?.len(self.layout, cx) +impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> + for OpTy<'tcx, Prov> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout } - /// Offset the operand in memory (if possible) and change its metadata. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub(super) fn offset_with_meta( + fn meta>( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta> { + self.meta() + } + + fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta, @@ -320,22 +389,16 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { Right(imm) => { assert!(!meta.has_meta()); // no place to store metadata here // Every part of an uninit is uninit. - Ok(imm.offset(offset, layout, cx).into()) + Ok(imm.offset(offset, layout, cx)?.into()) } } } - /// Offset the operand in memory (if possible). - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub fn offset( + fn to_op>( &self, - offset: Size, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - assert!(layout.is_sized()); - self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.clone()) } } @@ -525,7 +588,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Every place can be read from, so we can turn them into an operand. /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this /// will never actually read from memory. - #[inline(always)] pub fn place_to_op( &self, place: &PlaceTy<'tcx, M::Provenance>, @@ -564,7 +626,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { - op = self.operand_projection(&op, elem)? + op = self.project(&op, elem)? } trace!("eval_place_to_op: got {:?}", *op); diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index e04764636cc1..49c3b152e1d7 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -38,9 +38,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to // do a component-wise write here. This code path is slower than the above because // `place_field` will have to `force_allocate` locals here. - let val_field = self.place_field(&dest, 0)?; + let val_field = self.project_field(dest, 0)?; self.write_scalar(val, &val_field)?; - let overflowed_field = self.place_field(&dest, 1)?; + let overflowed_field = self.project_field(dest, 1)?; self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?; } Ok(()) diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index dd07c5fa8776..9c492fb8b936 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -18,7 +18,7 @@ use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_V use super::{ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, - Pointer, Provenance, Scalar, + Pointer, Projectable, Provenance, Scalar, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -183,7 +183,8 @@ impl MemPlace { } #[inline] - fn offset_with_meta<'tcx>( + // Not called `offset_with_meta` to avoid confusion with the trait method. + fn offset_with_meta_<'tcx>( self, offset: Size, meta: MemPlaceMeta, @@ -195,11 +196,6 @@ impl MemPlace { ); Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta }) } - - #[inline] - fn offset<'tcx>(&self, offset: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - self.offset_with_meta(offset, MemPlaceMeta::None, cx) - } } impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { @@ -214,37 +210,6 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align } } - /// Offset the place in memory and change its metadata. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - #[inline] - pub(crate) fn offset_with_meta( - &self, - offset: Size, - meta: MemPlaceMeta, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - Ok(MPlaceTy { - mplace: self.mplace.offset_with_meta(offset, meta, cx)?, - align: self.align.restrict_for_offset(offset), - layout, - }) - } - - /// Offset the place in memory. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub fn offset( - &self, - offset: Size, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - assert!(layout.is_sized()); - self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) - } - #[inline] pub fn from_aligned_ptr(ptr: Pointer>, layout: TyAndLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } @@ -262,10 +227,42 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { align: layout.align.abi, } } +} - #[inline] - pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - self.mplace.meta.len(self.layout, cx) +impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> + for MPlaceTy<'tcx, Prov> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + fn meta>( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta> { + Ok(self.meta) + } + + fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + Ok(MPlaceTy { + mplace: self.mplace.offset_with_meta_(offset, meta, cx)?, + align: self.align.restrict_for_offset(offset), + layout, + }) + } + + fn to_op>( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.into()) } } @@ -293,7 +290,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { } } -impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { +impl<'tcx, Prov: Provenance + 'static> PlaceTy<'tcx, Prov> { /// A place is either an mplace or some local. #[inline] pub fn as_mplace_or_local( @@ -315,11 +312,24 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { ) }) } +} - /// Offset the place in memory and change its metadata. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub(crate) fn offset_with_meta( +impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> + for PlaceTy<'tcx, Prov> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + fn meta>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta> { + ecx.place_meta(self) + } + + fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta, @@ -346,17 +356,11 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { }) } - /// Offset the place in memory. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub fn offset( + fn to_op>( &self, - offset: Size, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - assert!(layout.is_sized()); - self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + ecx.place_to_op(self) } } @@ -506,7 +510,7 @@ where let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { - place = self.place_projection(&place, elem)? + place = self.project(&place, elem)? } trace!("{:?}", self.dump_place(place.place)); @@ -849,7 +853,7 @@ where &mut Operand::Indirect(mplace) => mplace, // this already was an indirect local }; if let Some(offset) = offset { - whole_local.offset(offset, self)? + whole_local.offset_with_meta_(offset, MemPlaceMeta::None, self)? } else { // Preserve wide place metadata, do not call `offset`. whole_local @@ -902,7 +906,7 @@ where self.write_uninit(&dest)?; let (variant_index, variant_dest, active_field_index) = match *kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { - let variant_dest = self.place_downcast(&dest, variant_index)?; + let variant_dest = self.project_downcast(dest, variant_index)?; (variant_index, variant_dest, active_field_index) } _ => (FIRST_VARIANT, dest.clone(), None), @@ -912,7 +916,7 @@ where } for (field_index, operand) in operands.iter_enumerated() { let field_index = active_field_index.unwrap_or(field_index); - let field_dest = self.place_field(&variant_dest, field_index.as_usize())?; + let field_dest = self.project_field(&variant_dest, field_index.as_usize())?; let op = self.eval_operand(operand, Some(field_dest.layout))?; self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?; } @@ -952,22 +956,24 @@ where Ok((mplace, vtable)) } - /// Turn an operand with a `dyn* Trait` type into an operand with the actual dynamic type. - /// Aso returns the vtable. - pub(super) fn unpack_dyn_star( + /// Turn a `dyn* Trait` type into an value with the actual dynamic type. + /// Also returns the vtable. + pub(super) fn unpack_dyn_star>( &self, - op: &OpTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, (OpTy<'tcx, M::Provenance>, Pointer>)> { + val: &P, + ) -> InterpResult<'tcx, (P, Pointer>)> { assert!( - matches!(op.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)), + matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), "`unpack_dyn_star` only makes sense on `dyn*` types" ); - let data = self.operand_field(&op, 0)?; - let vtable = self.operand_field(&op, 1)?; - let vtable = self.read_pointer(&vtable)?; + let data = self.project_field(val, 0)?; + let vtable = self.project_field(val, 1)?; + let vtable = self.read_pointer(&vtable.to_op(self)?)?; let (ty, _) = self.get_ptr_vtable(vtable)?; let layout = self.layout_of(ty)?; - let data = data.offset(Size::ZERO, layout, self)?; + // `data` is already the right thing but has the wrong type. So we transmute it, by + // projecting with offset 0. + let data = data.transmute(layout, self)?; Ok((data, vtable)) } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 5a3f19e5dc9b..ddcbc8350aae 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -11,12 +11,67 @@ use rustc_middle::mir; use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; +use rustc_middle::ty::TyCtxt; +use rustc_target::abi::HasDataLayout; use rustc_target::abi::Size; use rustc_target::abi::{self, VariantIdx}; -use super::{ - InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy, Provenance, Scalar, -}; +use super::MPlaceTy; +use super::{InterpCx, InterpResult, Machine, MemPlaceMeta, OpTy, Provenance, Scalar}; + +/// A thing that we can project into, and that has a layout. +pub trait Projectable<'mir, 'tcx: 'mir, Prov: Provenance>: Sized { + /// Get the layout. + fn layout(&self) -> TyAndLayout<'tcx>; + + /// Get the metadata of a wide value. + fn meta>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta>; + + fn len>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, u64> { + self.meta(ecx)?.len(self.layout(), ecx) + } + + /// Offset the value by the given amount, replacing the layout and metadata. + fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self>; + + fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert!(layout.is_sized()); + self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + } + + fn transmute( + &self, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert_eq!(self.layout().size, layout.size); + self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, cx) + } + + /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for + /// reading from this thing. + fn to_op>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; +} // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> @@ -24,24 +79,33 @@ where Prov: Provenance + 'static, M: Machine<'mir, 'tcx, Provenance = Prov>, { - //# Field access - - fn project_field( + /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is + /// always possible without allocating, so it can take `&self`. Also return the field's layout. + /// This supports both struct and array fields, but not slices! + /// + /// This also works for arrays, but then the `usize` index type is restricting. + /// For indexing into arrays, use `mplace_index`. + pub fn project_field>( &self, - base_layout: TyAndLayout<'tcx>, - base_meta: MemPlaceMeta, + base: &P, field: usize, - ) -> InterpResult<'tcx, (Size, MemPlaceMeta, TyAndLayout<'tcx>)> { - let offset = base_layout.fields.offset(field); - let field_layout = base_layout.field(self, field); + ) -> InterpResult<'tcx, P> { + // Slices nominally have length 0, so they will panic somewhere in `fields.offset`. + debug_assert!( + !matches!(base.layout().ty.kind(), ty::Slice(..)), + "`field` projection called on a slice -- call `index` projection instead" + ); + let offset = base.layout().fields.offset(field); + let field_layout = base.layout().field(self, field); // Offset may need adjustment for unsized fields. let (meta, offset) = if field_layout.is_unsized() { - if base_layout.is_sized() { + if base.layout().is_sized() { // An unsized field of a sized type? Sure... // But const-prop actually feeds us such nonsense MIR! throw_inval!(ConstPropNonsense); } + let base_meta = base.meta(self)?; // Re-use parent metadata to determine dynamic field layout. // With custom DSTS, this *will* execute user-defined code, but the same // happens at run-time so that's okay. @@ -60,189 +124,68 @@ where (MemPlaceMeta::None, offset) }; - Ok((offset, meta, field_layout)) - } - - /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is - /// always possible without allocating, so it can take `&self`. Also return the field's layout. - /// This supports both struct and array fields. - /// - /// This also works for arrays, but then the `usize` index type is restricting. - /// For indexing into arrays, use `mplace_index`. - pub fn mplace_field( - &self, - base: &MPlaceTy<'tcx, M::Provenance>, - field: usize, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let (offset, meta, field_layout) = self.project_field(base.layout, base.meta, field)?; - - // We do not look at `base.layout.align` nor `field_layout.align`, unlike - // codegen -- mostly to see if we can get away with that base.offset_with_meta(offset, meta, field_layout, self) } - /// Gets the place of a field inside the place, and also the field's type. - pub fn place_field( + /// Downcasting to an enum variant. + pub fn project_downcast>( &self, - base: &PlaceTy<'tcx, M::Provenance>, - field: usize, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let (offset, meta, field_layout) = - self.project_field(base.layout, self.place_meta(base)?, field)?; - base.offset_with_meta(offset, meta, field_layout, self) - } - - pub fn operand_field( - &self, - base: &OpTy<'tcx, M::Provenance>, - field: usize, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let (offset, meta, field_layout) = self.project_field(base.layout, base.meta()?, field)?; - base.offset_with_meta(offset, meta, field_layout, self) - } - - //# Downcasting - - pub fn mplace_downcast( - &self, - base: &MPlaceTy<'tcx, M::Provenance>, + base: &P, variant: VariantIdx, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + ) -> InterpResult<'tcx, P> { + assert!(!base.meta(self)?.has_meta()); // Downcasts only change the layout. // (In particular, no check about whether this is even the active variant -- that's by design, // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) - assert!(!base.meta.has_meta()); - let mut base = *base; - base.layout = base.layout.for_variant(self, variant); - Ok(base) + // So we just "offset" by 0. + let layout = base.layout().for_variant(self, variant); + if layout.abi.is_uninhabited() { + // `read_discriminant` should have excluded uninhabited variants... but ConstProp calls + // us on dead code. + throw_inval!(ConstPropNonsense) + } + // This cannot be `transmute` as variants *can* have a smaller size than the entire enum. + base.offset(Size::ZERO, layout, self) } - pub fn place_downcast( - &self, - base: &PlaceTy<'tcx, M::Provenance>, - variant: VariantIdx, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - // Downcast just changes the layout - let mut base = base.clone(); - base.layout = base.layout.for_variant(self, variant); - Ok(base) - } - - pub fn operand_downcast( - &self, - base: &OpTy<'tcx, M::Provenance>, - variant: VariantIdx, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // Downcast just changes the layout - let mut base = base.clone(); - base.layout = base.layout.for_variant(self, variant); - Ok(base) - } - - //# Slice and array indexing - /// Compute the offset and field layout for accessing the given index. - fn project_index( + pub fn project_index>( &self, - base_layout: TyAndLayout<'tcx>, - base_meta: MemPlaceMeta, + base: &P, index: u64, - ) -> InterpResult<'tcx, (Size, TyAndLayout<'tcx>)> { + ) -> InterpResult<'tcx, P> { // Not using the layout method because we want to compute on u64 - match base_layout.fields { + let (offset, field_layout) = match base.layout().fields { abi::FieldsShape::Array { stride, count: _ } => { // `count` is nonsense for slices, use the dynamic length instead. - let len = base_meta.len(base_layout, self)?; + let len = base.len(self)?; if index >= len { // This can only be reached in ConstProp and non-rustc-MIR. throw_ub!(BoundsCheckFailed { len, index }); } let offset = stride * index; // `Size` multiplication // All fields have the same layout. - let field_layout = base_layout.field(self, 0); - Ok((offset, field_layout)) + let field_layout = base.layout().field(self, 0); + (offset, field_layout) } _ => span_bug!( self.cur_span(), "`mplace_index` called on non-array type {:?}", - base_layout.ty + base.layout().ty ), - } - } - - #[inline(always)] - pub fn operand_index( - &self, - base: &OpTy<'tcx, M::Provenance>, - index: u64, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let (offset, field_layout) = self.project_index(base.layout, base.meta()?, index)?; - base.offset(offset, field_layout, self) - } - - /// Index into an array. - pub fn mplace_index( - &self, - base: &MPlaceTy<'tcx, M::Provenance>, - index: u64, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let (offset, field_layout) = self.project_index(base.layout, base.meta, index)?; - base.offset(offset, field_layout, self) - } - - pub fn place_index( - &self, - base: &PlaceTy<'tcx, M::Provenance>, - index: u64, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let (offset, field_layout) = - self.project_index(base.layout, self.place_meta(base)?, index)?; - base.offset(offset, field_layout, self) - } - - /// Iterates over all fields of an array. Much more efficient than doing the - /// same by repeatedly calling `operand_index`. - pub fn operand_array_fields<'a>( - &self, - base: &'a OpTy<'tcx, Prov>, - ) -> InterpResult<'tcx, impl Iterator>> + 'a> { - let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { - span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); }; - let len = base.len(self)?; - let field_layout = base.layout.field(self, 0); - let dl = &self.tcx.data_layout; - // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) + + base.offset(offset, field_layout, self) } - /// Iterates over all fields of an array. Much more efficient than doing the - /// same by repeatedly calling `place_index`. - pub fn place_array_fields<'a>( + fn project_constant_index>( &self, - base: &'a PlaceTy<'tcx, Prov>, - ) -> InterpResult<'tcx, impl Iterator>> + 'a> { - let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { - span_bug!(self.cur_span(), "place_array_fields: expected an array layout"); - }; - let len = self.place_meta(base)?.len(base.layout, self)?; - let field_layout = base.layout.field(self, 0); - let dl = &self.tcx.data_layout; - // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) - } - - //# ConstantIndex support - - fn project_constant_index( - &self, - base_layout: TyAndLayout<'tcx>, - base_meta: MemPlaceMeta, + base: &P, offset: u64, min_length: u64, from_end: bool, - ) -> InterpResult<'tcx, (Size, TyAndLayout<'tcx>)> { - let n = base_meta.len(base_layout, self)?; + ) -> InterpResult<'tcx, P> { + let n = base.len(self)?; if n < min_length { // This can only be reached in ConstProp and non-rustc-MIR. throw_ub!(BoundsCheckFailed { len: min_length, index: n }); @@ -256,49 +199,39 @@ where offset }; - self.project_index(base_layout, base_meta, index) + self.project_index(base, index) } - fn operand_constant_index( + /// Iterates over all fields of an array. Much more efficient than doing the + /// same by repeatedly calling `operand_index`. + pub fn project_array_fields<'a, P: Projectable<'mir, 'tcx, M::Provenance>>( &self, - base: &OpTy<'tcx, M::Provenance>, - offset: u64, - min_length: u64, - from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let (offset, layout) = - self.project_constant_index(base.layout, base.meta()?, offset, min_length, from_end)?; - base.offset(offset, layout, self) + base: &'a P, + ) -> InterpResult<'tcx, impl Iterator> + 'a> + where + 'tcx: 'a, + { + let abi::FieldsShape::Array { stride, .. } = base.layout().fields else { + span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); + }; + let len = base.len(self)?; + let field_layout = base.layout().field(self, 0); + let tcx: TyCtxt<'tcx> = *self.tcx; + // `Size` multiplication + Ok((0..len).map(move |i| { + base.offset_with_meta(stride * i, MemPlaceMeta::None, field_layout, &tcx) + })) } - fn place_constant_index( + /// Subslicing + fn project_subslice>( &self, - base: &PlaceTy<'tcx, M::Provenance>, - offset: u64, - min_length: u64, - from_end: bool, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let (offset, layout) = self.project_constant_index( - base.layout, - self.place_meta(base)?, - offset, - min_length, - from_end, - )?; - base.offset(offset, layout, self) - } - - //# Subslicing - - fn project_subslice( - &self, - base_layout: TyAndLayout<'tcx>, - base_meta: MemPlaceMeta, + base: &P, from: u64, to: u64, from_end: bool, - ) -> InterpResult<'tcx, (Size, MemPlaceMeta, TyAndLayout<'tcx>)> { - let len = base_meta.len(base_layout, self)?; // also asserts that we have a type where this makes sense + ) -> InterpResult<'tcx, P> { + let len = base.len(self)?; // also asserts that we have a type where this makes sense let actual_to = if from_end { if from.checked_add(to).map_or(true, |to| to > len) { // This can only be reached in ConstProp and non-rustc-MIR. @@ -311,16 +244,20 @@ where // Not using layout method because that works with usize, and does not work with slices // (that have count 0 in their layout). - let from_offset = match base_layout.fields { + let from_offset = match base.layout().fields { abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked _ => { - span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base_layout) + span_bug!( + self.cur_span(), + "unexpected layout of index access: {:#?}", + base.layout() + ) } }; // Compute meta and new layout let inner_len = actual_to.checked_sub(from).unwrap(); - let (meta, ty) = match base_layout.ty.kind() { + let (meta, ty) = match base.layout().ty.kind() { // It is not nice to match on the type, but that seems to be the only way to // implement this. ty::Array(inner, _) => { @@ -328,98 +265,45 @@ where } ty::Slice(..) => { let len = Scalar::from_target_usize(inner_len, self); - (MemPlaceMeta::Meta(len), base_layout.ty) + (MemPlaceMeta::Meta(len), base.layout().ty) } _ => { - span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base_layout.ty) + span_bug!( + self.cur_span(), + "cannot subslice non-array type: `{:?}`", + base.layout().ty + ) } }; let layout = self.layout_of(ty)?; - Ok((from_offset, meta, layout)) - } - fn operand_subslice( - &self, - base: &OpTy<'tcx, M::Provenance>, - from: u64, - to: u64, - from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let (from_offset, meta, layout) = - self.project_subslice(base.layout, base.meta()?, from, to, from_end)?; base.offset_with_meta(from_offset, meta, layout, self) } - pub fn place_subslice( - &self, - base: &PlaceTy<'tcx, M::Provenance>, - from: u64, - to: u64, - from_end: bool, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let (from_offset, meta, layout) = - self.project_subslice(base.layout, self.place_meta(base)?, from, to, from_end)?; - base.offset_with_meta(from_offset, meta, layout, self) - } - - //# Applying a general projection - - /// Projects into a place. + /// Applying a general projection #[instrument(skip(self), level = "trace")] - pub fn place_projection( - &self, - base: &PlaceTy<'tcx, M::Provenance>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + pub fn project

(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P> + where + P: Projectable<'mir, 'tcx, M::Provenance> + + From> + + std::fmt::Debug, + { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { - OpaqueCast(ty) => { - let mut place = base.clone(); - place.layout = self.layout_of(ty)?; - place - } - Field(field, _) => self.place_field(base, field.index())?, - Downcast(_, variant) => self.place_downcast(base, variant)?, - Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), + OpaqueCast(ty) => base.transmute(self.layout_of(ty)?, self)?, + Field(field, _) => self.project_field(base, field.index())?, + Downcast(_, variant) => self.project_downcast(base, variant)?, + Deref => self.deref_operand(&base.to_op(self)?)?.into(), Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; let n = self.local_to_op(self.frame(), local, Some(layout))?; let n = self.read_target_usize(&n)?; - self.place_index(base, n)? + self.project_index(base, n)? } ConstantIndex { offset, min_length, from_end } => { - self.place_constant_index(base, offset, min_length, from_end)? + self.project_constant_index(base, offset, min_length, from_end)? } - Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?, - }) - } - - #[instrument(skip(self), level = "trace")] - pub fn operand_projection( - &self, - base: &OpTy<'tcx, M::Provenance>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - OpaqueCast(ty) => { - let mut op = base.clone(); - op.layout = self.layout_of(ty)?; - op - } - Field(field, _) => self.operand_field(base, field.index())?, - Downcast(_, variant) => self.operand_downcast(base, variant)?, - Deref => self.deref_operand(base)?.into(), - Index(local) => { - let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.local_to_op(self.frame(), local, Some(layout))?; - let n = self.read_target_usize(&n)?; - self.operand_index(base, n)? - } - ConstantIndex { offset, min_length, from_end } => { - self.operand_constant_index(base, offset, min_length, from_end)? - } - Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?, + Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?, }) } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 619da8abb7d2..319c422134c5 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -8,7 +8,7 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::ty::layout::LayoutOf; -use super::{ImmTy, InterpCx, Machine}; +use super::{ImmTy, InterpCx, Machine, Projectable}; use crate::util; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -197,7 +197,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.get_place_alloc_mut(&dest)?; } else { // Write the src to the first element. - let first = self.mplace_field(&dest, 0)?; + let first = self.project_index(&dest, 0)?; self.copy_op(&src, &first.into(), /*allow_transmute*/ false)?; // This is performance-sensitive code for big static/const arrays! So we diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index f5e385703300..f934cca2517b 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -65,8 +65,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { field: usize, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { Ok(match arg { - FnArg::Copy(op) => FnArg::Copy(self.operand_field(op, field)?), - FnArg::InPlace(place) => FnArg::InPlace(self.place_field(place, field)?), + FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?), + FnArg::InPlace(place) => FnArg::InPlace(self.project_field(place, field)?), }) } @@ -382,8 +382,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // This all has to be in memory, there are no immediate unsized values. let src = caller_arg_copy.assert_mem_place(); // The destination cannot be one of these "spread args". - let (dest_frame, dest_local, dest_offset) = - callee_arg.as_mplace_or_local().right().expect("calee fn arguments must be locals"); + let (dest_frame, dest_local, dest_offset) = callee_arg + .as_mplace_or_local() + .right() + .expect("callee fn arguments must be locals"); // We are just initializing things, so there can't be anything here yet. assert!(matches!( *self.local_to_op(&self.stack()[dest_frame], dest_local, None)?, @@ -597,7 +599,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if Some(local) == body.spread_arg { // Must be a tuple for i in 0..dest.layout.fields.count() { - let dest = self.place_field(&dest, i)?; + let dest = self.project_field(&dest, i)?; let callee_abi = callee_args_abis.next().unwrap(); self.pass_argument(&mut caller_args, callee_abi, &dest)?; } @@ -679,7 +681,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Not there yet, search for the only non-ZST field. let mut non_zst_field = None; for i in 0..receiver.layout.fields.count() { - let field = self.operand_field(&receiver, i)?; + let field = self.project_field(&receiver, i)?; let zst = field.layout.is_zst() && field.layout.align.abi.bytes() == 1; if !zst { @@ -705,12 +707,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { - let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?; + let (recv, vptr) = self.unpack_dyn_star(&receiver_place)?; let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; if dyn_trait != data.principal() { throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch); } - let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one (vptr, dyn_ty, recv.ptr) } else { @@ -838,7 +839,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } ty::Dynamic(_, _, ty::DynStar) => { // Dropping a `dyn*`. Need to find actual drop fn. - self.unpack_dyn_star(&place.into())?.0.assert_mem_place() + self.unpack_dyn_star(&place)?.0 } _ => { debug_assert_eq!( diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 21c655988a0e..6618c70ac752 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -29,7 +29,7 @@ use std::hash::Hash; use super::UndefinedBehaviorInfo::*; use super::{ AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, - Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor, + Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor, }; macro_rules! throw_validation_failure { @@ -462,6 +462,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' /// Check if this is a value of primitive type, and if yes check the validity of the value /// at that type. Return `true` if the type is indeed primitive. + /// + /// Note that not all of these have `FieldsShape::Primitive`, e.g. wide references. fn try_visit_primitive( &mut self, value: &OpTy<'tcx, M::Provenance>, @@ -655,15 +657,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ) -> InterpResult<'tcx, VariantIdx> { self.with_elem(PathElem::EnumTag, move |this| { Ok(try_validation!( - this.ecx.read_discriminant(op), + this.ecx.read_discriminant(op).map(|(_, idx)| idx), this.path, InvalidTag(val) => InvalidEnumTag { value: format!("{val:x}"), }, - + UninhabitedEnumVariantRead(_) => UninhabitedEnumTag, InvalidUninitBytes(None) => UninitEnumTag, - ) - .1) + )) }) } @@ -733,60 +734,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } } - // Recursively walk the value at its type. - self.walk_value(op)?; - - // *After* all of this, check the ABI. We need to check the ABI to handle - // types like `NonNull` where the `Scalar` info is more restrictive than what - // the fields say (`rustc_layout_scalar_valid_range_start`). - // But in most cases, this will just propagate what the fields say, - // and then we want the error to point at the field -- so, first recurse, - // then check ABI. - // - // FIXME: We could avoid some redundant checks here. For newtypes wrapping - // scalars, we do the same check on every "level" (e.g., first we check - // MyNewtype and then the scalar in there). - match op.layout.abi { - Abi::Uninhabited => { - let ty = op.layout.ty; - throw_validation_failure!(self.path, UninhabitedVal { ty }); - } - Abi::Scalar(scalar_layout) => { - if !scalar_layout.is_uninit_valid() { - // There is something to check here. - let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?; - self.visit_scalar(scalar, scalar_layout)?; - } - } - Abi::ScalarPair(a_layout, b_layout) => { - // We can only proceed if *both* scalars need to be initialized. - // FIXME: find a way to also check ScalarPair when one side can be uninit but - // the other must be init. - if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() { - let (a, b) = - self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair(); - self.visit_scalar(a, a_layout)?; - self.visit_scalar(b, b_layout)?; - } - } - Abi::Vector { .. } => { - // No checks here, we assume layout computation gets this right. - // (This is harder to check since Miri does not represent these as `Immediate`. We - // also cannot use field projections since this might be a newtype around a vector.) - } - Abi::Aggregate { .. } => { - // Nothing to do. - } - } - - Ok(()) - } - - fn visit_aggregate( - &mut self, - op: &OpTy<'tcx, M::Provenance>, - fields: impl Iterator>, - ) -> InterpResult<'tcx> { + // Recursively walk the value at its type. Apply optimizations for some large types. match op.layout.ty.kind() { ty::Str => { let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate @@ -874,12 +822,58 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // ZST type, so either validation fails for all elements or none. ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => { // Validate just the first element (if any). - self.walk_aggregate(op, fields.take(1))? + if op.len(self.ecx)? > 0 { + self.visit_field(op, 0, &self.ecx.project_index(op, 0)?)?; + } } _ => { - self.walk_aggregate(op, fields)? // default handler + self.walk_value(op)?; // default handler } } + + // *After* all of this, check the ABI. We need to check the ABI to handle + // types like `NonNull` where the `Scalar` info is more restrictive than what + // the fields say (`rustc_layout_scalar_valid_range_start`). + // But in most cases, this will just propagate what the fields say, + // and then we want the error to point at the field -- so, first recurse, + // then check ABI. + // + // FIXME: We could avoid some redundant checks here. For newtypes wrapping + // scalars, we do the same check on every "level" (e.g., first we check + // MyNewtype and then the scalar in there). + match op.layout.abi { + Abi::Uninhabited => { + let ty = op.layout.ty; + throw_validation_failure!(self.path, UninhabitedVal { ty }); + } + Abi::Scalar(scalar_layout) => { + if !scalar_layout.is_uninit_valid() { + // There is something to check here. + let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?; + self.visit_scalar(scalar, scalar_layout)?; + } + } + Abi::ScalarPair(a_layout, b_layout) => { + // We can only proceed if *both* scalars need to be initialized. + // FIXME: find a way to also check ScalarPair when one side can be uninit but + // the other must be init. + if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() { + let (a, b) = + self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair(); + self.visit_scalar(a, a_layout)?; + self.visit_scalar(b, b_layout)?; + } + } + Abi::Vector { .. } => { + // No checks here, we assume layout computation gets this right. + // (This is harder to check since Miri does not represent these as `Immediate`. We + // also cannot use field projections since this might be a newtype around a vector.) + } + Abi::Aggregate { .. } => { + // Nothing to do. + } + } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 8d7c1953abe4..a50233fa3dea 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -1,544 +1,204 @@ //! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound //! types until we arrive at the leaves, with custom handling for primitive types. +use rustc_index::IndexVec; use rustc_middle::mir::interpret::InterpResult; use rustc_middle::ty; -use rustc_middle::ty::layout::TyAndLayout; +use rustc_target::abi::FieldIdx; use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; use std::num::NonZeroUsize; -use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy}; +use super::{InterpCx, MPlaceTy, Machine, Projectable}; -/// A thing that we can project into, and that has a layout. -/// This wouldn't have to depend on `Machine` but with the current type inference, -/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). -pub trait Value<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - /// Gets this value's layout. - fn layout(&self) -> TyAndLayout<'tcx>; +/// How to traverse a value and what to do when we are at the leaves. +pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { + type V: Projectable<'mir, 'tcx, M::Provenance> + + From> + + std::fmt::Debug; - /// Makes this into an `OpTy`, in a cheap way that is good for reading. - fn to_op_for_read( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; + /// The visitor must have an `InterpCx` in it. + fn ecx(&self) -> &InterpCx<'mir, 'tcx, M>; - /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. - fn to_op_for_proj( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - self.to_op_for_read(ecx) + /// `read_discriminant` can be hooked for better error messages. + #[inline(always)] + fn read_discriminant(&mut self, v: &Self::V) -> InterpResult<'tcx, VariantIdx> { + Ok(self.ecx().read_discriminant(&v.to_op(self.ecx())?)?.1) } - /// Creates this from an `OpTy`. + /// This function provides the chance to reorder the order in which fields are visited for + /// `FieldsShape::Aggregate`: The order of fields will be + /// `(0..num_fields).map(aggregate_field_order)`. /// - /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; - - /// Projects to the given enum variant. - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self>; - - /// Projects to the n-th field. - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self>; -} - -/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout. -/// This wouldn't have to depend on `Machine` but with the current type inference, -/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). -pub trait ValueMut<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - /// Gets this value's layout. - fn layout(&self) -> TyAndLayout<'tcx>; - - /// Makes this into an `OpTy`, in a cheap way that is good for reading. - fn to_op_for_read( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; - - /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. - fn to_op_for_proj( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; - - /// Creates this from an `OpTy`. - /// - /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; - - /// Projects to the given enum variant. - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self>; - - /// Projects to the n-th field. - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self>; -} - -// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we -// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.) -// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this -// double-impl, that would barely make the code shorter, if at all.) - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> { + /// The default means we iterate in source declaration order; alternative this can do an inverse + /// lookup in `memory_index` to use memory field order instead. #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout + fn aggregate_field_order(_memory_index: &IndexVec, idx: usize) -> usize { + idx } + // Recursive actions, ready to be overloaded. + /// Visits the given value, dispatching as appropriate to more specialized visitors. #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) + fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx> { + self.walk_value(v) + } + /// Visits the given value as a union. No automatic recursion can happen here. + #[inline(always)] + fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> { + Ok(()) + } + /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. + /// The type of `v` will be a raw pointer, but this is a field of `Box` and the + /// pointee type is the actual `T`. + #[inline(always)] + fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> { + Ok(()) } + /// Called each time we recurse down to a field of a "product-like" aggregate + /// (structs, tuples, arrays and the like, but not enums), passing in old (outer) + /// and new (inner) value. + /// This gives the visitor the chance to track the stack of nested fields that + /// we are descending through. #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - op.clone() + fn visit_field( + &mut self, + _old_val: &Self::V, + _field: usize, + new_val: &Self::V, + ) -> InterpResult<'tcx> { + self.visit_value(new_val) + } + /// Called when recursing into an enum variant. + /// This gives the visitor the chance to track the stack of nested fields that + /// we are descending through. + #[inline(always)] + fn visit_variant( + &mut self, + _old_val: &Self::V, + _variant: VariantIdx, + new_val: &Self::V, + ) -> InterpResult<'tcx> { + self.visit_value(new_val) } - #[inline(always)] - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.operand_downcast(self, variant) - } + fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> { + let ty = v.layout().ty; + trace!("walk_value: type: {ty}"); - #[inline(always)] - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.operand_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for OpTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) - } - - #[inline(always)] - fn to_op_for_proj( - &self, - _ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - op.clone() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.operand_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.operand_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands. - op.assert_mem_place() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) - } - - #[inline(always)] - fn to_op_for_proj( - &self, - _ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands. - op.assert_mem_place() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for PlaceTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // No need for `force_allocation` since we are just going to read from this. - ecx.place_to_op(self) - } - - #[inline(always)] - fn to_op_for_proj( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // We `force_allocation` here so that `from_op` below can work. - Ok(ecx.force_allocation(self)?.into()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - // assert is justified because our `to_op` only ever produces `Indirect` operands. - op.assert_mem_place().into() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.place_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.place_field(self, field) - } -} - -macro_rules! make_value_visitor { - ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => { - /// How to traverse a value and what to do when we are at the leaves. - pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - type V: $value_trait<'mir, 'tcx, M>; - - /// The visitor must have an `InterpCx` in it. - fn ecx(&$($mutability)? self) - -> &$($mutability)? InterpCx<'mir, 'tcx, M>; - - /// `read_discriminant` can be hooked for better error messages. - #[inline(always)] - fn read_discriminant( - &mut self, - op: &OpTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, VariantIdx> { - Ok(self.ecx().read_discriminant(op)?.1) + // Special treatment for special types, where the (static) layout is not sufficient. + match *ty.kind() { + // If it is a trait object, switch to the real type that was used to create it. + ty::Dynamic(_, _, ty::Dyn) => { + // Dyn types. This is unsized, and the actual dynamic type of the data is given by the + // vtable stored in the place metadata. + // unsized values are never immediate, so we can assert_mem_place + let op = v.to_op(self.ecx())?; + let dest = op.assert_mem_place(); + let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0; + trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); + // recurse with the inner type + return self.visit_field(&v, 0, &inner_mplace.into()); } + ty::Dynamic(_, _, ty::DynStar) => { + // DynStar types. Very different from a dyn type (but strangely part of the + // same variant in `TyKind`): These are pairs where the 2nd component is the + // vtable, and the first component is the data (which must be ptr-sized). + let data = self.ecx().unpack_dyn_star(v)?.0; + return self.visit_field(&v, 0, &data); + } + // Slices do not need special handling here: they have `Array` field + // placement with length 0, so we enter the `Array` case below which + // indirectly uses the metadata to determine the actual length. - // Recursive actions, ready to be overloaded. - /// Visits the given value, dispatching as appropriate to more specialized visitors. - #[inline(always)] - fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx> - { - self.walk_value(v) - } - /// Visits the given value as a union. No automatic recursion can happen here. - #[inline(always)] - fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> - { - Ok(()) - } - /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. - /// The type of `v` will be a raw pointer, but this is a field of `Box` and the - /// pointee type is the actual `T`. - #[inline(always)] - fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> - { - Ok(()) - } - /// Visits this value as an aggregate, you are getting an iterator yielding - /// all the fields (still in an `InterpResult`, you have to do error handling yourself). - /// Recurses into the fields. - #[inline(always)] - fn visit_aggregate( - &mut self, - v: &Self::V, - fields: impl Iterator>, - ) -> InterpResult<'tcx> { - self.walk_aggregate(v, fields) - } + // However, `Box`... let's talk about `Box`. + ty::Adt(def, ..) if def.is_box() => { + // `Box` is a hybrid primitive-library-defined type that one the one hand is + // a dereferenceable pointer, on the other hand has *basically arbitrary + // user-defined layout* since the user controls the 'allocator' field. So it + // cannot be treated like a normal pointer, since it does not fit into an + // `Immediate`. Yeah, it is quite terrible. But many visitors want to do + // something with "all boxed pointers", so we handle this mess for them. + // + // When we hit a `Box`, we do not do the usual field recursion; instead, + // we (a) call `visit_box` on the pointer value, and (b) recurse on the + // allocator field. We also assert tons of things to ensure we do not miss + // any other fields. - /// Called each time we recurse down to a field of a "product-like" aggregate - /// (structs, tuples, arrays and the like, but not enums), passing in old (outer) - /// and new (inner) value. - /// This gives the visitor the chance to track the stack of nested fields that - /// we are descending through. - #[inline(always)] - fn visit_field( - &mut self, - _old_val: &Self::V, - _field: usize, - new_val: &Self::V, - ) -> InterpResult<'tcx> { - self.visit_value(new_val) - } - /// Called when recursing into an enum variant. - /// This gives the visitor the chance to track the stack of nested fields that - /// we are descending through. - #[inline(always)] - fn visit_variant( - &mut self, - _old_val: &Self::V, - _variant: VariantIdx, - new_val: &Self::V, - ) -> InterpResult<'tcx> { - self.visit_value(new_val) - } + // `Box` has two fields: the pointer we care about, and the allocator. + assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); + let (unique_ptr, alloc) = + (self.ecx().project_field(v, 0)?, self.ecx().project_field(v, 1)?); + // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`... + // (which means another 2 fields, the second of which is a `PhantomData`) + assert_eq!(unique_ptr.layout().fields.count(), 2); + let (nonnull_ptr, phantom) = ( + self.ecx().project_field(&unique_ptr, 0)?, + self.ecx().project_field(&unique_ptr, 1)?, + ); + assert!( + phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), + "2nd field of `Unique` should be PhantomData but is {:?}", + phantom.layout().ty, + ); + // ... that contains a `NonNull`... (gladly, only a single field here) + assert_eq!(nonnull_ptr.layout().fields.count(), 1); + let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr + // ... whose only field finally is a raw ptr we can dereference. + self.visit_box(&raw_ptr)?; - // Default recursors. Not meant to be overloaded. - fn walk_aggregate( - &mut self, - v: &Self::V, - fields: impl Iterator>, - ) -> InterpResult<'tcx> { - // Now iterate over it. - for (idx, field_val) in fields.enumerate() { - self.visit_field(v, idx, &field_val?)?; + // The second `Box` field is the allocator, which we recursively check for validity + // like in regular structs. + self.visit_field(v, 1, &alloc)?; + + // We visited all parts of this one. + return Ok(()); + } + _ => {} + }; + + // Visit the fields of this value. + match &v.layout().fields { + FieldsShape::Primitive => {} + &FieldsShape::Union(fields) => { + self.visit_union(v, fields)?; + } + FieldsShape::Arbitrary { offsets, memory_index } => { + for idx in 0..offsets.len() { + let idx = Self::aggregate_field_order(memory_index, idx); + let field = self.ecx().project_field(v, idx)?; + self.visit_field(v, idx, &field)?; } - Ok(()) } - fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> - { - let ty = v.layout().ty; - trace!("walk_value: type: {ty}"); - - // Special treatment for special types, where the (static) layout is not sufficient. - match *ty.kind() { - // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(_, _, ty::Dyn) => { - // Dyn types. This is unsized, and the actual dynamic type of the data is given by the - // vtable stored in the place metadata. - // unsized values are never immediate, so we can assert_mem_place - let op = v.to_op_for_read(self.ecx())?; - let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0; - trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); - // recurse with the inner type - return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); - }, - ty::Dynamic(_, _, ty::DynStar) => { - // DynStar types. Very different from a dyn type (but strangely part of the - // same variant in `TyKind`): These are pairs where the 2nd component is the - // vtable, and the first component is the data (which must be ptr-sized). - let op = v.to_op_for_proj(self.ecx())?; - let data = self.ecx().unpack_dyn_star(&op)?.0; - return self.visit_field(&v, 0, &$value_trait::from_op(&data)); - } - // Slices do not need special handling here: they have `Array` field - // placement with length 0, so we enter the `Array` case below which - // indirectly uses the metadata to determine the actual length. - - // However, `Box`... let's talk about `Box`. - ty::Adt(def, ..) if def.is_box() => { - // `Box` is a hybrid primitive-library-defined type that one the one hand is - // a dereferenceable pointer, on the other hand has *basically arbitrary - // user-defined layout* since the user controls the 'allocator' field. So it - // cannot be treated like a normal pointer, since it does not fit into an - // `Immediate`. Yeah, it is quite terrible. But many visitors want to do - // something with "all boxed pointers", so we handle this mess for them. - // - // When we hit a `Box`, we do not do the usual `visit_aggregate`; instead, - // we (a) call `visit_box` on the pointer value, and (b) recurse on the - // allocator field. We also assert tons of things to ensure we do not miss - // any other fields. - - // `Box` has two fields: the pointer we care about, and the allocator. - assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); - let (unique_ptr, alloc) = - (v.project_field(self.ecx(), 0)?, v.project_field(self.ecx(), 1)?); - // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`... - // (which means another 2 fields, the second of which is a `PhantomData`) - assert_eq!(unique_ptr.layout().fields.count(), 2); - let (nonnull_ptr, phantom) = ( - unique_ptr.project_field(self.ecx(), 0)?, - unique_ptr.project_field(self.ecx(), 1)?, - ); - assert!( - phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), - "2nd field of `Unique` should be PhantomData but is {:?}", - phantom.layout().ty, - ); - // ... that contains a `NonNull`... (gladly, only a single field here) - assert_eq!(nonnull_ptr.layout().fields.count(), 1); - let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr - // ... whose only field finally is a raw ptr we can dereference. - self.visit_box(&raw_ptr)?; - - // The second `Box` field is the allocator, which we recursively check for validity - // like in regular structs. - self.visit_field(v, 1, &alloc)?; - - // We visited all parts of this one. - return Ok(()); - } - _ => {}, - }; - - // Visit the fields of this value. - match &v.layout().fields { - FieldsShape::Primitive => {} - &FieldsShape::Union(fields) => { - self.visit_union(v, fields)?; - } - FieldsShape::Arbitrary { offsets, .. } => { - // FIXME: We collect in a vec because otherwise there are lifetime - // errors: Projecting to a field needs access to `ecx`. - let fields: Vec> = - (0..offsets.len()).map(|i| { - v.project_field(self.ecx(), i) - }) - .collect(); - self.visit_aggregate(v, fields.into_iter())?; - } - FieldsShape::Array { .. } => { - // Let's get an mplace (or immediate) first. - // FIXME: This might `force_allocate` if `v` is a `PlaceTy`! - let op = v.to_op_for_proj(self.ecx())?; - // Now we can go over all the fields. - // This uses the *run-time length*, i.e., if we are a slice, - // the dynamic info from the metadata is used. - let iter = self.ecx().operand_array_fields(&op)? - .map(|f| f.and_then(|f| { - Ok($value_trait::from_op(&f)) - })); - self.visit_aggregate(v, iter)?; - } - } - - match v.layout().variants { - // If this is a multi-variant layout, find the right variant and proceed - // with *its* fields. - Variants::Multiple { .. } => { - let op = v.to_op_for_read(self.ecx())?; - let idx = self.read_discriminant(&op)?; - let inner = v.project_downcast(self.ecx(), idx)?; - trace!("walk_value: variant layout: {:#?}", inner.layout()); - // recurse with the inner type - self.visit_variant(v, idx, &inner) - } - // For single-variant layouts, we already did anything there is to do. - Variants::Single { .. } => Ok(()) + FieldsShape::Array { .. } => { + for (idx, field) in self.ecx().project_array_fields(v)?.enumerate() { + self.visit_field(v, idx, &field?)?; } } } + + match v.layout().variants { + // If this is a multi-variant layout, find the right variant and proceed + // with *its* fields. + Variants::Multiple { .. } => { + let idx = self.read_discriminant(v)?; + // There are 3 cases where downcasts can turn a Scalar/ScalarPair into a different ABI which + // could be a problem for `ImmTy` (see layout_sanity_check): + // - variant.size == Size::ZERO: works fine because `ImmTy::offset` has a special case for + // zero-sized layouts. + // - variant.fields.count() == 0: works fine because `ImmTy::offset` has a special case for + // zero-field aggregates. + // - variant.abi.is_uninhabited(): triggers UB in `read_discriminant` so we never get here. + let inner = self.ecx().project_downcast(v, idx)?; + trace!("walk_value: variant layout: {:#?}", inner.layout()); + // recurse with the inner type + self.visit_variant(v, idx, &inner)?; + } + // For single-variant layouts, we already did anything there is to do. + Variants::Single { .. } => {} + } + + Ok(()) } } - -make_value_visitor!(ValueVisitor, Value,); -make_value_visitor!(MutValueVisitor, ValueMut, mut); diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index c4803448e2bf..690fcfe1be40 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -12,7 +12,8 @@ use rustc_errors::{ use rustc_macros::HashStable; use rustc_session::CtfeBacktrace; use rustc_span::def_id::DefId; -use rustc_target::abi::{call, Align, Size, WrappingRange}; +use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange}; + use std::borrow::Cow; use std::{any::Any, backtrace::Backtrace, fmt}; @@ -323,7 +324,9 @@ pub enum UndefinedBehaviorInfo<'a> { /// Data size is not equal to target size. ScalarSizeMismatch(ScalarSizeMismatch), /// A discriminant of an uninhabited enum variant is written. - UninhabitedEnumVariantWritten, + UninhabitedEnumVariantWritten(VariantIdx), + /// An uninhabited enum variant is projected. + UninhabitedEnumVariantRead(VariantIdx), /// Validation error. Validation(ValidationErrorInfo<'a>), // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically @@ -393,6 +396,7 @@ pub enum ValidationErrorKind<'tcx> { UnsafeCell, UninhabitedVal { ty: Ty<'tcx> }, InvalidEnumTag { value: String }, + UninhabitedEnumTag, UninitEnumTag, UninitStr, Uninit { expected: ExpectedKind }, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 62805d1e8b5c..81e7dc3728ab 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -741,9 +741,9 @@ where let fields = match this.ty.kind() { ty::Adt(def, _) if def.variants().is_empty() => - bug!("for_variant called on zero-variant enum"), + bug!("for_variant called on zero-variant enum {}", this.ty), ty::Adt(def, _) => def.variant(variant_index).fields.len(), - _ => bug!(), + _ => bug!("`ty_and_layout_for_variant` on unexpected type {}", this.ty), }; tcx.mk_layout(LayoutS { variants: Variants::Single { index: variant_index }, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index f9c1ca9a8b16..3e023ccdead6 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2670,11 +2670,6 @@ impl<'tcx> Ty<'tcx> { variant_index: VariantIdx, ) -> Option> { match self.kind() { - TyKind::Adt(adt, _) if adt.variants().is_empty() => { - // This can actually happen during CTFE, see - // https://github.com/rust-lang/rust/issues/89765. - None - } TyKind::Adt(adt, _) if adt.is_enum() => { Some(adt.discriminant_for_variant(tcx, variant_index)) } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 15a7d72edf12..b331158eb12f 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -930,13 +930,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } } - impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>> + impl<'ecx, 'mir, 'tcx> ValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>> for RetagVisitor<'ecx, 'mir, 'tcx> { type V = PlaceTy<'tcx, Provenance>; #[inline(always)] - fn ecx(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> { + fn ecx(&self) -> &MiriInterpCx<'mir, 'tcx> { self.ecx } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index e7e8fa27f82f..9073f6954429 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -413,13 +413,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } } - impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>> + impl<'ecx, 'mir, 'tcx> ValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>> for RetagVisitor<'ecx, 'mir, 'tcx> { type V = PlaceTy<'tcx, Provenance>; #[inline(always)] - fn ecx(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> { + fn ecx(&self) -> &MiriInterpCx<'mir, 'tcx> { self.ecx } @@ -585,7 +585,7 @@ fn inner_ptr_of_unique<'tcx>( // `rustc_const_eval`, just with one fewer layer. // Here we have a `Unique(NonNull(*mut), PhantomData)` assert_eq!(place.layout.fields.count(), 2, "Unique must have exactly 2 fields"); - let (nonnull, phantom) = (ecx.place_field(place, 0)?, ecx.place_field(place, 1)?); + let (nonnull, phantom) = (ecx.project_field(place, 0)?, ecx.project_field(place, 1)?); assert!( phantom.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), "2nd field of `Unique` should be `PhantomData` but is `{:?}`", @@ -593,7 +593,7 @@ fn inner_ptr_of_unique<'tcx>( ); // Now down to `NonNull(*mut)` assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field"); - let ptr = ecx.place_field(&nonnull, 0)?; + let ptr = ecx.project_field(&nonnull, 0)?; // Finally a plain `*mut` Ok(ptr) } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index ed3d741db1cb..89d8a865149e 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -320,7 +320,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ))?; let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; for (idx, arg) in argvs.into_iter().enumerate() { - let place = ecx.mplace_field(&argvs_place, idx)?; + let place = ecx.project_field(&argvs_place, idx)?; ecx.write_immediate(arg, &place.into())?; } ecx.mark_immutable(&argvs_place); @@ -354,7 +354,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ecx.machine.cmd_line = Some(*cmd_place); // Store the UTF-16 string. We just allocated so we know the bounds are fine. for (idx, &c) in cmd_utf16.iter().enumerate() { - let place = ecx.mplace_field(&cmd_place, idx)?; + let place = ecx.project_field(&cmd_place, idx)?; ecx.write_scalar(Scalar::from_u16(c), &place.into())?; } ecx.mark_immutable(&cmd_place); diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index ab143cff7fa9..f6a438f5d629 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -10,6 +10,7 @@ use log::trace; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc_index::IndexVec; use rustc_middle::mir; use rustc_middle::ty::{ self, @@ -17,7 +18,7 @@ use rustc_middle::ty::{ List, TyCtxt, }; use rustc_span::{def_id::CrateNum, sym, Span, Symbol}; -use rustc_target::abi::{Align, FieldsShape, Size, Variants}; +use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants}; use rustc_target::spec::abi::Abi; use rand::RngCore; @@ -229,20 +230,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.layout_of(ty).unwrap() } - /// Project to the given *named* field of the mplace (which must be a struct or union type). - fn mplace_field_named( + /// Project to the given *named* field (which must be a struct or union type). + fn project_field_named>( &self, - mplace: &MPlaceTy<'tcx, Provenance>, + base: &P, name: &str, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, P> { let this = self.eval_context_ref(); - let adt = mplace.layout.ty.ty_adt_def().unwrap(); + let adt = base.layout().ty.ty_adt_def().unwrap(); for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() { if field.name.as_str() == name { - return this.mplace_field(mplace, idx); + return this.project_field(base, idx); } } - bug!("No field named {} in type {}", name, mplace.layout.ty); + bug!("No field named {} in type {}", name, base.layout().ty); } /// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned, @@ -270,7 +271,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); for (idx, &val) in values.iter().enumerate() { - let field = this.mplace_field(dest, idx)?; + let field = this.project_field(dest, idx)?; this.write_int(val, &field.into())?; } Ok(()) @@ -284,7 +285,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); for &(name, val) in values.iter() { - let field = this.mplace_field_named(dest, name)?; + let field = this.project_field_named(dest, name)?; this.write_int(val, &field.into())?; } Ok(()) @@ -479,6 +480,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { self.ecx } + fn aggregate_field_order(memory_index: &IndexVec, idx: usize) -> usize { + // We need to do an *inverse* lookup: find the field that has position `idx` in memory order. + for (src_field, &mem_pos) in memory_index.iter_enumerated() { + if mem_pos as usize == idx { + return src_field.as_usize(); + } + } + panic!("invalid `memory_index`, could not find {}-th field in memory order", idx); + } + // Hook to detect `UnsafeCell`. fn visit_value(&mut self, v: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty); @@ -524,33 +535,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - // Make sure we visit aggregates in increasing offset order. - fn visit_aggregate( - &mut self, - place: &MPlaceTy<'tcx, Provenance>, - fields: impl Iterator>>, - ) -> InterpResult<'tcx> { - match place.layout.fields { - FieldsShape::Array { .. } => { - // For the array layout, we know the iterator will yield sorted elements so - // we can avoid the allocation. - self.walk_aggregate(place, fields) - } - FieldsShape::Arbitrary { .. } => { - // Gather the subplaces and sort them before visiting. - let mut places = fields - .collect::>>>()?; - // we just compare offsets, the abs. value never matters - places.sort_by_key(|place| place.ptr.addr()); - self.walk_aggregate(place, places.into_iter().map(Ok)) - } - FieldsShape::Union { .. } | FieldsShape::Primitive => { - // Uh, what? - bug!("unions/primitives are not aggregates we should ever visit") - } - } - } - fn visit_union( &mut self, _v: &MPlaceTy<'tcx, Provenance>, @@ -746,7 +730,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(mplace) } - fn deref_pointer_as( + /// Deref' a pointer *without* checking that the place is dereferenceable. + fn deref_pointer_unchecked( &self, val: &ImmTy<'tcx, Provenance>, layout: TyAndLayout<'tcx>, @@ -811,10 +796,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { tp: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - let seconds_place = this.mplace_field(tp, 0)?; + let seconds_place = this.project_field(tp, 0)?; let seconds_scalar = this.read_scalar(&seconds_place.into())?; let seconds = seconds_scalar.to_target_isize(this)?; - let nanoseconds_place = this.mplace_field(tp, 1)?; + let nanoseconds_place = this.project_field(tp, 1)?; let nanoseconds_scalar = this.read_scalar(&nanoseconds_place.into())?; let nanoseconds = nanoseconds_scalar.to_target_isize(this)?; diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index adf9a35d5c3a..e4aa7467cbe0 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -83,7 +83,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Write pointers into array for (i, ptr) in ptrs.into_iter().enumerate() { - let place = this.mplace_index(&alloc, i as u64)?; + let place = this.project_index(&alloc, i as u64)?; this.write_pointer(ptr, &place.into())?; } @@ -196,33 +196,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_immediate( name_alloc.to_ref(this), - &this.mplace_field(&dest, 0)?.into(), + &this.project_field(&dest, 0)?.into(), )?; this.write_immediate( filename_alloc.to_ref(this), - &this.mplace_field(&dest, 1)?.into(), + &this.project_field(&dest, 1)?.into(), )?; } 1 => { this.write_scalar( Scalar::from_target_usize(name.len().try_into().unwrap(), this), - &this.mplace_field(&dest, 0)?.into(), + &this.project_field(&dest, 0)?.into(), )?; this.write_scalar( Scalar::from_target_usize(filename.len().try_into().unwrap(), this), - &this.mplace_field(&dest, 1)?.into(), + &this.project_field(&dest, 1)?.into(), )?; } _ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags), } - this.write_scalar(Scalar::from_u32(lineno), &this.mplace_field(&dest, 2)?.into())?; - this.write_scalar(Scalar::from_u32(colno), &this.mplace_field(&dest, 3)?.into())?; + this.write_scalar(Scalar::from_u32(lineno), &this.project_field(&dest, 2)?.into())?; + this.write_scalar(Scalar::from_u32(colno), &this.project_field(&dest, 3)?.into())?; // Support a 4-field struct for now - this is deprecated // and slated for removal. if num_fields == 5 { - this.write_pointer(fn_ptr, &this.mplace_field(&dest, 4)?.into())?; + this.write_pointer(fn_ptr, &this.project_field(&dest, 4)?.into())?; } Ok(()) diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 1dcb877a83f0..f98fd0431ae6 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -456,7 +456,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ))?; let vars_place = this.allocate(vars_layout, MiriMemoryKind::Runtime.into())?; for (idx, var) in vars.into_iter().enumerate() { - let place = this.mplace_field(&vars_place, idx)?; + let place = this.project_field(&vars_place, idx)?; this.write_pointer(var, &place.into())?; } this.write_pointer(vars_place.ptr, &this.machine.env_vars.environ.unwrap().into())?; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 72a4adba8d41..c753eadbbad3 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -942,9 +942,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(clippy::arithmetic_side_effects)] // it's a u128, we can shift by 64 let (c_out, sum) = ((wide_sum >> 64).truncate::(), wide_sum.truncate::()); - let c_out_field = this.place_field(dest, 0)?; + let c_out_field = this.project_field(dest, 0)?; this.write_scalar(Scalar::from_u8(c_out), &c_out_field)?; - let sum_field = this.place_field(dest, 1)?; + let sum_field = this.project_field(dest, 1)?; this.write_scalar(Scalar::from_u64(sum), &sum_field)?; } "llvm.x86.sse2.pause" diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 94f8cfbfb1c3..3afe5214f08b 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -57,8 +57,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; for i in 0..dest_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; + let op = this.read_immediate(&this.project_index(&op, i)?.into())?; + let dest = this.project_index(&dest, i)?; let val = match which { Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar(), Op::Abs => { @@ -172,9 +172,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; for i in 0..dest_len { - let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?; - let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; + let left = this.read_immediate(&this.project_index(&left, i)?.into())?; + let right = this.read_immediate(&this.project_index(&right, i)?.into())?; + let dest = this.project_index(&dest, i)?; let val = match which { Op::MirOp(mir_op) => { let (val, overflowed, ty) = this.overflowing_binary_op(mir_op, &left, &right)?; @@ -232,10 +232,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, c_len); for i in 0..dest_len { - let a = this.read_scalar(&this.mplace_index(&a, i)?.into())?; - let b = this.read_scalar(&this.mplace_index(&b, i)?.into())?; - let c = this.read_scalar(&this.mplace_index(&c, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; + let a = this.read_scalar(&this.project_index(&a, i)?.into())?; + let b = this.read_scalar(&this.project_index(&b, i)?.into())?; + let c = this.read_scalar(&this.project_index(&c, i)?.into())?; + let dest = this.project_index(&dest, i)?; // Works for f32 and f64. // FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468. @@ -295,13 +295,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // Initialize with first lane, then proceed with the rest. - let mut res = this.read_immediate(&this.mplace_index(&op, 0)?.into())?; + let mut res = this.read_immediate(&this.project_index(&op, 0)?.into())?; if matches!(which, Op::MirOpBool(_)) { // Convert to `bool` scalar. res = imm_from_bool(simd_element_to_bool(res)?); } for i in 1..op_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + let op = this.read_immediate(&this.project_index(&op, i)?.into())?; res = match which { Op::MirOp(mir_op) => { this.binary_op(mir_op, &res, &op)? @@ -355,7 +355,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut res = init; for i in 0..op_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + let op = this.read_immediate(&this.project_index(&op, i)?.into())?; res = this.binary_op(mir_op, &res, &op)?; } this.write_immediate(*res, dest)?; @@ -372,10 +372,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, no_len); for i in 0..dest_len { - let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; - let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; - let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; + let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?; + let yes = this.read_immediate(&this.project_index(&yes, i)?.into())?; + let no = this.read_immediate(&this.project_index(&no, i)?.into())?; + let dest = this.project_index(&dest, i)?; let val = if simd_element_to_bool(mask)? { yes } else { no }; this.write_immediate(*val, &dest.into())?; @@ -403,9 +403,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { & 1u64 .checked_shl(simd_bitmask_index(i, dest_len, this.data_layout().endian)) .unwrap(); - let yes = this.read_immediate(&this.mplace_index(&yes, i.into())?.into())?; - let no = this.read_immediate(&this.mplace_index(&no, i.into())?.into())?; - let dest = this.mplace_index(&dest, i.into())?; + let yes = this.read_immediate(&this.project_index(&yes, i.into())?.into())?; + let no = this.read_immediate(&this.project_index(&no, i.into())?.into())?; + let dest = this.project_index(&dest, i.into())?; let val = if mask != 0 { yes } else { no }; this.write_immediate(*val, &dest.into())?; @@ -435,8 +435,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let from_exposed_cast = intrinsic_name == "from_exposed_addr"; for i in 0..dest_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; + let op = this.read_immediate(&this.project_index(&op, i)?.into())?; + let dest = this.project_index(&dest, i)?; let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { // Int-to-(int|float): always safe @@ -496,17 +496,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { for i in 0..dest_len { let src_index: u64 = this - .read_immediate(&this.operand_index(index, i)?)? + .read_immediate(&this.project_index(index, i)?)? .to_scalar() .to_u32()? .into(); - let dest = this.mplace_index(&dest, i)?; + let dest = this.project_index(&dest, i)?; let val = if src_index < left_len { - this.read_immediate(&this.mplace_index(&left, src_index)?.into())? + this.read_immediate(&this.project_index(&left, src_index)?.into())? } else if src_index < left_len.checked_add(right_len).unwrap() { let right_idx = src_index.checked_sub(left_len).unwrap(); - this.read_immediate(&this.mplace_index(&right, right_idx)?.into())? + this.read_immediate(&this.project_index(&right, right_idx)?.into())? } else { span_bug!( this.cur_span(), @@ -528,10 +528,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, mask_len); for i in 0..dest_len { - let passthru = this.read_immediate(&this.mplace_index(&passthru, i)?.into())?; - let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?; - let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; + let passthru = this.read_immediate(&this.project_index(&passthru, i)?.into())?; + let ptr = this.read_immediate(&this.project_index(&ptrs, i)?.into())?; + let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?; + let dest = this.project_index(&dest, i)?; let val = if simd_element_to_bool(mask)? { let place = this.deref_operand(&ptr.into())?; @@ -552,9 +552,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(ptrs_len, mask_len); for i in 0..ptrs_len { - let value = this.read_immediate(&this.mplace_index(&value, i)?.into())?; - let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?; - let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; + let value = this.read_immediate(&this.project_index(&value, i)?.into())?; + let ptr = this.read_immediate(&this.project_index(&ptrs, i)?.into())?; + let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?; if simd_element_to_bool(mask)? { let place = this.deref_operand(&ptr.into())?; @@ -578,7 +578,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut res = 0u64; for i in 0..op_len { - let op = this.read_immediate(&this.mplace_index(&op, i.into())?.into())?; + let op = this.read_immediate(&this.project_index(&op, i.into())?.into())?; if simd_element_to_bool(op)? { res |= 1u64 .checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian)) diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 4931f3688572..14297845d3db 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -593,7 +593,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(deprecated)] let home_dir = std::env::home_dir().unwrap(); let (written, _) = this.write_path_to_c_str(&home_dir, buf, buflen)?; - let pw_dir = this.mplace_field_named(&pwd, "pw_dir")?; + let pw_dir = this.project_field_named(&pwd, "pw_dir")?; this.write_pointer(buf, &pw_dir.into())?; if written { diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 0fdd55b407cd..3da6c17f3b0b 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1141,7 +1141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ("tv_sec", access_sec.into()), ("tv_nsec", access_nsec.into()), ], - &this.mplace_field_named(&statxbuf, "stx_atime")?, + &this.project_field_named(&statxbuf, "stx_atime")?, )?; #[rustfmt::skip] this.write_int_fields_named( @@ -1149,7 +1149,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ("tv_sec", created_sec.into()), ("tv_nsec", created_nsec.into()), ], - &this.mplace_field_named(&statxbuf, "stx_btime")?, + &this.project_field_named(&statxbuf, "stx_btime")?, )?; #[rustfmt::skip] this.write_int_fields_named( @@ -1157,7 +1157,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ("tv_sec", 0.into()), ("tv_nsec", 0.into()), ], - &this.mplace_field_named(&statxbuf, "stx_ctime")?, + &this.project_field_named(&statxbuf, "stx_ctime")?, )?; #[rustfmt::skip] this.write_int_fields_named( @@ -1165,7 +1165,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ("tv_sec", modified_sec.into()), ("tv_nsec", modified_nsec.into()), ], - &this.mplace_field_named(&statxbuf, "stx_mtime")?, + &this.project_field_named(&statxbuf, "stx_mtime")?, )?; Ok(0) @@ -1421,7 +1421,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // } let entry_place = this.deref_operand_as(entry_op, this.libc_ty_layout("dirent"))?; - let name_place = this.mplace_field(&entry_place, 5)?; + let name_place = this.project_field(&entry_place, 5)?; let file_name = dir_entry.file_name(); // not a Path as there are no separators! let (name_fits, file_name_buf_len) = this.write_os_str_to_c_str( diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs index 87e887000c52..9c43651132b7 100644 --- a/src/tools/miri/src/shims/unix/linux/fd.rs +++ b/src/tools/miri/src/shims/unix/linux/fd.rs @@ -73,9 +73,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if op == epoll_ctl_add || op == epoll_ctl_mod { let event = this.deref_operand_as(event, this.libc_ty_layout("epoll_event"))?; - let events = this.mplace_field(&event, 0)?; + let events = this.project_field(&event, 0)?; let events = this.read_scalar(&events.into())?.to_u32()?; - let data = this.mplace_field(&event, 1)?; + let data = this.project_field(&event, 1)?; let data = this.read_scalar(&data.into())?; let event = EpollEvent { events, data }; diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index 873b84e30599..0474c9fd90a5 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -85,7 +85,8 @@ pub fn futex<'tcx>( return Ok(()); } - let timeout = this.deref_pointer_as( + // `read_timespec` will check the place when it is not null. + let timeout = this.deref_pointer_unchecked( &this.read_immediate(&args[3])?, this.libc_ty_layout("timespec"), )?; diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 2888424c21e1..d64aa53ed958 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -122,7 +122,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to put the result into io_status_block. if let Some(n) = written { let io_status_information = - this.mplace_field_named(&io_status_block, "Information")?; + this.project_field_named(&io_status_block, "Information")?; this.write_scalar( Scalar::from_target_usize(n.into(), this), &io_status_information.into(), diff --git a/src/tools/miri/tests/pass/intrinsics.rs b/src/tools/miri/tests/pass/intrinsics.rs index 6267e6e6bc11..a6a4a06f8600 100644 --- a/src/tools/miri/tests/pass/intrinsics.rs +++ b/src/tools/miri/tests/pass/intrinsics.rs @@ -3,7 +3,7 @@ //! Tests for various intrinsics that do not fit anywhere else. use std::intrinsics; -use std::mem::{size_of, size_of_val, size_of_val_raw}; +use std::mem::{size_of, size_of_val, size_of_val_raw, discriminant}; struct Bomb; @@ -39,4 +39,7 @@ fn main() { let _v = intrinsics::discriminant_value(&0); let _v = intrinsics::discriminant_value(&true); let _v = intrinsics::discriminant_value(&vec![1, 2, 3]); + // Make sure that even if the discriminant is stored together with data, the intrinsic returns + // only the discriminant, nothing about the data. + assert_eq!(discriminant(&Some(false)), discriminant(&Some(true))); } diff --git a/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir b/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir index 3797dbabb3c1..8c8e69595950 100644 --- a/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir +++ b/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir @@ -23,17 +23,17 @@ alloc1 (static: FOO, size: 8, align: 4) { alloc19 (size: 48, align: 4) { 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc6──╼ 00 00 00 00 │ ....░░░░╾──╼.... - 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc9──╼ 02 00 00 00 │ ....░░░░╾──╼.... - 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ....*...╾──╼.... + 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc10─╼ 02 00 00 00 │ ....░░░░╾──╼.... + 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc15─╼ 03 00 00 00 │ ....*...╾──╼.... } alloc6 (size: 0, align: 4) {} -alloc9 (size: 16, align: 4) { - ╾─alloc10─╼ 03 00 00 00 ╾─alloc11─╼ 03 00 00 00 │ ╾──╼....╾──╼.... +alloc10 (size: 16, align: 4) { + ╾─alloc9──╼ 03 00 00 00 ╾─alloc11─╼ 03 00 00 00 │ ╾──╼....╾──╼.... } -alloc10 (size: 3, align: 1) { +alloc9 (size: 3, align: 1) { 66 6f 6f │ foo } @@ -41,12 +41,12 @@ alloc11 (size: 3, align: 1) { 62 61 72 │ bar } -alloc14 (size: 24, align: 4) { - 0x00 │ ╾─alloc15─╼ 03 00 00 00 ╾─alloc16─╼ 03 00 00 00 │ ╾──╼....╾──╼.... +alloc15 (size: 24, align: 4) { + 0x00 │ ╾─alloc14─╼ 03 00 00 00 ╾─alloc16─╼ 03 00 00 00 │ ╾──╼....╾──╼.... 0x10 │ ╾─alloc17─╼ 04 00 00 00 │ ╾──╼.... } -alloc15 (size: 3, align: 1) { +alloc14 (size: 3, align: 1) { 6d 65 68 │ meh } diff --git a/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir b/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir index dc16c064009d..e22547032967 100644 --- a/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir +++ b/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir @@ -24,19 +24,19 @@ alloc1 (static: FOO, size: 16, align: 8) { alloc19 (size: 72, align: 8) { 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc6────────╼ │ ....░░░░╾──────╼ 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ - 0x20 │ ╾───────alloc9────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc14───────╼ │ ....*...╾──────╼ + 0x20 │ ╾───────alloc10───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc15───────╼ │ ....*...╾──────╼ 0x40 │ 03 00 00 00 00 00 00 00 │ ........ } alloc6 (size: 0, align: 8) {} -alloc9 (size: 32, align: 8) { - 0x00 │ ╾───────alloc10───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +alloc10 (size: 32, align: 8) { + 0x00 │ ╾───────alloc9────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ 0x10 │ ╾───────alloc11───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ } -alloc10 (size: 3, align: 1) { +alloc9 (size: 3, align: 1) { 66 6f 6f │ foo } @@ -44,13 +44,13 @@ alloc11 (size: 3, align: 1) { 62 61 72 │ bar } -alloc14 (size: 48, align: 8) { - 0x00 │ ╾───────alloc15───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +alloc15 (size: 48, align: 8) { + 0x00 │ ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ 0x10 │ ╾───────alloc16───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ 0x20 │ ╾───────alloc17───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } -alloc15 (size: 3, align: 1) { +alloc14 (size: 3, align: 1) { 6d 65 68 │ meh } diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr index 20c905878e07..08b98b37bd81 100644 --- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr @@ -24,7 +24,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:42:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of the never type `!` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { @@ -35,7 +35,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:44:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of uninhabited type `Never` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { diff --git a/tests/ui/consts/const-eval/ub-enum.64bit.stderr b/tests/ui/consts/const-eval/ub-enum.64bit.stderr index fb40babb0b9c..c28a1b722aef 100644 --- a/tests/ui/consts/const-eval/ub-enum.64bit.stderr +++ b/tests/ui/consts/const-eval/ub-enum.64bit.stderr @@ -75,7 +75,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:81:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of the never type `!` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { @@ -86,7 +86,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:83:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of uninhabited type `Never` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { @@ -108,14 +108,27 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:96:77 | LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered a value of uninhabited type `Never` + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:98:77 | LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered a value of the never type `!` + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant -error: aborting due to 13 previous errors +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | + = note: read discriminant of an uninhabited enum variant + | +note: inside `discriminant::` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: inside `TEST_ICE_89765` + --> $DIR/ub-enum.rs:103:14 + | +LL | unsafe { std::mem::discriminant(&*(&() as *const () as *const Never)); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 8f26d9a00d71..078283fbd1f7 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -2,7 +2,7 @@ // Strip out raw byte dumps to make comparison platform-independent: // normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" // normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*a(lloc)?[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" -#![feature(never_type)] +#![feature(never_type, const_discriminant)] #![allow(invalid_value)] use std::mem; @@ -66,8 +66,8 @@ const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; // # valid discriminant for uninhabited variant -// An enum with 3 variants of which some are uninhabited -- so the uninhabited variants *do* -// have a discriminant. +// An enum with uninhabited variants but also at least 2 inhabited variants -- so the uninhabited +// variants *do* have a discriminant. enum UninhDiscriminant { A, B(!), @@ -98,5 +98,11 @@ const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem: const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; //~^ ERROR evaluation of constant value failed +const TEST_ICE_89765: () = { + // This is a regression test for https://github.com/rust-lang/rust/issues/89765. + unsafe { std::mem::discriminant(&*(&() as *const () as *const Never)); }; + //~^ inside `TEST_ICE_89765` +}; + fn main() { } diff --git a/tests/ui/consts/const_discriminant.rs b/tests/ui/consts/const_discriminant.rs index 79e68590e85d..b1180faa6977 100644 --- a/tests/ui/consts/const_discriminant.rs +++ b/tests/ui/consts/const_discriminant.rs @@ -24,13 +24,6 @@ enum SingleVariant { const TEST_V: Discriminant = discriminant(&SingleVariant::V); -pub const TEST_VOID: () = { - // This is UB, but CTFE does not check validity so it does not detect this. - // This is a regression test for https://github.com/rust-lang/rust/issues/89765. - unsafe { std::mem::discriminant(&*(&() as *const () as *const Void)); }; -}; - - fn main() { assert_eq!(TEST_A, TEST_A_OTHER); assert_eq!(TEST_A, discriminant(black_box(&Test::A(17)))); diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs index 6a3c93ce7a67..5bff34bbe933 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -1,8 +1,26 @@ // revisions: no_flag with_flag // [no_flag] check-pass // [with_flag] compile-flags: -Zextra-const-ub-checks +#![feature(never_type)] use std::mem::transmute; +use std::ptr::addr_of; + +#[derive(Clone, Copy)] +enum E { A, B } + +#[derive(Clone, Copy)] +enum Never {} + +// An enum with uninhabited variants but also at least 2 inhabited variants -- so the uninhabited +// variants *do* have a discriminant. +#[derive(Clone, Copy)] +enum UninhDiscriminant { + A, + B(!), + C, + D(Never), +} const INVALID_BOOL: () = unsafe { let _x: bool = transmute(3u8); @@ -27,4 +45,15 @@ const UNALIGNED_PTR: () = unsafe { //[with_flag]~| invalid value }; +const UNINHABITED_VARIANT: () = unsafe { + let data = [1u8]; + // Not using transmute, we want to hit the ImmTy code path. + let v = *addr_of!(data).cast::(); + //[with_flag]~^ ERROR: evaluation of constant value failed +}; + +// Regression tests for an ICE (related to ). +const VALID_ENUM1: E = { let e = E::A; e }; +const VALID_ENUM2: Result<&'static [u8], ()> = { let e = Err(()); e }; + fn main() {} diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr index 3970baefcb35..19f1748ff9c9 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:8:20 + --> $DIR/detect-extra-ub.rs:26:20 | LL | let _x: bool = transmute(3u8); | ^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:14:21 + --> $DIR/detect-extra-ub.rs:32:21 | LL | let _x: usize = transmute(&3u8); | ^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes @@ -14,7 +14,7 @@ LL | let _x: usize = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:20:30 + --> $DIR/detect-extra-ub.rs:38:30 | LL | let _x: (usize, usize) = transmute(x); | ^^^^^^^^^^^^ unable to turn pointer into raw bytes @@ -23,11 +23,17 @@ LL | let _x: (usize, usize) = transmute(x); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:25:20 + --> $DIR/detect-extra-ub.rs:43:20 | LL | let _x: &u32 = transmute(&[0u8; 4]); | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 4 byte alignment but found 1) -error: aborting due to 4 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/detect-extra-ub.rs:51:13 + | +LL | let v = *addr_of!(data).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0080`. From 14a674c88cde2465905d1657919242c1a3bd9d10 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Jul 2023 17:45:27 +0200 Subject: [PATCH 090/105] interpret: read_discriminant: only return VariantIdx --- .../rustc_const_eval/src/const_eval/mod.rs | 2 +- .../src/const_eval/valtrees.rs | 2 +- .../src/interpret/discriminant.rs | 66 +++++++++++-------- .../src/interpret/intrinsics.rs | 5 +- .../rustc_const_eval/src/interpret/step.rs | 5 +- .../src/interpret/validity.rs | 2 +- .../rustc_const_eval/src/interpret/visitor.rs | 2 +- 7 files changed, 49 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 655c89cac222..ef31155215a1 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -101,7 +101,7 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>( return None; } ty::Adt(def, _) => { - let variant = ecx.read_discriminant(&op).ok()?.1; + let variant = ecx.read_discriminant(&op).ok()?; let down = ecx.project_downcast(&op, variant).ok()?; (def.variants()[variant].fields.len(), Some(variant), down) } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 5f65cd6ec253..b519bcdf4a33 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -130,7 +130,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( bug!("uninhabited types should have errored and never gotten converted to valtree") } - let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else { + let Ok(variant) = ecx.read_discriminant(&place.into()) else { return Err(ValTreeCreationError::Other); }; branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 9ea5e7cb1f97..aff86d5f4866 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -1,6 +1,6 @@ //! Functions for reading and writing discriminants of multi-variant layouts (enums and generators). -use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; +use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::{mir, ty}; use rustc_target::abi::{self, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; @@ -93,7 +93,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn read_discriminant( &self, op: &OpTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { + ) -> InterpResult<'tcx, VariantIdx> { trace!("read_discriminant_value {:#?}", op.layout); // Get type and layout of the discriminant. let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; @@ -106,30 +106,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { Variants::Single { index } => { - // Hilariously, `Single` is used even for 0-variant enums. - // (See https://github.com/rust-lang/rust/issues/89765). - if matches!(op.layout.ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { - throw_ub!(UninhabitedEnumVariantRead(index)) - } - let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { - Some(discr) => { - // This type actually has discriminants. - assert_eq!(discr.ty, discr_layout.ty); - Scalar::from_uint(discr.val, discr_layout.size) + // Do some extra checks on enums. + if op.layout.ty.is_enum() { + // Hilariously, `Single` is used even for 0-variant enums. + // (See https://github.com/rust-lang/rust/issues/89765). + if matches!(op.layout.ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) + { + throw_ub!(UninhabitedEnumVariantRead(index)) } - None => { - // On a type without actual discriminants, variant is 0. - assert_eq!(index.as_u32(), 0); - Scalar::from_uint(index.as_u32(), discr_layout.size) + // For consisteny with `write_discriminant`, and to make sure that + // `project_downcast` cannot fail due to strange layouts, we declare immediate UB + // for uninhabited variants. + if op.layout.for_variant(self, index).abi.is_uninhabited() { + throw_ub!(UninhabitedEnumVariantRead(index)) } - }; - // For consisteny with `write_discriminant`, and to make sure that - // `project_downcast` cannot fail due to strange layouts, we declare immediate UB - // for uninhabited variants. - if op.layout.ty.is_enum() && op.layout.for_variant(self, index).abi.is_uninhabited() { - throw_ub!(UninhabitedEnumVariantRead(index)) } - return Ok((discr, index)); + return Ok(index); } Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { (tag, tag_encoding, tag_field) @@ -155,7 +147,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("tag value: {}", tag_val); // Figure out which discriminant and variant this corresponds to. - let (discr, index) = match *tag_encoding { + let index = match *tag_encoding { TagEncoding::Direct => { let scalar = tag_val.to_scalar(); // Generate a specific error if `tag_val` is not an integer. @@ -183,7 +175,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?; // Return the cast value, and the index. - (discr_val, index.0) + index.0 } TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { let tag_val = tag_val.to_scalar(); @@ -241,13 +233,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Compute the size of the scalar we need to return. // No need to cast, because the variant index directly serves as discriminant and is // encoded in the tag. - (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) + variant } }; // For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants. if op.layout.for_variant(self, index).abi.is_uninhabited() { throw_ub!(UninhabitedEnumVariantRead(index)) } - Ok((discr, index)) + Ok(index) + } + + pub fn discriminant_for_variant( + &self, + layout: TyAndLayout<'tcx>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Scalar> { + let discr_layout = self.layout_of(layout.ty.discriminant_ty(*self.tcx))?; + Ok(match layout.ty.discriminant_for_variant(*self.tcx, variant) { + Some(discr) => { + // This type actually has discriminants. + assert_eq!(discr.ty, discr_layout.ty); + Scalar::from_uint(discr.val, discr_layout.size) + } + None => { + // On a type without actual discriminants, variant is 0. + assert_eq!(variant.as_u32(), 0); + Scalar::from_uint(variant.as_u32(), discr_layout.size) + } + }) } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 5f981c9b9188..3f6971682805 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -226,8 +226,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::discriminant_value => { let place = self.deref_operand(&args[0])?; - let discr_val = self.read_discriminant(&place.into())?.0; - self.write_scalar(discr_val, dest)?; + let variant = self.read_discriminant(&place.into())?; + let discr = self.discriminant_for_variant(place.layout, variant)?; + self.write_scalar(discr, dest)?; } sym::exact_div => { let l = self.read_immediate(&args[0])?; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 319c422134c5..9182d23128fe 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -302,8 +302,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Discriminant(place) => { let op = self.eval_place_to_op(place, None)?; - let discr_val = self.read_discriminant(&op)?.0; - self.write_scalar(discr_val, &dest)?; + let variant = self.read_discriminant(&op)?; + let discr = self.discriminant_for_variant(op.layout, variant)?; + self.write_scalar(discr, &dest)?; } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 6618c70ac752..a82c98e72057 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -657,7 +657,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ) -> InterpResult<'tcx, VariantIdx> { self.with_elem(PathElem::EnumTag, move |this| { Ok(try_validation!( - this.ecx.read_discriminant(op).map(|(_, idx)| idx), + this.ecx.read_discriminant(op), this.path, InvalidTag(val) => InvalidEnumTag { value: format!("{val:x}"), diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index a50233fa3dea..4ec19d9e655b 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -23,7 +23,7 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { /// `read_discriminant` can be hooked for better error messages. #[inline(always)] fn read_discriminant(&mut self, v: &Self::V) -> InterpResult<'tcx, VariantIdx> { - Ok(self.ecx().read_discriminant(&v.to_op(self.ecx())?)?.1) + Ok(self.ecx().read_discriminant(&v.to_op(self.ecx())?)?) } /// This function provides the chance to reorder the order in which fields are visited for From 4ea2bd1c8f6fc5aba40af7b0421f2eea201ba147 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Jul 2023 19:42:21 +0200 Subject: [PATCH 091/105] bless more --- src/tools/clippy/tests/ui/self_assignment.rs | 2 +- .../clippy/tests/ui/self_assignment.stderr | 6 ++--- .../consts/const-eval/raw-bytes.32bit.stderr | 4 ++-- .../ui/consts/const-eval/ub-enum.32bit.stderr | 23 +++++++++++++++---- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/tools/clippy/tests/ui/self_assignment.rs b/src/tools/clippy/tests/ui/self_assignment.rs index d6682cc63dcf..a7f9fbaae7cf 100644 --- a/src/tools/clippy/tests/ui/self_assignment.rs +++ b/src/tools/clippy/tests/ui/self_assignment.rs @@ -14,7 +14,7 @@ pub fn positives(mut a: usize, b: &mut u32, mut s: S) { *b = *b; s = s; s.a = s.a; - s.b[10] = s.b[5 + 5]; + s.b[9] = s.b[5 + 4]; s.c[0][1] = s.c[0][1]; s.b[a] = s.b[a]; *s.e = *s.e; diff --git a/src/tools/clippy/tests/ui/self_assignment.stderr b/src/tools/clippy/tests/ui/self_assignment.stderr index bed88244eea7..25b8569fa3d0 100644 --- a/src/tools/clippy/tests/ui/self_assignment.stderr +++ b/src/tools/clippy/tests/ui/self_assignment.stderr @@ -24,11 +24,11 @@ error: self-assignment of `s.a` to `s.a` LL | s.a = s.a; | ^^^^^^^^^ -error: self-assignment of `s.b[5 + 5]` to `s.b[10]` +error: self-assignment of `s.b[5 + 4]` to `s.b[9]` --> $DIR/self_assignment.rs:17:5 | -LL | s.b[10] = s.b[5 + 5]; - | ^^^^^^^^^^^^^^^^^^^^ +LL | s.b[9] = s.b[5 + 4]; + | ^^^^^^^^^^^^^^^^^^^ error: self-assignment of `s.c[0][1]` to `s.c[0][1]` --> $DIR/self_assignment.rs:18:5 diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr index c0ddaceea4ce..8a5424b3a6c6 100644 --- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr @@ -24,7 +24,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:42:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of the never type `!` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { @@ -35,7 +35,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:44:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of uninhabited type `Never` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { diff --git a/tests/ui/consts/const-eval/ub-enum.32bit.stderr b/tests/ui/consts/const-eval/ub-enum.32bit.stderr index 1810600b7851..5ef0d0146f2f 100644 --- a/tests/ui/consts/const-eval/ub-enum.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-enum.32bit.stderr @@ -75,7 +75,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:81:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of the never type `!` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { @@ -86,7 +86,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:83:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a value of uninhabited type `Never` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { @@ -108,14 +108,27 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:96:77 | LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered a value of uninhabited type `Never` + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:98:77 | LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered a value of the never type `!` + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant -error: aborting due to 13 previous errors +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | + = note: read discriminant of an uninhabited enum variant + | +note: inside `discriminant::` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: inside `TEST_ICE_89765` + --> $DIR/ub-enum.rs:103:14 + | +LL | unsafe { std::mem::discriminant(&*(&() as *const () as *const Never)); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0080`. From d1276005119341f381ce1ec04894b24a9275d5f0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 25 Jul 2023 09:47:19 +0200 Subject: [PATCH 092/105] add some sanity checks in write_immediate_no_validate --- .../rustc_const_eval/src/interpret/place.rs | 31 +++++++++++++++++-- .../src/mir/interpret/allocation.rs | 2 +- .../rustc_middle/src/mir/interpret/value.rs | 8 +++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 9c492fb8b936..db1239c7136a 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -577,7 +577,7 @@ where src: Immediate, dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - assert!(dest.layout.is_sized(), "Cannot write unsized data"); + assert!(dest.layout.is_sized(), "Cannot write unsized immediate data"); trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, @@ -591,9 +591,34 @@ where *self.force_allocation(dest)? } else { match M::access_local_mut(self, frame, local)? { - Operand::Immediate(local) => { + Operand::Immediate(local_val) => { // Local can be updated in-place. - *local = src; + *local_val = src; + // Double-check that the value we are storing and the local fit to each other. + // (*After* doing the update for borrow checker reasons.) + if cfg!(debug_assertions) { + let local_layout = + self.layout_of_local(&self.stack()[frame], local, None)?; + match (src, local_layout.abi) { + (Immediate::Scalar(scalar), Abi::Scalar(s)) => { + assert_eq!(scalar.size(), s.size(self)) + } + ( + Immediate::ScalarPair(a_val, b_val), + Abi::ScalarPair(a, b), + ) => { + assert_eq!(a_val.size(), a.size(self)); + assert_eq!(b_val.size(), b.size(self)); + } + (Immediate::Uninit, _) => {} + (src, abi) => { + bug!( + "value {src:?} cannot be written into local with type {} (ABI {abi:?})", + local_layout.ty + ) + } + }; + } return Ok(()); } Operand::Indirect(mplace) => { diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index b8030d9db13c..c1cb2f2e497f 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -571,7 +571,7 @@ impl Allocation assert!(self.mutability == Mutability::Mut); // `to_bits_or_ptr_internal` is the right method because we just want to store this data - // as-is into memory. + // as-is into memory. This also double-checks that `val.size()` matches `range.size`. let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? { Right(ptr) => { let (provenance, offset) = ptr.into_parts(); diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 0416411dfe14..47421d0f0371 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -320,6 +320,14 @@ impl Scalar { } }) } + + #[inline] + pub fn size(self) -> Size { + match self { + Scalar::Int(int) => int.size(), + Scalar::Ptr(_ptr, sz) => Size::from_bytes(sz), + } + } } impl<'tcx, Prov: Provenance> Scalar { From c83dfe9aedf79942e3416749b00803e0b5dcb5f2 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Tue, 25 Jul 2023 11:23:11 +0000 Subject: [PATCH 093/105] Suggest `{Option,Result}::as_ref()` instead of `cloned()` in some cases --- compiler/rustc_hir_typeck/src/demand.rs | 2 +- .../src/fn_ctxt/suggestions.rs | 19 +++++++++++++++---- .../dont-suggest-cyclic-constraint.fixed | 13 +++++++++++++ .../dont-suggest-cyclic-constraint.rs | 4 +++- .../dont-suggest-cyclic-constraint.stderr | 6 +++++- tests/ui/suggestions/copied-and-cloned.fixed | 8 ++++++++ tests/ui/suggestions/copied-and-cloned.rs | 8 ++++++++ tests/ui/suggestions/copied-and-cloned.stderr | 15 ++++++++++++++- 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 tests/ui/associated-types/dont-suggest-cyclic-constraint.fixed diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 45085534f72f..26fa3d80d553 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_no_capture_closure(err, expected, expr_ty) || self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) - || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) + || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected, expected_ty_expr) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ec19d017c259..acea5f8c1f63 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1085,12 +1085,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - pub(crate) fn suggest_copied_or_cloned( + pub(crate) fn suggest_copied_cloned_or_as_ref( &self, diag: &mut Diagnostic, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let ty::Adt(adt_def, args) = expr_ty.kind() else { return false; @@ -1102,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - let mut suggest_copied_or_cloned = || { + let mut suggest_copied_cloned_or_as_ref = || { let expr_inner_ty = args.type_at(0); let expected_inner_ty = expected_args.type_at(0); if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() @@ -1119,6 +1120,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); return true; + } else if let Some(expected_ty_expr) = expected_ty_expr { + diag.span_suggestion_verbose( + expected_ty_expr.span.shrink_to_hi(), + format!( + "use `{def_path}::as_ref()` to convert `{expected_ty}` to `{expr_ty}`" + ), + ".as_ref()", + Applicability::MachineApplicable, + ); + return true; } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( self, @@ -1146,11 +1157,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that the error types are equal && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1)) { - return suggest_copied_or_cloned(); + return suggest_copied_cloned_or_as_ref(); } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) && adt_def.did() == option_did { - return suggest_copied_or_cloned(); + return suggest_copied_cloned_or_as_ref(); } false diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.fixed b/tests/ui/associated-types/dont-suggest-cyclic-constraint.fixed new file mode 100644 index 000000000000..ec4165cc71e2 --- /dev/null +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +use std::fmt::Debug; + +pub fn foo(mut iter: I, value: &I::Item) +where + I::Item: Eq + Debug, +{ + debug_assert_eq!(iter.next().as_ref(), Some(value)); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs index 6894f6b6cc4a..0b4df08783d6 100644 --- a/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs @@ -1,6 +1,8 @@ +// run-rustfix + use std::fmt::Debug; -fn foo(mut iter: I, value: &I::Item) +pub fn foo(mut iter: I, value: &I::Item) where I::Item: Eq + Debug, { diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr index 3ecac9c83e57..c06c506a311a 100644 --- a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr @@ -1,11 +1,15 @@ error[E0308]: mismatched types - --> $DIR/dont-suggest-cyclic-constraint.rs:7:35 + --> $DIR/dont-suggest-cyclic-constraint.rs:9:35 | LL | debug_assert_eq!(iter.next(), Some(value)); | ^^^^^^^^^^^ expected `Option<::Item>`, found `Option<&::Item>` | = note: expected enum `Option<::Item>` found enum `Option<&::Item>` +help: use `Option::as_ref()` to convert `Option<::Item>` to `Option<&::Item>` + | +LL | debug_assert_eq!(iter.next().as_ref(), Some(value)); + | +++++++++ error: aborting due to previous error diff --git a/tests/ui/suggestions/copied-and-cloned.fixed b/tests/ui/suggestions/copied-and-cloned.fixed index f801403feec4..13031f424cbe 100644 --- a/tests/ui/suggestions/copied-and-cloned.fixed +++ b/tests/ui/suggestions/copied-and-cloned.fixed @@ -20,4 +20,12 @@ fn main() { expect::>(x.cloned()); //~^ ERROR mismatched types //~| HELP use `Result::cloned` to clone the value inside the `Result` + + let s = String::new(); + let x = Some(s.clone()); + let y = Some(&s); + println!("{}", x.as_ref() == y); + //~^ ERROR mismatched types + //~| HELP use `Option::as_ref()` to convert `Option` to `Option<&String>` + } diff --git a/tests/ui/suggestions/copied-and-cloned.rs b/tests/ui/suggestions/copied-and-cloned.rs index 640450b76552..2927d66dea94 100644 --- a/tests/ui/suggestions/copied-and-cloned.rs +++ b/tests/ui/suggestions/copied-and-cloned.rs @@ -20,4 +20,12 @@ fn main() { expect::>(x); //~^ ERROR mismatched types //~| HELP use `Result::cloned` to clone the value inside the `Result` + + let s = String::new(); + let x = Some(s.clone()); + let y = Some(&s); + println!("{}", x == y); + //~^ ERROR mismatched types + //~| HELP use `Option::as_ref()` to convert `Option` to `Option<&String>` + } diff --git a/tests/ui/suggestions/copied-and-cloned.stderr b/tests/ui/suggestions/copied-and-cloned.stderr index 067808141824..19aaf6e00b13 100644 --- a/tests/ui/suggestions/copied-and-cloned.stderr +++ b/tests/ui/suggestions/copied-and-cloned.stderr @@ -78,6 +78,19 @@ help: use `Result::cloned` to clone the value inside the `Result` LL | expect::>(x.cloned()); | +++++++++ -error: aborting due to 4 previous errors +error[E0308]: mismatched types + --> $DIR/copied-and-cloned.rs:27:25 + | +LL | println!("{}", x == y); + | ^ expected `Option`, found `Option<&String>` + | + = note: expected enum `Option` + found enum `Option<&String>` +help: use `Option::as_ref()` to convert `Option` to `Option<&String>` + | +LL | println!("{}", x.as_ref() == y); + | +++++++++ + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0308`. From df4bfd9e9759a9f9d5b1407db23d5881a13612ca Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 25 Jul 2023 13:40:04 +0000 Subject: [PATCH 094/105] Try explaining where `Inner` is in the signature better --- compiler/rustc_hir_analysis/src/check/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 96eb83f37682..58dd0c1b8aba 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -545,7 +545,7 @@ fn sanity_check_found_hidden_type<'tcx>( /// } /// fn func<'a>(x: &'a ()) -> impl Id { x } /// // desugared to -/// fn func<'a>(x: &'a () -> Outer<'a, Assoc = Inner<'a>> { +/// fn func<'a>(x: &'a () -> Outer<'a> where as Id>::Assoc = Inner<'a> { /// // Note that in contrast to other nested items, RPIT type aliases can /// // access their parents' generics. /// From 037b27430bc338ca5722aeb87a4614e70a5b3df4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 25 Jul 2023 15:24:58 +0100 Subject: [PATCH 095/105] abi: unsized field in union - assert to delay bug Unions cannot have unsized fields, and as such, layout computation for unions asserts that each union field is sized (as this would normally have halted compilation earlier). However, if a generator ends up with an unsized local - a circumstance in which an error will always have been emitted earlier, for example, if attempting to dereference a `&str` - then the generator transform will produce a union with an unsized field. Since #110107, later passes will be run, such as constant propagation, and can attempt layout computation on the generator, which will result in layout computation of `str` in the context of it being a field of a union - and so the aforementioned assertion would cause an ICE. It didn't seem appropriate to try and detect this case in the MIR body and skip this specific pass; tainting the MIR body or delaying a bug from the generator transform (or elsewhere) wouldn't prevent this either (as neither would prevent the later pass from running); and tainting when the deref of `&str` is reported, if that's possible, would unnecessarily prevent potential other errors from being reported later in compilation, and is very tailored to this specific case of getting a unsized type in a generator. Given that this circumstance can only happen when an error should have already been reported, the correct fix appears to be just changing the assert to a delayed bug. This will still assert if there is some circumstance where this occurs and no error has been reported, but it won't crash the compiler in this instance. Signed-off-by: David Wood --- compiler/rustc_abi/src/layout.rs | 4 +++- compiler/rustc_abi/src/lib.rs | 1 - tests/ui/generator/issue-113279.rs | 27 ++++++++++++++++++++++++++ tests/ui/generator/issue-113279.stderr | 16 +++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/ui/generator/issue-113279.rs create mode 100644 tests/ui/generator/issue-113279.stderr diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index aea88641f82b..489a8403c3b1 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -763,7 +763,9 @@ pub trait LayoutCalculator { let mut size = Size::ZERO; let only_variant = &variants[FIRST_VARIANT]; for field in only_variant { - assert!(field.0.is_sized()); + if field.0.is_unsized() { + self.delay_bug("unsized field in union".to_string()); + } align = align.max(field.align()); max_repr_align = max_repr_align.max(field.max_repr_align()); diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index ef0c763ac203..93b29d037d67 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1345,7 +1345,6 @@ impl Abi { /// Discard validity range information and allow undef. pub fn to_union(&self) -> Self { - assert!(self.is_sized()); match *self { Abi::Scalar(s) => Abi::Scalar(s.to_union()), Abi::ScalarPair(s1, s2) => Abi::ScalarPair(s1.to_union(), s2.to_union()), diff --git a/tests/ui/generator/issue-113279.rs b/tests/ui/generator/issue-113279.rs new file mode 100644 index 000000000000..f69f804b716d --- /dev/null +++ b/tests/ui/generator/issue-113279.rs @@ -0,0 +1,27 @@ +#![feature(generators)] + +// `foo` attempts to dereference `""`, which results in an error being reported. Later, the +// generator transform for `foo` then produces a union which contains a `str` type - unions should +// not contain unsized types, but this is okay because an error has been reported already. +// When const propagation happens later in compilation, it attempts to compute the layout of the +// generator (as part of checking whether something can be const propagated) and in turn attempts +// to compute the layout of `str` in the context of a union - where this caused an ICE. This test +// makes sure that doesn't happen again. + +fn foo() { + let _y = static || { + let x = &mut 0; + *{ + yield; + x + } += match { *"" }.len() { + //~^ ERROR cannot move a value of type `str` [E0161] + //~^^ ERROR cannot move out of a shared reference [E0507] + _ => 0, + }; + }; +} + +fn main() { + foo() +} diff --git a/tests/ui/generator/issue-113279.stderr b/tests/ui/generator/issue-113279.stderr new file mode 100644 index 000000000000..cc9b64ef9ac2 --- /dev/null +++ b/tests/ui/generator/issue-113279.stderr @@ -0,0 +1,16 @@ +error[E0161]: cannot move a value of type `str` + --> $DIR/issue-113279.rs:17:20 + | +LL | } += match { *"" }.len() { + | ^^^^^^^ the size of `str` cannot be statically determined + +error[E0507]: cannot move out of a shared reference + --> $DIR/issue-113279.rs:17:22 + | +LL | } += match { *"" }.len() { + | ^^^ move occurs because value has type `str`, which does not implement the `Copy` trait + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0161, E0507. +For more information about an error, try `rustc --explain E0161`. From 7e66c0b7edaf680f26c6170d81ce18869345046e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 6 Jul 2023 02:53:21 +0000 Subject: [PATCH 096/105] Normalize the RHS of an unsize goal --- .../rustc_middle/src/traits/solve/inspect.rs | 6 +- .../src/traits/solve/inspect/format.rs | 3 + .../src/solve/eval_ctxt/select.rs | 10 +- .../src/solve/trait_goals.rs | 198 ++++++++++++------ .../traits/new-solver/normalize-unsize-rhs.rs | 21 ++ 5 files changed, 169 insertions(+), 69 deletions(-) create mode 100644 tests/ui/traits/new-solver/normalize-unsize-rhs.rs diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 8698cf860226..e793f481995f 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> { pub enum CandidateKind<'tcx> { /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, + DynUpcastingAssembly, /// A normal candidate for proving a goal - Candidate { name: String, result: QueryResult<'tcx> }, + Candidate { + name: String, + result: QueryResult<'tcx>, + }, } impl Debug for GoalCandidate<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index d0d2080b6a16..39f84279259c 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { CandidateKind::NormalizedSelfTyAssembly => { writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } + CandidateKind::DynUpcastingAssembly => { + writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:") + } CandidateKind::Candidate { name, result } => { writeln!(self.f, "CANDIDATE {}: {:?}", name, result) } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index a6d118d8cc29..27d877b84ddf 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -330,9 +330,13 @@ fn rematch_unsize<'tcx>( mut nested: Vec>, ) -> SelectionResult<'tcx, Selection<'tcx>> { let tcx = infcx.tcx; - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - + let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); + let b_ty = structurally_normalize( + goal.predicate.trait_ref.args.type_at(1), + infcx, + goal.param_env, + &mut nested, + ); match (a_ty.kind(), b_ty.kind()) { (_, &ty::Dynamic(data, region, ty::Dyn)) => { // Check that the type implements all of the predicates of the def-id. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 930e62d63883..69b30f0a1a88 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,12 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use super::assembly::{self, structural_traits}; +use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::solve::inspect::CandidateKind; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; use rustc_middle::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -376,11 +378,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); let a_ty = goal.predicate.self_ty(); let b_ty = goal.predicate.trait_ref.args.type_at(1); - if b_ty.is_ty_var() { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - } + ecx.probe_candidate("builtin unsize").enter(|ecx| { + let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else { + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( + MaybeCause::Overflow, + )); + }; + match (a_ty.kind(), b_ty.kind()) { + (_, ty::Infer(ty::TyVar(_))) => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { // Dyn upcasting is handled separately, since due to upcasting, @@ -489,74 +498,90 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { - return vec![]; - }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { - return vec![]; - }; + // Need to wrap in a probe since `normalize_non_self_ty` has side-effects. + ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + let b_ty = goal.predicate.trait_ref.args.type_at(1); + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + return vec![]; + }; - // All of a's auto traits need to be in b's auto traits. - let auto_traits_compatible = - b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); - if !auto_traits_compatible { - return vec![]; - } + // We don't care about `ty::Infer` here or errors here, since we'll + // register an ambiguous/error response in the other unsize candidate + // assembly function. + let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else { + return vec![]; + }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + return vec![]; + }; - let mut unsize_dyn_to_principal = |principal: Option>| { - ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal( - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - }; - - let mut responses = vec![]; - // If the principal def ids match (or are both none), then we're not doing - // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); + // All of a's auto traits need to be in b's auto traits. + let auto_traits_compatible = + b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); + if !auto_traits_compatible { + return vec![]; } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + + let mut unsize_dyn_to_principal = |principal: Option< + ty::PolyExistentialTraitRef<'tcx>, + >| { + ecx.probe_candidate("upcast dyn to principle").enter( + |ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(goal.param_env, new_a_ty, b_ty)?; + ecx.add_goal(goal.with( + tcx, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }, + ) + }; + + let mut responses = vec![]; + // If the principal def ids match (or are both none), then we're not doing + // trait upcasting. We're just removing auto traits (or shortening the lifetime). + if a_data.principal_def_id() == b_data.principal_def_id() { + if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { responses.push(response); } + } else if let Some(a_principal) = a_data.principal() + && let Some(b_principal) = b_data.principal() + { + for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { + if super_trait_ref.def_id() != b_principal.def_id() { + continue; + } + let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }); + if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + responses.push(response); + } + } } - } - responses + responses + }) } fn consider_builtin_discriminant_kind_candidate( @@ -750,4 +775,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) } + + /// Normalize a non-self type when it is structually matched on when solving + /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty` + /// for the self type, but for other goals, additional normalization of other + /// arguments may be needed to completely implement the semantics of the trait. + /// + /// This is required when structurally matching on any trait argument that is + /// not the self type. + fn normalize_non_self_ty( + &mut self, + mut ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result>, NoSolution> { + if !matches!(ty.kind(), ty::Alias(..)) { + return Ok(Some(ty)); + } + + self.repeat_while_none( + |_| Ok(None), + |ecx| { + let ty::Alias(_, projection_ty) = *ty.kind() else { + return Some(Ok(Some(ty))); + }; + + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = Goal::new( + ecx.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + ecx.add_goal(normalizes_to_goal); + if let Err(err) = ecx.try_evaluate_added_goals() { + return Some(Err(err)); + } + + ty = ecx.resolve_vars_if_possible(normalized_ty); + None + }, + ) + } } diff --git a/tests/ui/traits/new-solver/normalize-unsize-rhs.rs b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs new file mode 100644 index 000000000000..1bb5c9b4111a --- /dev/null +++ b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs @@ -0,0 +1,21 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait A {} +trait B: A {} + +impl A for usize {} +impl B for usize {} + +trait Mirror { + type Assoc: ?Sized; +} + +impl Mirror for T { + type Assoc = T; +} + +fn main() { + let x = Box::new(1usize) as Box<::Assoc>; + let y = x as Box<::Assoc>; +} From 24eefd08e25b8ffec2df092f0add812ed58875a0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Jul 2023 22:11:03 +0000 Subject: [PATCH 097/105] Make sure to detect trait upcasting coercion even after normalization --- compiler/rustc_hir_typeck/src/coercion.rs | 37 +++++-------------- .../src/solve/eval_ctxt/select.rs | 8 +++- .../traits/new-solver/normalize-unsize-rhs.rs | 2 + .../trait-upcast-lhs-needs-normalization.rs | 2 + 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 3b1d9c8905b4..f8efe4ad585c 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -622,7 +622,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]) )]; - let mut has_unsized_tuple_coercion = false; let mut has_trait_upcasting_coercion = None; // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid @@ -636,22 +635,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) if traits.contains(&trait_pred.def_id()) => { - let trait_pred = self.resolve_vars_if_possible(trait_pred); - if unsize_did == trait_pred.def_id() { - let self_ty = trait_pred.self_ty(); - let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); - if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) = - (self_ty.kind(), unsize_ty.kind()) - && data_a.principal_def_id() != data_b.principal_def_id() - { - debug!("coerce_unsized: found trait upcasting coercion"); - has_trait_upcasting_coercion = Some((self_ty, unsize_ty)); - } - if let ty::Tuple(..) = unsize_ty.kind() { - debug!("coerce_unsized: found unsized tuple coercion"); - has_unsized_tuple_coercion = true; - } - } trait_pred } _ => { @@ -659,6 +642,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { continue; } }; + let trait_pred = self.resolve_vars_if_possible(trait_pred); match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { // Uncertain or unimplemented. Ok(None) => { @@ -701,18 +685,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // be silent, as it causes a type mismatch later. } - Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), - } - } + Ok(Some(impl_source)) => { + if matches!(impl_source, traits::ImplSource::TraitUpcasting(..)) { + has_trait_upcasting_coercion = + Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); + } - if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { - feature_err( - &self.tcx.sess.parse_sess, - sym::unsized_tuple_coercion, - self.cause.span, - "unsized tuple coercion is not stable enough for use and is subject to change", - ) - .emit(); + queue.extend(impl_source.nested_obligations()) + } + } } if let Some((sub, sup)) = has_trait_upcasting_coercion diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 27d877b84ddf..1cedbb6b7615 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -315,7 +315,13 @@ fn rematch_object<'tcx>( // If we're upcasting, get the offset of the vtable pointer, otherwise get // the base of the vtable. Ok(Some(if is_upcasting { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + // If source and target trait def ids are identical, + // then we are simply removing auto traits. + if source_trait_ref.def_id() == target_trait_ref.def_id() { + ImplSource::Builtin(nested) + } else { + ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + } } else { ImplSource::Object(ImplSourceObjectData { vtable_base, nested }) })) diff --git a/tests/ui/traits/new-solver/normalize-unsize-rhs.rs b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs index 1bb5c9b4111a..a398ab4f2f2c 100644 --- a/tests/ui/traits/new-solver/normalize-unsize-rhs.rs +++ b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs @@ -1,6 +1,8 @@ // compile-flags: -Ztrait-solver=next // check-pass +#![feature(trait_upcasting)] + trait A {} trait B: A {} diff --git a/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs b/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs index 79114b93b78d..43cd773bf3ce 100644 --- a/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs +++ b/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs @@ -1,6 +1,8 @@ // check-pass // compile-flags: -Ztrait-solver=next +#![feature(trait_upcasting)] + pub trait A {} pub trait B: A {} From c02d1a65534244532aba0f42a6719d87084865d2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Jul 2023 22:42:46 +0000 Subject: [PATCH 098/105] Restore tuple unsizing feature gate --- compiler/rustc_hir_typeck/src/coercion.rs | 24 +++++++++++++++--- compiler/rustc_middle/src/traits/mod.rs | 12 ++++++--- .../src/traits/structural_impls.rs | 2 ++ .../src/solve/eval_ctxt/select.rs | 3 +++ .../src/traits/project.rs | 6 +++-- .../src/traits/select/confirmation.rs | 25 ++++++++++++------- compiler/rustc_ty_utils/src/instance.rs | 4 ++- 7 files changed, 57 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index f8efe4ad585c..9a25669ed31d 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -622,6 +622,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]) )]; + let mut has_unsized_tuple_coercion = false; let mut has_trait_upcasting_coercion = None; // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid @@ -686,11 +687,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } Ok(Some(impl_source)) => { - if matches!(impl_source, traits::ImplSource::TraitUpcasting(..)) { - has_trait_upcasting_coercion = - Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); + match impl_source { + traits::ImplSource::TraitUpcasting(..) => { + has_trait_upcasting_coercion = + Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); + } + traits::ImplSource::TupleUnsizing(_) => { + has_unsized_tuple_coercion = true; + } + _ => {} } - queue.extend(impl_source.nested_obligations()) } } @@ -711,6 +717,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { err.emit(); } + if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { + feature_err( + &self.tcx.sess.parse_sess, + sym::unsized_tuple_coercion, + self.cause.span, + "unsized tuple coercion is not stable enough for use and is subject to change", + ) + .emit(); + } + Ok(coercion) } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index b7ffed57a0be..fb791d564a26 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -657,6 +657,9 @@ pub enum ImplSource<'tcx, N> { /// Successful resolution for a builtin trait. Builtin(Vec), + // Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y` + TupleUnsizing(Vec), + /// ImplSource for trait upcasting coercion TraitUpcasting(ImplSourceTraitUpcastingData), } @@ -665,7 +668,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn nested_obligations(self) -> Vec { match self { ImplSource::UserDefined(i) => i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, + ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, ImplSource::Object(d) => d.nested, ImplSource::TraitUpcasting(d) => d.nested, } @@ -674,7 +677,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn borrow_nested_obligations(&self) -> &[N] { match self { ImplSource::UserDefined(i) => &i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => &n, + ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => &n, ImplSource::Object(d) => &d.nested, ImplSource::TraitUpcasting(d) => &d.nested, } @@ -683,7 +686,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { match self { ImplSource::UserDefined(i) => &mut i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, + ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, ImplSource::Object(d) => &mut d.nested, ImplSource::TraitUpcasting(d) => &mut d.nested, } @@ -701,6 +704,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { }), ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct), ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()), + ImplSource::TupleUnsizing(n) => { + ImplSource::TupleUnsizing(n.into_iter().map(f).collect()) + } ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData { vtable_base: o.vtable_base, nested: o.nested.into_iter().map(f).collect(), diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index e2cd118500b6..b7bce05300d2 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -17,6 +17,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { write!(f, "ImplSourceParamData({:?}, {:?})", n, ct) } + super::ImplSource::TupleUnsizing(ref d) => write!(f, "{:?}", d), + super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 1cedbb6b7615..e2f91d7668db 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -456,6 +456,9 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), )); + + // We need to be able to detect tuple unsizing to require its feature gate. + return Ok(Some(ImplSource::TupleUnsizing(nested))); } // FIXME: We *could* ICE here if either: // 1. the certainty is `Certainty::Yes`, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index a39fc1f17718..18470d520f7d 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1925,7 +1925,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // why we special case object types. false } - | super::ImplSource::TraitUpcasting(_) => { + super::ImplSource::TraitUpcasting(_) + | super::ImplSource::TupleUnsizing(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -2005,7 +2006,8 @@ fn confirm_select_candidate<'cx, 'tcx>( } super::ImplSource::Object(_) | super::ImplSource::Param(..) - | super::ImplSource::TraitUpcasting(_) => { + | super::ImplSource::TraitUpcasting(_) + | super::ImplSource::TupleUnsizing(_) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2cb2895b4764..f423d40b9949 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -114,8 +114,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } BuiltinUnsizeCandidate => { - let data = self.confirm_builtin_unsize_candidate(obligation)?; - ImplSource::Builtin(data) + let source = + self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let target = self.infcx.shallow_resolve(target); + let data = self.confirm_builtin_unsize_candidate(obligation, source, target)?; + // If the source and target are both unsize goals, then we need to signify that + // this is tuple unsizing so that during unsized coercion we require the proper + // feature gate. + if matches!(source.kind(), ty::Tuple(..)) && matches!(target.kind(), ty::Tuple(..)) + { + ImplSource::TupleUnsizing(data) + } else { + ImplSource::Builtin(data) + } } TraitUpcastingUnsizeCandidate(idx) => { @@ -1000,15 +1012,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_builtin_unsize_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, + source: Ty<'tcx>, + target: Ty<'tcx>, ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); - - // `assemble_candidates_for_unsizing` should ensure there are no late-bound - // regions here. See the comment there for more details. - let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); - let target = self.infcx.shallow_resolve(target); - debug!(?source, ?target, "confirm_builtin_unsize_candidate"); let mut nested = vec![]; diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 16b1836ba9fa..88eb33fee379 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -290,7 +290,9 @@ fn resolve_associated_item<'tcx>( None } } - traits::ImplSource::Param(..) | traits::ImplSource::TraitUpcasting(_) => None, + traits::ImplSource::Param(..) + | traits::ImplSource::TraitUpcasting(_) + | traits::ImplSource::TupleUnsizing(_) => None, }) } From de81007d1310580e6060f2356f1938e45cafb92c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Jul 2023 18:36:34 +0000 Subject: [PATCH 099/105] Consolidate trait upcasting and unsize into one normalization --- .../src/solve/assembly/mod.rs | 25 +- .../src/solve/eval_ctxt/select.rs | 13 +- .../src/solve/project_goals.rs | 13 +- .../src/solve/trait_goals.rs | 453 ++++++++++-------- 4 files changed, 272 insertions(+), 232 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 6920e790e71a..942f40884720 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -94,6 +94,7 @@ pub(super) enum CandidateSource { #[derive(Debug, Clone, Copy)] pub(super) enum BuiltinImplSource { TraitUpcasting, + TupleUnsize, Object, Misc, Ambiguity, @@ -281,20 +282,19 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + /// Consider (possibly several) goals to upcast or unsize a type to another + /// type. + /// /// The most common forms of unsizing are array to slice, and concrete (Sized) /// type into a `dyn Trait`. ADTs and Tuples can also have their final field /// unsized if it's generic. - fn consider_builtin_unsize_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; - + /// /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or /// if `Trait2` is a (transitive) supertrait of `Trait2`. - fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + fn consider_builtin_unsize_and_upcast_candidates( + _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec>; + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, @@ -610,8 +610,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.gen_trait() == Some(trait_def_id) { G::consider_builtin_generator_candidate(self, goal) - } else if lang_items.unsize_trait() == Some(trait_def_id) { - G::consider_builtin_unsize_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { G::consider_builtin_discriminant_kind_candidate(self, goal) } else if lang_items.destruct_trait() == Some(trait_def_id) { @@ -633,11 +631,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar + Bar` and `dyn Foo: Unsize>` if lang_items.unsize_trait() == Some(trait_def_id) { - for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { - candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), - result, - }); + for (result, source) in G::consider_builtin_unsize_and_upcast_candidates(self, goal) { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); } } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index e2f91d7668db..cdfbaedb8c23 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -116,10 +116,19 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { ), ) => rematch_object(self, goal, nested_obligations), - (Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) + ( + Certainty::Maybe(_), + CandidateSource::BuiltinImpl( + BuiltinImplSource::Misc | BuiltinImplSource::TupleUnsize, + ), + ) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { + rematch_unsize(self, goal, nested_obligations) + } + + (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize)) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - rematch_unsize(self, goal, nested_obligations) + Ok(Some(ImplSource::TupleUnsizing(nested_obligations))) } // Technically some builtin impls have nested obligations, but if diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 222ed9939ba2..0f4fec50427a 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,6 +1,6 @@ use crate::traits::specialization_graph; -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, BuiltinImplSource}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -502,17 +502,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( + fn consider_builtin_unsize_and_upcast_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Unsize` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_dyn_upcast_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec> { + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { bug!("`Unsize` does not have an associated type: {:?}", goal); } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 69b30f0a1a88..7f813c58c84e 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,6 +1,6 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, BuiltinImplSource}; use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; @@ -8,7 +8,7 @@ use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -367,220 +367,55 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( + fn consider_builtin_unsize_and_upcast_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); - } - - let tcx = ecx.tcx(); - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - - ecx.probe_candidate("builtin unsize").enter(|ecx| { - let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Overflow, - )); - }; - - match (a_ty.kind(), b_ty.kind()) { - (_, ty::Infer(ty::TyVar(_))) => { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` - (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { - // Dyn upcasting is handled separately, since due to upcasting, - // when there are two supertraits that differ by args, we - // may return more than one query response. - Err(NoSolution) - } - // `T` -> `dyn Trait` unsizing - (_, &ty::Dynamic(data, region, ty::Dyn)) => { - // Can only unsize to an object-safe type - if data - .principal_def_id() - .is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) - { - return Err(NoSolution); - } - - let Some(sized_def_id) = tcx.lang_items().sized_trait() else { - return Err(NoSolution); - }; - // Check that the type implements all of the predicates of the def-id. - // (i.e. the principal, all of the associated types match, and any auto traits) - ecx.add_goals( - data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), - ); - // The type must be Sized to be unsized. - ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty]))); - // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal( - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - // `[T; n]` -> `[T]` unsizing - (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { - // We just require that the element type stays the same - ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - // Struct unsizing `Struct` -> `Struct` where `T: Unsize` - (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) - if a_def.is_struct() && a_def.did() == b_def.did() => - { - let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); - // We must be unsizing some type parameters. This also implies - // that the struct has a tail field. - if unsizing_params.is_empty() { - return Err(NoSolution); - } - - let tail_field = a_def.non_enum_variant().tail(); - let tail_field_ty = tcx.type_of(tail_field.did); - - let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); - let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); - - // Substitute just the unsizing params from B into A. The type after - // this substitution must be equal to B. This is so we don't unsize - // unrelated type parameters. - let new_a_args = - tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| { - if unsizing_params.contains(i as u32) { b_args[i] } else { a } - })); - let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args); - - // Finally, we require that `TailA: Unsize` for the tail field - // types. - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; - ecx.add_goal(goal.with( - tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` - (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) - if a_tys.len() == b_tys.len() && !a_tys.is_empty() => - { - let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); - let b_last_ty = b_tys.last().unwrap(); - - // Substitute just the tail field of B., and require that they're equal. - let unsized_a_ty = - Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; - - // Similar to ADTs, require that the rest of the fields are equal. - ecx.add_goal(goal.with( - tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - _ => Err(NoSolution), - } - }) - } - - fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec> { + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return vec![]; } - let tcx = ecx.tcx(); - - // Need to wrap in a probe since `normalize_non_self_ty` has side-effects. ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { - return vec![]; - }; - - // We don't care about `ty::Infer` here or errors here, since we'll - // register an ambiguous/error response in the other unsize candidate - // assembly function. - let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else { - return vec![]; - }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { - return vec![]; - }; - - // All of a's auto traits need to be in b's auto traits. - let auto_traits_compatible = - b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); - if !auto_traits_compatible { - return vec![]; - } - - let mut unsize_dyn_to_principal = |principal: Option< - ty::PolyExistentialTraitRef<'tcx>, - >| { - ecx.probe_candidate("upcast dyn to principle").enter( - |ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - ) - }; - - let mut responses = vec![]; - // If the principal def ids match (or are both none), then we're not doing - // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); - } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() + // We need to normalize the b_ty since it's matched structurally + // in the other functions below. + let b_ty = match ecx + .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| { - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) - }); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { - responses.push(response); - } + Ok(Some(b_ty)) if !b_ty.is_ty_var() => b_ty, + Ok(_) => { + return vec![( + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(), + BuiltinImplSource::Ambiguity, + )]; } - } + Err(_) => return vec![], + }; - responses + let mut results = vec![]; + results.extend( + ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty) + .into_iter() + .map(|resp| (resp, BuiltinImplSource::TraitUpcasting)), + ); + results.extend( + ecx.consider_builtin_unsize_candidate(goal.param_env, a_ty, b_ty).into_iter().map( + |resp| { + // If we're unsizing from tuple -> tuple, detect + let source = + if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..))) + { + BuiltinImplSource::TupleUnsize + } else { + BuiltinImplSource::Misc + }; + (resp, source) + }, + ), + ); + + results }) } @@ -647,6 +482,214 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } impl<'tcx> EvalCtxt<'_, 'tcx> { + fn consider_builtin_unsize_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + ) -> QueryResult<'tcx> { + self.probe_candidate("builtin unsize").enter(|ecx| { + let tcx = ecx.tcx(); + match (a_ty.kind(), b_ty.kind()) { + (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => { + bug!("unexpected type variable in unsize goal") + } + // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` + (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { + // Dyn upcasting is handled separately, since due to upcasting, + // when there are two supertraits that differ by args, we + // may return more than one query response. + Err(NoSolution) + } + // `T` -> `dyn Trait` unsizing + (_, &ty::Dynamic(data, region, ty::Dyn)) => { + // Can only unsize to an object-safe type + if data + .principal_def_id() + .is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) + { + return Err(NoSolution); + } + + let Some(sized_def_id) = tcx.lang_items().sized_trait() else { + return Err(NoSolution); + }; + // Check that the type implements all of the predicates of the def-id. + // (i.e. the principal, all of the associated types match, and any auto traits) + ecx.add_goals( + data.iter() + .map(|pred| Goal::new(tcx, param_env, pred.with_self_ty(tcx, a_ty))), + ); + // The type must be Sized to be unsized. + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::TraitRef::new(tcx, sized_def_id, [a_ty]), + )); + // The type must outlive the lifetime of the `dyn` we're unsizing into. + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + // `[T; n]` -> `[T]` unsizing + (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { + // We just require that the element type stays the same + ecx.eq(param_env, a_elem_ty, b_elem_ty)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + // Struct unsizing `Struct` -> `Struct` where `T: Unsize` + (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) + if a_def.is_struct() && a_def.did() == b_def.did() => + { + let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); + // We must be unsizing some type parameters. This also implies + // that the struct has a tail field. + if unsizing_params.is_empty() { + return Err(NoSolution); + } + + let tail_field = a_def.non_enum_variant().tail(); + let tail_field_ty = tcx.type_of(tail_field.did); + + let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); + let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); + + // Substitute just the unsizing params from B into A. The type after + // this substitution must be equal to B. This is so we don't unsize + // unrelated type parameters. + let new_a_args = + tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| { + if unsizing_params.contains(i as u32) { b_args[i] } else { a } + })); + let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args); + + // Finally, we require that `TailA: Unsize` for the tail field + // types. + ecx.eq(param_env, unsized_a_ty, b_ty)?; + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_tail_ty, b_tail_ty], + ), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` + (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) + if a_tys.len() == b_tys.len() && !a_tys.is_empty() => + { + let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); + let b_last_ty = b_tys.last().unwrap(); + + // Substitute just the tail field of B., and require that they're equal. + let unsized_a_ty = + Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); + ecx.eq(param_env, unsized_a_ty, b_ty)?; + + // Similar to ADTs, require that the rest of the fields are equal. + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [*a_last_ty, *b_last_ty], + ), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + _ => Err(NoSolution), + } + }) + } + + fn consider_builtin_dyn_upcast_candidates( + &mut self, + param_env: ty::ParamEnv<'tcx>, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + ) -> Vec> { + if a_ty.is_ty_var() || b_ty.is_ty_var() { + bug!("unexpected type variable in unsize goal") + } + + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + return vec![]; + }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + return vec![]; + }; + + let tcx = self.tcx(); + // All of a's auto traits need to be in b's auto traits. + let auto_traits_compatible = + b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); + if !auto_traits_compatible { + return vec![]; + } + + let mut unsize_dyn_to_principal = |principal: Option>| { + self.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(param_env, new_a_ty, b_ty)?; + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }; + + let mut responses = vec![]; + // If the principal def ids match (or are both none), then we're not doing + // trait upcasting. We're just removing auto traits (or shortening the lifetime). + if a_data.principal_def_id() == b_data.principal_def_id() { + if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { + responses.push(response); + } + } else if let Some(a_principal) = a_data.principal() + && let Some(b_principal) = b_data.principal() + { + for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { + if super_trait_ref.def_id() != b_principal.def_id() { + continue; + } + let erased_trait_ref = super_trait_ref + .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + responses.push(response); + } + } + } + + responses + } + // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering From e0c479eea2382930bb550bbb7ddb98a841f8c54b Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 24 Jul 2023 13:09:52 +0800 Subject: [PATCH 100/105] Add help for crate arg when crate name is invalid --- compiler/rustc_session/messages.ftl | 1 + compiler/rustc_session/src/errors.rs | 8 ++++++++ compiler/rustc_session/src/output.rs | 13 +++++++++++-- src/tools/tidy/src/ui_tests.rs | 4 +++- tests/ui/command/need-crate-arg-ignore-tidy.x.rs | 2 ++ .../ui/command/need-crate-arg-ignore-tidy.x.stderr | 6 ++++++ 6 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 tests/ui/command/need-crate-arg-ignore-tidy.x.rs create mode 100644 tests/ui/command/need-crate-arg-ignore-tidy.x.stderr diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index ee24c6d902fa..d4042a2e61ad 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -45,6 +45,7 @@ session_int_literal_too_large = integer literal is too large .note = value exceeds limit of `{$limit}` session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}` +session_invalid_character_in_create_name_help = you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal .label = invalid suffix `{$suffix}` diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index dd15ad45145f..1ffee01b2f1c 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -199,6 +199,14 @@ pub struct InvalidCharacterInCrateName { pub span: Option, pub character: char, pub crate_name: Symbol, + #[subdiagnostic] + pub crate_name_help: Option, +} + +#[derive(Subdiagnostic)] +pub enum InvalidCrateNameHelp { + #[help(session_invalid_character_in_create_name_help)] + AddCrateName, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 2088744bc5bf..c0884fb21cd1 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -2,7 +2,7 @@ use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, - InvalidCharacterInCrateName, + InvalidCharacterInCrateName, InvalidCrateNameHelp, }; use crate::Session; use rustc_ast::{self as ast, attr}; @@ -101,7 +101,16 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option) { continue; } err_count += 1; - sess.emit_err(InvalidCharacterInCrateName { span: sp, character: c, crate_name: s }); + sess.emit_err(InvalidCharacterInCrateName { + span: sp, + character: c, + crate_name: s, + crate_name_help: if sp.is_none() { + Some(InvalidCrateNameHelp::AddCrateName) + } else { + None + }, + }); } } diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index c3a639528413..18cee7d3d5c2 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -116,7 +116,9 @@ pub fn check(path: &Path, bad: &mut bool) { // must strip all of them. let testname = file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0; - if !file_path.with_file_name(testname).with_extension("rs").exists() { + if !file_path.with_file_name(testname).with_extension("rs").exists() + && !testname.contains("ignore-tidy") + { tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path); } diff --git a/tests/ui/command/need-crate-arg-ignore-tidy.x.rs b/tests/ui/command/need-crate-arg-ignore-tidy.x.rs new file mode 100644 index 000000000000..b1ac4a4ae216 --- /dev/null +++ b/tests/ui/command/need-crate-arg-ignore-tidy.x.rs @@ -0,0 +1,2 @@ +// issue: 113981 +pub fn main() {} diff --git a/tests/ui/command/need-crate-arg-ignore-tidy.x.stderr b/tests/ui/command/need-crate-arg-ignore-tidy.x.stderr new file mode 100644 index 000000000000..305f76694f71 --- /dev/null +++ b/tests/ui/command/need-crate-arg-ignore-tidy.x.stderr @@ -0,0 +1,6 @@ +error: invalid character `'.'` in crate name: `need_crate_arg_ignore_tidy.x` + | + = help: you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name + +error: aborting due to previous error + From a7ed9c1da7a3d850be67e5cf5ae2fcabeb42de3b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 24 Jul 2023 22:02:52 +0000 Subject: [PATCH 101/105] Make everything builtin! --- .../src/transform/check_consts/check.rs | 5 +- .../src/transform/check_consts/qualifs.rs | 4 +- compiler/rustc_hir_typeck/src/coercion.rs | 11 +- compiler/rustc_middle/src/macros.rs | 4 +- compiler/rustc_middle/src/mir/basic_blocks.rs | 4 +- .../rustc_middle/src/mir/interpret/error.rs | 4 +- compiler/rustc_middle/src/mir/mod.rs | 4 +- compiler/rustc_middle/src/traits/mod.rs | 82 +++------ compiler/rustc_middle/src/traits/select.rs | 4 +- .../src/traits/structural_impls.rs | 38 +--- .../rustc_middle/src/ty/abstract_const.rs | 4 +- compiler/rustc_middle/src/ty/binding.rs | 2 +- .../src/solve/assembly/mod.rs | 115 ++++++------ .../src/solve/eval_ctxt.rs | 36 ++++ .../src/solve/eval_ctxt/select.rs | 171 +++--------------- .../src/solve/project_goals.rs | 3 +- .../src/solve/trait_goals.rs | 148 ++++++++------- .../src/traits/project.rs | 28 +-- .../src/traits/select/confirmation.rs | 88 +++++---- .../rustc_trait_selection/src/traits/util.rs | 6 +- .../src/traits/vtable.rs | 5 +- compiler/rustc_ty_utils/src/instance.rs | 19 +- .../clippy_utils/src/qualify_min_const_fn.rs | 4 +- 23 files changed, 345 insertions(+), 444 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index d8b8fa927c7e..15a7c0536cbb 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -8,6 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgKind, GenericArgs}; use rustc_middle::ty::{TraitRef, TypeVisitableExt}; @@ -766,7 +767,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }; match implsrc { - Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => { + Ok(Some(ImplSource::Param(ty::BoundConstness::ConstIfConst, _))) => { debug!( "const_trait_impl: provided {:?} via where-clause in {:?}", trait_ref, param_env @@ -774,7 +775,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } // Closure: Fn{Once|Mut} - Ok(Some(ImplSource::Builtin(_))) + Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _))) if trait_ref.self_ty().is_closure() && tcx.fn_trait_kind_from_def_id(trait_id).is_some() => { diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 0ef7ace69653..b3730dd26487 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -7,6 +7,7 @@ use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir; use rustc_middle::mir::*; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_trait_selection::traits::{ self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, @@ -172,7 +173,8 @@ impl Qualif for NeedsNonConstDrop { if !matches!( impl_src, - ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ImplSource::Builtin(BuiltinImplSource::Misc, _) + | ImplSource::Param(ty::BoundConstness::ConstIfConst, _) ) { // If our const destruct candidate is not ConstDestruct or implied by the param env, // then it's bad diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 9a25669ed31d..56e485a4a3c1 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -46,6 +46,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::lint::in_external_macro; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; @@ -687,12 +688,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } Ok(Some(impl_source)) => { + // Some builtin coercions are still unstable so we detect + // these here and emit a feature error if coercion doesn't fail + // due to another reason. match impl_source { - traits::ImplSource::TraitUpcasting(..) => { + traits::ImplSource::Builtin( + BuiltinImplSource::TraitUpcasting { .. }, + _, + ) => { has_trait_upcasting_coercion = Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); } - traits::ImplSource::TupleUnsizing(_) => { + traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { has_unsized_tuple_coercion = true; } _ => {} diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index cd1c6c330bc1..fca16d8e509f 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -43,7 +43,7 @@ macro_rules! span_bug { #[macro_export] macro_rules! CloneLiftImpls { - ($($ty:ty,)+) => { + ($($ty:ty),+ $(,)?) => { $( impl<'tcx> $crate::ty::Lift<'tcx> for $ty { type Lifted = Self; @@ -59,7 +59,7 @@ macro_rules! CloneLiftImpls { /// allocated data** (i.e., don't need to be folded). #[macro_export] macro_rules! TrivialTypeTraversalImpls { - ($($ty:ty,)+) => { + ($($ty:ty),+ $(,)?) => { $( impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty { fn try_fold_with>>( diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 7722e7b47cff..0ad17e819c74 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -178,9 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> { } } -TrivialTypeTraversalAndLiftImpls! { - Cache, -} +TrivialTypeTraversalAndLiftImpls! { Cache } impl Encodable for Cache { #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 372452ea29a8..0b532c4badcc 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -66,9 +66,7 @@ impl Into for ReportedErrorInfo { } } -TrivialTypeTraversalAndLiftImpls! { - ErrorHandled, -} +TrivialTypeTraversalAndLiftImpls! { ErrorHandled } pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 97f53a59fd66..3a9585485155 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -706,9 +706,7 @@ pub enum BindingForm<'tcx> { RefForGuard, } -TrivialTypeTraversalAndLiftImpls! { - BindingForm<'tcx>, -} +TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> } mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index fb791d564a26..a2b33bb2737a 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -649,46 +649,31 @@ pub enum ImplSource<'tcx, N> { /// for some type parameter. The `Vec` represents the /// obligations incurred from normalizing the where-clause (if /// any). - Param(Vec, ty::BoundConstness), + Param(ty::BoundConstness, Vec), - /// Virtual calls through an object. - Object(ImplSourceObjectData), - - /// Successful resolution for a builtin trait. - Builtin(Vec), - - // Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y` - TupleUnsizing(Vec), - - /// ImplSource for trait upcasting coercion - TraitUpcasting(ImplSourceTraitUpcastingData), + /// Successful resolution for a builtin impl. + Builtin(BuiltinImplSource, Vec), } impl<'tcx, N> ImplSource<'tcx, N> { pub fn nested_obligations(self) -> Vec { match self { ImplSource::UserDefined(i) => i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, - ImplSource::Object(d) => d.nested, - ImplSource::TraitUpcasting(d) => d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, } } pub fn borrow_nested_obligations(&self) -> &[N] { match self { ImplSource::UserDefined(i) => &i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => &n, - ImplSource::Object(d) => &d.nested, - ImplSource::TraitUpcasting(d) => &d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n, } } pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { match self { ImplSource::UserDefined(i) => &mut i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, - ImplSource::Object(d) => &mut d.nested, - ImplSource::TraitUpcasting(d) => &mut d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, } } @@ -702,20 +687,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { args: i.args, nested: i.nested.into_iter().map(f).collect(), }), - ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct), - ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()), - ImplSource::TupleUnsizing(n) => { - ImplSource::TupleUnsizing(n.into_iter().map(f).collect()) - } - ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData { - vtable_base: o.vtable_base, - nested: o.nested.into_iter().map(f).collect(), - }), - ImplSource::TraitUpcasting(d) => { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { - vtable_vptr_slot: d.vtable_vptr_slot, - nested: d.nested.into_iter().map(f).collect(), - }) + ImplSource::Param(ct, n) => ImplSource::Param(ct, n.into_iter().map(f).collect()), + ImplSource::Builtin(source, n) => { + ImplSource::Builtin(source, n.into_iter().map(f).collect()) } } } @@ -739,30 +713,32 @@ pub struct ImplSourceUserDefinedData<'tcx, N> { pub nested: Vec, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct ImplSourceTraitUpcastingData { - /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits, pointers to supertrait vtable will - /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable - /// within that vtable. - pub vtable_vptr_slot: Option, - - pub nested: Vec, -} - -#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct ImplSourceObjectData { +#[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Debug)] +pub enum BuiltinImplSource { + /// Some builtin impl we don't need to differentiate. This should be used + /// unless more specific information is necessary. + Misc, + /// A builtin impl for trait objects. + /// /// The vtable is formed by concatenating together the method lists of /// the base object trait and all supertraits, pointers to supertrait vtable will /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods /// in that vtable. - pub vtable_base: usize, - - pub nested: Vec, + Object { vtable_base: usize }, + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits, pointers to supertrait vtable will + /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable + /// within that vtable. + TraitUpcasting { vtable_vptr_slot: Option }, + /// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`. + /// + /// This needs to be a separate variant as it is still unstable and we need to emit + /// a feature error when using it on stable. + TupleUnsizing, } +TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource } + #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] pub enum ObjectSafetyViolation { /// `Self: Sized` declared on the trait. diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index f2dda003b99c..a90d58f5fc17 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -304,9 +304,7 @@ impl From for OverflowError { } } -TrivialTypeTraversalAndLiftImpls! { - OverflowError, -} +TrivialTypeTraversalAndLiftImpls! { OverflowError } impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index b7bce05300d2..530d1ec5d353 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -6,20 +6,16 @@ use std::fmt; impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v), + match self { + super::ImplSource::UserDefined(v) => write!(f, "{:?}", v), - super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d), - - super::ImplSource::Object(ref d) => write!(f, "{:?}", d), - - super::ImplSource::Param(ref n, ct) => { - write!(f, "ImplSourceParamData({:?}, {:?})", n, ct) + super::ImplSource::Builtin(source, d) => { + write!(f, "Builtin({source:?}, {d:?})") } - super::ImplSource::TupleUnsizing(ref d) => write!(f, "{:?}", d), - - super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), + super::ImplSource::Param(ct, n) => { + write!(f, "ImplSourceParamData({:?}, {:?})", n, ct) + } } } } @@ -33,23 +29,3 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx, ) } } - -impl fmt::Debug for traits::ImplSourceTraitUpcastingData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceTraitUpcastingData(vtable_vptr_slot={:?}, nested={:?})", - self.vtable_vptr_slot, self.nested - ) - } -} - -impl fmt::Debug for traits::ImplSourceObjectData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceObjectData(vtable_base={}, nested={:?})", - self.vtable_base, self.nested - ) - } -} diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 0364a620810a..cdd8351499b4 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -27,9 +27,7 @@ impl From for NotConstEvaluatable { } } -TrivialTypeTraversalAndLiftImpls! { - NotConstEvaluatable, -} +TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable } pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs index a5b05a4f9b52..2fec8ac90956 100644 --- a/compiler/rustc_middle/src/ty/binding.rs +++ b/compiler/rustc_middle/src/ty/binding.rs @@ -6,7 +6,7 @@ pub enum BindingMode { BindByValue(Mutability), } -TrivialTypeTraversalAndLiftImpls! { BindingMode, } +TrivialTypeTraversalAndLiftImpls! { BindingMode } impl BindingMode { pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 942f40884720..ab90db6ff586 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -3,17 +3,16 @@ use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use crate::traits::coherence; -use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::util::elaborate; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; -use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{fast_reject, TypeFoldable}; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; use std::fmt::Debug; @@ -89,17 +88,6 @@ pub(super) enum CandidateSource { AliasBound, } -/// Records additional information about what kind of built-in impl this is. -/// This should only be used by selection. -#[derive(Debug, Clone, Copy)] -pub(super) enum BuiltinImplSource { - TraitUpcasting, - TupleUnsize, - Object, - Misc, - Ambiguity, -} - /// Methods used to assemble candidates for either trait or projection goals. pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq + std::fmt::Display @@ -282,20 +270,6 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - /// Consider (possibly several) goals to upcast or unsize a type to another - /// type. - /// - /// The most common forms of unsizing are array to slice, and concrete (Sized) - /// type into a `dyn Trait`. ADTs and Tuples can also have their final field - /// unsized if it's generic. - /// - /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or - /// if `Trait2` is a (transitive) supertrait of `Trait2`. - fn consider_builtin_unsize_and_upcast_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; - fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -310,6 +284,25 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + /// Consider (possibly several) candidates to upcast or unsize a type to another + /// type. + /// + /// The most common forms of unsizing are array to slice, and concrete (Sized) + /// type into a `dyn Trait`. ADTs and Tuples can also have their final field + /// unsized if it's generic. + /// + /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or + /// if `Trait2` is a (transitive) supertrait of `Trait2`. + /// + /// We return the `BuiltinImplSource` for each candidate as it is needed + /// for unsize coercion in hir typeck and because it is difficult to + /// otherwise recompute this for codegen. This is a bit of a mess but the + /// easiest way to maintain the existing behavior for now. + fn consider_builtin_unsize_and_upcast_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -343,7 +336,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Option>> { goal.predicate.self_ty().is_ty_var().then(|| { vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result: self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .unwrap(), @@ -412,7 +405,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Certainty::Maybe(MaybeCause::Overflow), )?; Ok(vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result, }]) }, @@ -848,29 +841,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(bounds, ..) => bounds, }; - let own_bounds: FxIndexSet<_> = - bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect(); - for assumption in elaborate(tcx, own_bounds.iter().copied()) - // we only care about bounds that match the `Self` type - .filter_only_self() - { - // FIXME: Predicates are fully elaborated in the object type's existential bounds - // list. We want to only consider these pre-elaborated projections, and not other - // projection predicates that we reach by elaborating the principal trait ref, - // since that'll cause ambiguity. - // - // We can remove this when we have implemented lifetime intersections in responses. - if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) { - continue; + // Consider all of the auto-trait and projection bounds, which don't + // need to be recorded as a `BuiltinImplSource::Object` since they don't + // really have a vtable base... + for bound in bounds { + match bound.skip_binder() { + ty::ExistentialPredicate::Trait(_) => { + // Skip principal + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => { + match G::consider_object_bound_candidate( + self, + goal, + bound.with_self_ty(tcx, self_ty), + ) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => (), + } + } } + } - match G::consider_object_bound_candidate(self, goal, assumption) { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object), - result, - }), - Err(NoSolution) => (), - } + // FIXME: We only need to do *any* of this if we're considering a trait goal, + // since we don't need to look at any supertrait or anything if we are doing + // a projection goal. + if let Some(principal) = bounds.principal() { + let principal_trait_ref = principal.with_self_ty(tcx, self_ty); + self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| { + match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object { + vtable_base, + }), + result, + }), + Err(NoSolution) => (), + } + }); } } @@ -890,7 +901,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result, }), // FIXME: This will be reachable at some point if we're in diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 6e0aa08c307b..b04d77f0a1a3 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -25,6 +25,7 @@ use std::io::Write; use std::ops::ControlFlow; use crate::traits::specialization_graph; +use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; use super::search_graph::{self, OverflowHandler}; @@ -920,4 +921,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Err(ErrorHandled::TooGeneric) => None, } } + + /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor` + /// for every trait ref encountered (including the principal). Passes both the vtable + /// base and the (optional) vptr slot. + pub(super) fn walk_vtable( + &mut self, + principal: ty::PolyTraitRef<'tcx>, + mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option), + ) { + let tcx = self.tcx(); + let mut offset = 0; + prepare_vtable_segments::<()>(tcx, principal, |segment| { + match segment { + VtblSegment::MetadataDSA => { + offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref); + + supertrait_visitor( + self, + trait_ref, + offset, + emit_vptr.then(|| offset + own_vtable_entries), + ); + + offset += own_vtable_entries; + if emit_vptr { + offset += 1; + } + } + } + ControlFlow::Continue(()) + }); + } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index cdfbaedb8c23..6045001510e4 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -1,25 +1,20 @@ -use std::ops::ControlFlow; - use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; -use rustc_infer::traits::util::supertraits; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt}; use rustc_infer::traits::{ Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine, }; use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal}; use rustc_middle::traits::{ - ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, - ObligationCause, SelectionError, + BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError, }; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource}; +use crate::solve::assembly::{Candidate, CandidateSource}; use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::inspect::ProofTreeBuilder; use crate::solve::search_graph::OverflowHandler; -use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use crate::traits::StructurallyNormalizeExt; use crate::traits::TraitEngineExt; @@ -105,47 +100,26 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { rematch_impl(self, goal, def_id, nested_obligations) } - // Rematching the dyn upcast or object goal will instantiate the same nested - // goals that would have caused the ambiguity, so we can still make progress here - // regardless. - // FIXME: This doesn't actually check the object bounds hold here. - ( - _, - CandidateSource::BuiltinImpl( - BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting, - ), - ) => rematch_object(self, goal, nested_obligations), - - ( - Certainty::Maybe(_), - CandidateSource::BuiltinImpl( - BuiltinImplSource::Misc | BuiltinImplSource::TupleUnsize, - ), - ) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - rematch_unsize(self, goal, nested_obligations) - } - - (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize)) + (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src)) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - Ok(Some(ImplSource::TupleUnsizing(nested_obligations))) + rematch_unsize(self, goal, nested_obligations, src) } // Technically some builtin impls have nested obligations, but if // `Certainty::Yes`, then they should've all been verified and don't // need re-checking. - (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => { - Ok(Some(ImplSource::Builtin(nested_obligations))) + (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => { + Ok(Some(ImplSource::Builtin(src, nested_obligations))) } // It's fine not to do anything to rematch these, since there are no // nested obligations. (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { - Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst))) + Ok(Some(ImplSource::Param(ty::BoundConstness::NotConst, nested_obligations))) } - (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity)) - | (Certainty::Maybe(_), _) => Ok(None), + (Certainty::Maybe(_), _) => Ok(None), } } } @@ -192,11 +166,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( } (_, CandidateSource::ParamEnv(_)) => true, + // FIXME: we could prefer earlier vtable bases perhaps... ( - CandidateSource::BuiltinImpl(BuiltinImplSource::Object), - CandidateSource::BuiltinImpl(BuiltinImplSource::Object), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), ) => false, - (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true, + (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true, (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { tcx.specializes((other_def_id, victim_def_id)) @@ -234,108 +209,6 @@ fn rematch_impl<'tcx>( Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested }))) } -fn rematch_object<'tcx>( - infcx: &InferCtxt<'tcx>, - goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, - mut nested: Vec>, -) -> SelectionResult<'tcx, Selection<'tcx>> { - let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); - let ty::Dynamic(data, _, source_kind) = *a_ty.kind() else { bug!() }; - let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, a_ty); - - let (is_upcasting, target_trait_ref_unnormalized) = - if Some(goal.predicate.def_id()) == infcx.tcx.lang_items().unsize_trait() { - assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*"); - let b_ty = structurally_normalize( - goal.predicate.trait_ref.args.type_at(1), - infcx, - goal.param_env, - &mut nested, - ); - if let ty::Dynamic(data, _, ty::Dyn) = *b_ty.kind() { - // FIXME: We also need to ensure that the source lifetime outlives the - // target lifetime. This doesn't matter for codegen, though, and only - // *really* matters if the goal's certainty is ambiguous. - (true, data.principal().unwrap().with_self_ty(infcx.tcx, a_ty)) - } else { - bug!() - } - } else { - (false, ty::Binder::dummy(goal.predicate.trait_ref)) - }; - - let mut target_trait_ref = None; - for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) { - let result = infcx.commit_if_ok(|_| { - infcx.at(&ObligationCause::dummy(), goal.param_env).eq( - DefineOpaqueTypes::No, - target_trait_ref_unnormalized, - candidate_trait_ref, - ) - - // FIXME: We probably should at least shallowly verify these... - }); - - match result { - Ok(InferOk { value: (), obligations }) => { - target_trait_ref = Some(candidate_trait_ref); - nested.extend(obligations); - break; - } - Err(_) => continue, - } - } - - let target_trait_ref = target_trait_ref.unwrap(); - - let mut offset = 0; - let Some((vtable_base, vtable_vptr_slot)) = - prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| { - match segment { - VtblSegment::MetadataDSA => { - offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref); - - if trait_ref == target_trait_ref { - if emit_vptr { - return ControlFlow::Break(( - offset, - Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)), - )); - } else { - return ControlFlow::Break((offset, None)); - } - } - - offset += own_vtable_entries; - if emit_vptr { - offset += 1; - } - } - } - ControlFlow::Continue(()) - }) - else { - bug!(); - }; - - // If we're upcasting, get the offset of the vtable pointer, otherwise get - // the base of the vtable. - Ok(Some(if is_upcasting { - // If source and target trait def ids are identical, - // then we are simply removing auto traits. - if source_trait_ref.def_id() == target_trait_ref.def_id() { - ImplSource::Builtin(nested) - } else { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) - } - } else { - ImplSource::Object(ImplSourceObjectData { vtable_base, nested }) - })) -} - /// The `Unsize` trait is particularly important to coercion, so we try rematch it. /// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait /// goal assembly in the solver, both for soundness and in order to avoid ICEs. @@ -343,6 +216,7 @@ fn rematch_unsize<'tcx>( infcx: &InferCtxt<'tcx>, goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, mut nested: Vec>, + source: BuiltinImplSource, ) -> SelectionResult<'tcx, Selection<'tcx>> { let tcx = infcx.tcx; let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); @@ -379,6 +253,8 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), )); + + Ok(Some(ImplSource::Builtin(source, nested))) } // `[T; n]` -> `[T]` unsizing (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { @@ -389,6 +265,8 @@ fn rematch_unsize<'tcx>( .expect("expected rematch to succeed") .into_obligations(), ); + + Ok(Some(ImplSource::Builtin(source, nested))) } // Struct unsizing `Struct` -> `Struct` where `T: Unsize` (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) @@ -439,6 +317,8 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), )); + + Ok(Some(ImplSource::Builtin(source, nested))) } // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) @@ -467,15 +347,18 @@ fn rematch_unsize<'tcx>( )); // We need to be able to detect tuple unsizing to require its feature gate. - return Ok(Some(ImplSource::TupleUnsizing(nested))); + assert_eq!( + source, + BuiltinImplSource::TupleUnsizing, + "compiler-errors wants to know if this can ever be triggered..." + ); + Ok(Some(ImplSource::Builtin(source, nested))) } // FIXME: We *could* ICE here if either: // 1. the certainty is `Certainty::Yes`, // 2. we're in codegen (which should mean `Certainty::Yes`). - _ => return Ok(None), + _ => Ok(None), } - - Ok(Some(ImplSource::Builtin(nested))) } fn structurally_normalize<'tcx>( diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 0f4fec50427a..03ade738bb63 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,6 +1,6 @@ use crate::traits::specialization_graph; -use super::assembly::{self, structural_traits, BuiltinImplSource}; +use super::assembly::{self, structural_traits}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -10,6 +10,7 @@ use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 7f813c58c84e..24ea9b5ea124 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,15 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits, BuiltinImplSource}; +use super::assembly::{self, structural_traits}; use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::util::supertraits; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; -use rustc_middle::traits::Reveal; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; @@ -382,37 +381,48 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let b_ty = match ecx .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) { - Ok(Some(b_ty)) if !b_ty.is_ty_var() => b_ty, - Ok(_) => { - return vec![( - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + Ok(Some(b_ty)) => { + // If we have a type var, then bail with ambiguity. + if b_ty.is_ty_var() { + return vec![( + ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ) .unwrap(), - BuiltinImplSource::Ambiguity, + BuiltinImplSource::Misc, + )]; + } else { + b_ty + } + } + Ok(None) => { + return vec![( + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( + MaybeCause::Overflow, + )) + .unwrap(), + BuiltinImplSource::Misc, )]; } Err(_) => return vec![], }; let mut results = vec![]; + results.extend(ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty)); results.extend( - ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty) + ecx.consider_builtin_unsize_candidate(goal.with(ecx.tcx(), (a_ty, b_ty))) .into_iter() - .map(|resp| (resp, BuiltinImplSource::TraitUpcasting)), - ); - results.extend( - ecx.consider_builtin_unsize_candidate(goal.param_env, a_ty, b_ty).into_iter().map( - |resp| { + .map(|resp| { // If we're unsizing from tuple -> tuple, detect let source = if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..))) { - BuiltinImplSource::TupleUnsize + BuiltinImplSource::TupleUnsizing } else { BuiltinImplSource::Misc }; (resp, source) - }, - ), + }), ); results @@ -484,10 +494,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> { fn consider_builtin_unsize_candidate( &mut self, - param_env: ty::ParamEnv<'tcx>, - a_ty: Ty<'tcx>, - b_ty: Ty<'tcx>, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, ) -> QueryResult<'tcx> { + let Goal { param_env, predicate: (a_ty, b_ty) } = goal; self.probe_candidate("builtin unsize").enter(|ecx| { let tcx = ecx.tcx(); match (a_ty.kind(), b_ty.kind()) { @@ -614,7 +623,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, a_ty: Ty<'tcx>, b_ty: Ty<'tcx>, - ) -> Vec> { + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { if a_ty.is_ty_var() || b_ty.is_ty_var() { bug!("unexpected type variable in unsize goal") } @@ -634,57 +643,64 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return vec![]; } - let mut unsize_dyn_to_principal = |principal: Option>| { - self.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); + // Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with + // the supertrait principal and subtyping the types. + let unsize_dyn_to_principal = + |ecx: &mut Self, principal: Option>| { + ecx.probe_candidate("upcast dyn to principle").enter( + |ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(param_env, new_a_ty, b_ty)?; - ecx.add_goal(Goal::new( - tcx, - param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - }; + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(param_env, new_a_ty, b_ty)?; + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }, + ) + }; let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); - } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { - responses.push(response); - } + if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) { + responses.push((resp, BuiltinImplSource::Misc)); } + } else if let Some(a_principal) = a_data.principal() { + self.walk_vtable( + a_principal.with_self_ty(tcx, a_ty), + |ecx, new_a_principal, _, vtable_vptr_slot| { + if let Ok(resp) = unsize_dyn_to_principal( + ecx, + Some(new_a_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + })), + ) { + responses + .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot })); + } + }, + ); } responses diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 18470d520f7d..a35dd1f1a591 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -4,7 +4,6 @@ use super::check_args_compatible; use super::specialization_graph; use super::translate_args; use super::util; -use super::ImplSourceUserDefinedData; use super::MismatchedProjectionTypes; use super::Obligation; use super::ObligationCause; @@ -13,6 +12,9 @@ use super::Selection; use super::SelectionContext; use super::SelectionError; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::traits::ImplSource; +use rustc_middle::traits::ImplSourceUserDefinedData; use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -1717,7 +1719,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( }; let eligible = match &impl_source { - super::ImplSource::UserDefined(impl_data) => { + ImplSource::UserDefined(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in // codegen (i.e., projection mode is not "any"), and the @@ -1767,7 +1769,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } - super::ImplSource::Builtin(..) => { + ImplSource::Builtin(BuiltinImplSource::Misc, _) => { // While a builtin impl may be known to exist, the associated type may not yet // be known. Any type with multiple potential associated types is therefore // not eligible. @@ -1891,7 +1893,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( bug!("unexpected builtin trait with associated type: {trait_ref:?}") } } - super::ImplSource::Param(..) => { + ImplSource::Param(..) => { // This case tell us nothing about the value of an // associated type. Consider: // @@ -1919,14 +1921,14 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // in `assemble_candidates_from_param_env`. false } - super::ImplSource::Object(_) => { + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => { // Handled by the `Object` projection candidate. See // `assemble_candidates_from_object_ty` for an explanation of // why we special case object types. false } - super::ImplSource::TraitUpcasting(_) - | super::ImplSource::TupleUnsizing(_) => { + ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1986,8 +1988,8 @@ fn confirm_select_candidate<'cx, 'tcx>( impl_source: Selection<'tcx>, ) -> Progress<'tcx> { match impl_source { - super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), - super::ImplSource::Builtin(data) => { + ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), + ImplSource::Builtin(BuiltinImplSource::Misc, data) => { let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx()); let lang_items = selcx.tcx().lang_items(); if lang_items.gen_trait() == Some(trait_def_id) { @@ -2004,10 +2006,10 @@ fn confirm_select_candidate<'cx, 'tcx>( confirm_builtin_candidate(selcx, obligation, data) } } - super::ImplSource::Object(_) - | super::ImplSource::Param(..) - | super::ImplSource::TraitUpcasting(_) - | super::ImplSource::TupleUnsizing(_) => { + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) + | ImplSource::Param(..) + | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index f423d40b9949..92b9364adfa1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -11,7 +11,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::traits::SelectionOutputTypeParameterMismatch; +use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch}; use rustc_middle::ty::{ self, Binder, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, @@ -26,9 +26,9 @@ use crate::traits::vtable::{ }; use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, - ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, - Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation, - PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented, + ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, + OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection, + SelectionError, TraitNotObjectSafe, Unimplemented, }; use super::BuiltinImplConditions; @@ -48,18 +48,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut impl_src = match candidate { BuiltinCandidate { has_nested } => { let data = self.confirm_builtin_candidate(obligation, has_nested); - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } TransmutabilityCandidate => { let data = self.confirm_transmutability_candidate(obligation)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); - ImplSource::Param(obligations, param.skip_binder().constness) + ImplSource::Param(param.skip_binder().constness, obligations) } ImplCandidate(impl_def_id) => { @@ -68,76 +68,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { AutoImplCandidate => { let data = self.confirm_auto_impl_candidate(obligation)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } ProjectionCandidate(idx, constness) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; - ImplSource::Param(obligations, constness) + ImplSource::Param(constness, obligations) } - ObjectCandidate(idx) => { - let data = self.confirm_object_candidate(obligation, idx)?; - ImplSource::Object(data) - } + ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?, ClosureCandidate { .. } => { let vtable_closure = self.confirm_closure_candidate(obligation)?; - ImplSource::Builtin(vtable_closure) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure) } GeneratorCandidate => { let vtable_generator = self.confirm_generator_candidate(obligation)?; - ImplSource::Builtin(vtable_generator) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_generator) } FutureCandidate => { let vtable_future = self.confirm_future_candidate(obligation)?; - ImplSource::Builtin(vtable_future) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future) } FnPointerCandidate { is_const } => { let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } TraitAliasCandidate => { let data = self.confirm_trait_alias_candidate(obligation); - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } BuiltinObjectCandidate => { // This indicates something like `Trait + Send: Send`. In this case, we know that // this holds because that's what the object type is telling us, and there's really // no additional obligations to prove and no types in particular to unify, etc. - ImplSource::Builtin(Vec::new()) + ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new()) } - BuiltinUnsizeCandidate => { - let source = - self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); - let target = self.infcx.shallow_resolve(target); - let data = self.confirm_builtin_unsize_candidate(obligation, source, target)?; - // If the source and target are both unsize goals, then we need to signify that - // this is tuple unsizing so that during unsized coercion we require the proper - // feature gate. - if matches!(source.kind(), ty::Tuple(..)) && matches!(target.kind(), ty::Tuple(..)) - { - ImplSource::TupleUnsizing(data) - } else { - ImplSource::Builtin(data) - } - } + BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?, TraitUpcastingUnsizeCandidate(idx) => { - let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; - ImplSource::TraitUpcasting(data) + self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } ConstDestructCandidate(def_id) => { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } }; @@ -496,7 +477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, index: usize, - ) -> Result>, SelectionError<'tcx>> { + ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); debug!(?obligation, ?index, "confirm_object_candidate"); @@ -660,7 +641,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)), ); - Ok(ImplSourceObjectData { vtable_base, nested }) + Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested)) } fn confirm_fn_pointer_candidate( @@ -909,7 +890,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, idx: usize, - ) -> Result>, SelectionError<'tcx>> { + ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); // `assemble_candidates_for_unsizing` should ensure there are no late-bound @@ -1006,19 +987,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let vtable_vptr_slot = prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); - Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested)) } fn confirm_builtin_unsize_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - source: Ty<'tcx>, - target: Ty<'tcx>, - ) -> Result>, SelectionError<'tcx>> { + ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let target = self.infcx.shallow_resolve(target); debug!(?source, ?target, "confirm_builtin_unsize_candidate"); let mut nested = vec![]; + let src; match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). (&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b)) @@ -1062,6 +1048,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.predicate.rebind(outlives), )); + + src = BuiltinImplSource::Misc; } // `T` -> `Trait` @@ -1108,6 +1096,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested.push(predicate_to_obligation( ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx), )); + + src = BuiltinImplSource::Misc; } // `[T; n]` -> `[T]` @@ -1118,6 +1108,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .eq(DefineOpaqueTypes::No, b, a) .map_err(|_| Unimplemented)?; nested.extend(obligations); + + src = BuiltinImplSource::Misc; } // `Struct` -> `Struct` @@ -1174,6 +1166,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ), ); nested.push(tail_unsize_obligation); + + src = BuiltinImplSource::Misc; } // `(.., T)` -> `(.., U)` @@ -1201,12 +1195,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]), ); nested.push(last_unsize_obligation); + + src = BuiltinImplSource::TupleUnsizing; } _ => bug!("source: {source}, target: {target}"), }; - Ok(nested) + Ok(ImplSource::Builtin(src, nested)) } fn confirm_const_destruct_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 7b34908c4bed..3eab885a089b 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -241,9 +241,9 @@ pub fn upcast_choices<'tcx>( /// Given an upcast trait object described by `object`, returns the /// index of the method `method_def_id` (which should be part of /// `object.upcast_trait_ref`) within the vtable for `object`. -pub fn get_vtable_index_of_object_method<'tcx, N>( +pub fn get_vtable_index_of_object_method<'tcx>( tcx: TyCtxt<'tcx>, - object: &super::ImplSourceObjectData, + vtable_base: usize, method_def_id: DefId, ) -> Option { // Count number of methods preceding the one we are selecting and @@ -252,7 +252,7 @@ pub fn get_vtable_index_of_object_method<'tcx, N>( .iter() .copied() .position(|def_id| def_id == method_def_id) - .map(|index| object.vtable_base + index) + .map(|index| vtable_base + index) } pub fn closure_trait_ref_and_return_type<'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index b4f614e3e220..c4cc89aa9077 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -5,6 +5,7 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::traits::util::PredicateSet; use rustc_infer::traits::ImplSource; use rustc_middle::query::Providers; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; @@ -384,8 +385,8 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]); match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { - Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => { - implsrc_traitcasting.vtable_vptr_slot + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => { + *vtable_vptr_slot } otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"), } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 88eb33fee379..a21b5ef05e60 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -2,7 +2,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; -use rustc_middle::traits::CodegenObligationError; +use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_span::sym; @@ -177,12 +177,15 @@ fn resolve_associated_item<'tcx>( Some(ty::Instance::new(leaf_def.item.def_id, args)) } - traits::ImplSource::Object(ref data) => { - traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| { - Instance { def: ty::InstanceDef::Virtual(trait_item_id, index), args: rcvr_args } - }) + traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => { + traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map( + |index| Instance { + def: ty::InstanceDef::Virtual(trait_item_id, index), + args: rcvr_args, + }, + ) } - traits::ImplSource::Builtin(..) => { + traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => { let lang_items = tcx.lang_items(); if Some(trait_ref.def_id) == lang_items.clone_trait() { // FIXME(eddyb) use lang items for methods instead of names. @@ -291,8 +294,8 @@ fn resolve_associated_item<'tcx>( } } traits::ImplSource::Param(..) - | traits::ImplSource::TraitUpcasting(_) - | traits::ImplSource::TupleUnsizing(_) => None, + | traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => None, }) } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 1c29d614fa34..e563e41ab2aa 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::{ Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_middle::traits::{ImplSource, ObligationCause}; +use rustc_middle::traits::{ImplSource, ObligationCause, BuiltinImplSource}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, BoundConstness, GenericArgKind, TraitRef, Ty, TyCtxt}; use rustc_semver::RustcVersion; @@ -411,7 +411,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> if !matches!( impl_src, - ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(ty::BoundConstness::ConstIfConst, _) ) { return false; } From ecfac686afc0222479bc129936048f0e44feb30f Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Mon, 24 Jul 2023 12:21:50 -0500 Subject: [PATCH 102/105] Remove credential providers from bootstrap since they are now built-in to the Cargo binary --- src/bootstrap/dist.rs | 7 ------- src/bootstrap/doc.rs | 9 --------- src/bootstrap/tool.rs | 33 --------------------------------- 3 files changed, 49 deletions(-) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 8c71b7f7fc22..c8750c193887 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -1085,13 +1085,6 @@ impl Step for Cargo { tarball.add_dir(etc.join("man"), "share/man/man1"); tarball.add_legal_and_readme_to("share/doc/cargo"); - for dirent in fs::read_dir(cargo.parent().unwrap()).expect("read_dir") { - let dirent = dirent.expect("read dir entry"); - if dirent.file_name().to_str().expect("utf8").starts_with("cargo-credential-") { - tarball.add_file(&dirent.path(), "libexec", 0o755); - } - } - Some(tarball.generate()) } } diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index d4807aff4d69..2ccbf179ccaf 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -894,19 +894,10 @@ tool_doc!( "-p", "cargo-credential", "-p", - "cargo-credential-1password", - "-p", "mdman", // FIXME: this trips a license check in tidy. // "-p", // "resolver-tests", - // FIXME: we should probably document these, but they're different per-platform so we can't use `tool_doc`. - // "-p", - // "cargo-credential-gnome-secret", - // "-p", - // "cargo-credential-macos-keychain", - // "-p", - // "cargo-credential-wincred", ] ); tool_doc!(Tidy, "tidy", "src/tools/tidy", rustc_tool = false, ["-p", "tidy"]); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 725f864b718c..e6d27757ac66 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -558,39 +558,6 @@ impl Step for Cargo { allow_features: "", }) .expect("expected to build -- essential tool"); - - let build_cred = |name, path| { - // These credential helpers are currently experimental. - // Any build failures will be ignored. - let _ = builder.ensure(ToolBuild { - compiler: self.compiler, - target: self.target, - tool: name, - mode: Mode::ToolRustc, - path, - is_optional_tool: true, - source_type: SourceType::Submodule, - extra_features: Vec::new(), - allow_features: "", - }); - }; - - if self.target.contains("windows") { - build_cred( - "cargo-credential-wincred", - "src/tools/cargo/credential/cargo-credential-wincred", - ); - } - if self.target.contains("apple-darwin") { - build_cred( - "cargo-credential-macos-keychain", - "src/tools/cargo/credential/cargo-credential-macos-keychain", - ); - } - build_cred( - "cargo-credential-1password", - "src/tools/cargo/credential/cargo-credential-1password", - ); cargo_bin_path } } From 7d46885dc192d9e0eaebe2f55e90326c9ec6216d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 25 Jul 2023 17:56:18 +0200 Subject: [PATCH 103/105] Split nested GHA groups instead of panicking --- src/tools/build_helper/src/ci.rs | 62 ++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs index b70ea5ec1139..a8505ec95967 100644 --- a/src/tools/build_helper/src/ci.rs +++ b/src/tools/build_helper/src/ci.rs @@ -36,25 +36,26 @@ impl CiEnv { } pub mod gha { - use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Mutex; - static GROUP_ACTIVE: AtomicBool = AtomicBool::new(false); + static ACTIVE_GROUPS: Mutex> = Mutex::new(Vec::new()); /// All github actions log messages from this call to the Drop of the return value - /// will be grouped and hidden by default in logs. Note that nesting these does - /// not really work. + /// will be grouped and hidden by default in logs. Note that since github actions doesn't + /// support group nesting, any active group will be first finished when a subgroup is started, + /// and then re-started when the subgroup finishes. #[track_caller] pub fn group(name: impl std::fmt::Display) -> Group { - if std::env::var_os("GITHUB_ACTIONS").is_some() { - eprintln!("::group::{name}"); - } else { - eprintln!("{name}") + let mut groups = ACTIVE_GROUPS.lock().unwrap(); + + // A group is currently active. End it first to avoid nesting. + if !groups.is_empty() { + end_group(); } - // https://github.com/actions/toolkit/issues/1001 - assert!( - !GROUP_ACTIVE.swap(true, Ordering::Relaxed), - "nested groups are not supported by GHA!" - ); + + let name = name.to_string(); + start_group(&name); + groups.push(name); Group(()) } @@ -64,13 +65,36 @@ pub mod gha { impl Drop for Group { fn drop(&mut self) { - if std::env::var_os("GITHUB_ACTIONS").is_some() { - eprintln!("::endgroup::"); + end_group(); + + let mut groups = ACTIVE_GROUPS.lock().unwrap(); + // Remove the current group + groups.pop(); + + // If there was some previous group, restart it + if is_in_gha() { + if let Some(name) = groups.last() { + start_group(format!("{name} (continued)")); + } } - assert!( - GROUP_ACTIVE.swap(false, Ordering::Relaxed), - "group dropped but no group active!" - ); } } + + fn start_group(name: impl std::fmt::Display) { + if is_in_gha() { + eprintln!("::group::{name}"); + } else { + eprintln!("{name}") + } + } + + fn end_group() { + if is_in_gha() { + eprintln!("::endgroup::"); + } + } + + fn is_in_gha() -> bool { + std::env::var_os("GITHUB_ACTIONS").is_some() + } } From 13e2abf6b388762af2e97ce065d6206961264a8f Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Mon, 10 Jul 2023 23:44:52 -0400 Subject: [PATCH 104/105] add tidy check that forbids issue ui test filenames --- src/tools/tidy/src/issues.txt | 4282 ++++++++++++++++++++++++++++++++ src/tools/tidy/src/ui_tests.rs | 53 +- 2 files changed, 4329 insertions(+), 6 deletions(-) create mode 100644 src/tools/tidy/src/issues.txt diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt new file mode 100644 index 000000000000..9f1e8d1b3f35 --- /dev/null +++ b/src/tools/tidy/src/issues.txt @@ -0,0 +1,4282 @@ +/* +============================================================ + ⚠️⚠️⚠️NOTHING SHOULD EVER BE ADDED TO THIS LIST⚠️⚠️⚠️ +============================================================ +*/ +[ +"ui/abi/issues/issue-22565-rust-call.rs", +"ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs", +"ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs", +"ui/abi/issue-28676.rs", +"ui/abi/issue-94223.rs", +"ui/argument-suggestions/issue-100154.rs", +"ui/argument-suggestions/issue-100478.rs", +"ui/argument-suggestions/issue-101097.rs", +"ui/argument-suggestions/issue-109831.rs", +"ui/argument-suggestions/issue-96638.rs", +"ui/argument-suggestions/issue-97197.rs", +"ui/argument-suggestions/issue-97484.rs", +"ui/argument-suggestions/issue-98894.rs", +"ui/argument-suggestions/issue-98897.rs", +"ui/argument-suggestions/issue-99482.rs", +"ui/argument-suggestions/issue-112507.rs", +"ui/argument-suggestions/issue-109425.rs", +"ui/array-slice-vec/issue-15730.rs", +"ui/array-slice-vec/issue-18425.rs", +"ui/array-slice-vec/issue-69103-extra-binding-subslice.rs", +"ui/asm/x86_64/issue-82869.rs", +"ui/asm/x86_64/issue-89875.rs", +"ui/asm/x86_64/issue-96797.rs", +"ui/asm/issue-72570.rs", +"ui/asm/issue-85247.rs", +"ui/asm/issue-87802.rs", +"ui/asm/issue-89305.rs", +"ui/asm/issue-92378.rs", +"ui/asm/issue-97490.rs", +"ui/asm/issue-99071.rs", +"ui/asm/issue-99122-2.rs", +"ui/asm/issue-99122.rs", +"ui/associated-consts/issue-102335-const.rs", +"ui/associated-consts/issue-105330.rs", +"ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs", +"ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs", +"ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs", +"ui/associated-consts/issue-47814.rs", +"ui/associated-consts/issue-58022.rs", +"ui/associated-consts/issue-63496.rs", +"ui/associated-consts/issue-93775.rs", +"ui/associated-consts/issue-93835.rs", +"ui/associated-consts/issue-69020-assoc-const-arith-overflow.rs", +"ui/associated-consts/issue-88599-ref-self.rs", +"ui/associated-consts/issue-110933.rs", +"ui/associated-inherent-types/issue-109299-1.rs", +"ui/associated-inherent-types/issue-109299.rs", +"ui/associated-inherent-types/issue-109768.rs", +"ui/associated-inherent-types/issue-109789.rs", +"ui/associated-inherent-types/issue-111879-0.rs", +"ui/associated-inherent-types/issue-111879-1.rs", +"ui/associated-inherent-types/issue-111404-1.rs", +"ui/associated-inherent-types/issue-104260.rs", +"ui/associated-inherent-types/issue-109790.rs", +"ui/associated-inherent-types/issue-111404-0.rs", +"ui/associated-inherent-types/issue-109071.rs", +"ui/associated-item/issue-48027.rs", +"ui/associated-item/issue-105449.rs", +"ui/associated-item/issue-87638.rs", +"ui/associated-type-bounds/issue-102335-ty.rs", +"ui/associated-type-bounds/issue-104916.rs", +"ui/associated-type-bounds/issue-71443-1.rs", +"ui/associated-type-bounds/issue-99828.rs", +"ui/associated-type-bounds/issue-61752.rs", +"ui/associated-type-bounds/issue-70292.rs", +"ui/associated-type-bounds/issue-71443-2.rs", +"ui/associated-type-bounds/issue-73818.rs", +"ui/associated-type-bounds/issue-79949.rs", +"ui/associated-type-bounds/issue-81193.rs", +"ui/associated-type-bounds/issue-83017.rs", +"ui/associated-types/issue-18655.rs", +"ui/associated-types/issue-19883.rs", +"ui/associated-types/issue-20005.rs", +"ui/associated-types/issue-20825.rs", +"ui/associated-types/issue-22037.rs", +"ui/associated-types/issue-22560.rs", +"ui/associated-types/issue-22828.rs", +"ui/associated-types/issue-23208.rs", +"ui/associated-types/issue-23595-1.rs", +"ui/associated-types/issue-23595-2.rs", +"ui/associated-types/issue-25339.rs", +"ui/associated-types/issue-25700-1.rs", +"ui/associated-types/issue-25700-2.rs", +"ui/associated-types/issue-25700.rs", +"ui/associated-types/issue-26681.rs", +"ui/associated-types/issue-27675-unchecked-bounds.rs", +"ui/associated-types/issue-27901.rs", +"ui/associated-types/issue-38821.rs", +"ui/associated-types/issue-43784-associated-type.rs", +"ui/associated-types/issue-43924.rs", +"ui/associated-types/issue-44153.rs", +"ui/associated-types/issue-47139-1.rs", +"ui/associated-types/issue-47139-2.rs", +"ui/associated-types/issue-47814.rs", +"ui/associated-types/issue-54108.rs", +"ui/associated-types/issue-54182-1.rs", +"ui/associated-types/issue-54467.rs", +"ui/associated-types/issue-55846.rs", +"ui/associated-types/issue-59324.rs", +"ui/associated-types/issue-62200.rs", +"ui/associated-types/issue-63593.rs", +"ui/associated-types/issue-64848.rs", +"ui/associated-types/issue-64855.rs", +"ui/associated-types/issue-65774-1.rs", +"ui/associated-types/issue-65774-2.rs", +"ui/associated-types/issue-72806.rs", +"ui/associated-types/issue-85103.rs", +"ui/associated-types/issue-87261.rs", +"ui/associated-types/issue-19081.rs", +"ui/associated-types/issue-20825-2.rs", +"ui/associated-types/issue-21363.rs", +"ui/associated-types/issue-21726.rs", +"ui/associated-types/issue-22066.rs", +"ui/associated-types/issue-24159.rs", +"ui/associated-types/issue-24204.rs", +"ui/associated-types/issue-24338.rs", +"ui/associated-types/issue-28871.rs", +"ui/associated-types/issue-31597.rs", +"ui/associated-types/issue-32350.rs", +"ui/associated-types/issue-36499.rs", +"ui/associated-types/issue-37808.rs", +"ui/associated-types/issue-37883.rs", +"ui/associated-types/issue-38917.rs", +"ui/associated-types/issue-39532.rs", +"ui/associated-types/issue-40093.rs", +"ui/associated-types/issue-41868.rs", +"ui/associated-types/issue-43475.rs", +"ui/associated-types/issue-47385.rs", +"ui/associated-types/issue-48010.rs", +"ui/associated-types/issue-48551.rs", +"ui/associated-types/issue-50301.rs", +"ui/associated-types/issue-54182-2.rs", +"ui/associated-types/issue-63591.rs", +"ui/associated-types/issue-64855-2.rs", +"ui/associated-types/issue-65934.rs", +"ui/associated-types/issue-67684.rs", +"ui/associated-types/issue-69398.rs", +"ui/associated-types/issue-71113.rs", +"ui/associated-types/issue-76179.rs", +"ui/associated-types/issue-82079.rs", +"ui/associated-types/issue-88856.rs", +"ui/associated-types/issue-91069.rs", +"ui/associated-types/issue-91231.rs", +"ui/associated-types/issue-91234.rs", +"ui/async-await/auxiliary/issue-107036.rs", +"ui/async-await/auxiliary/issue-72470-lib.rs", +"ui/async-await/in-trait/issue-102138.rs", +"ui/async-await/in-trait/issue-102219.rs", +"ui/async-await/in-trait/issue-102310.rs", +"ui/async-await/in-trait/issue-104678.rs", +"ui/async-await/issues/auxiliary/issue-60674.rs", +"ui/async-await/issues/auxiliary/issue_67893.rs", +"ui/async-await/issues/issue-65419/issue-65419-async-fn-resume-after-completion.rs", +"ui/async-await/issues/issue-65419/issue-65419-async-fn-resume-after-panic.rs", +"ui/async-await/issues/issue-65419/issue-65419-generator-resume-after-completion.rs", +"ui/async-await/issues/issue-102206.rs", +"ui/async-await/issues/issue-107280.rs", +"ui/async-await/issues/issue-112225-1.rs", +"ui/async-await/issues/issue-112225-2.rs", +"ui/async-await/issues/issue-51719.rs", +"ui/async-await/issues/issue-51751.rs", +"ui/async-await/issues/issue-53249.rs", +"ui/async-await/issues/issue-54752-async-block.rs", +"ui/async-await/issues/issue-54974.rs", +"ui/async-await/issues/issue-55324.rs", +"ui/async-await/issues/issue-55809.rs", +"ui/async-await/issues/issue-58885.rs", +"ui/async-await/issues/issue-59001.rs", +"ui/async-await/issues/issue-59972.rs", +"ui/async-await/issues/issue-60518.rs", +"ui/async-await/issues/issue-60655-latebound-regions.rs", +"ui/async-await/issues/issue-60674.rs", +"ui/async-await/issues/issue-61187.rs", +"ui/async-await/issues/issue-61986.rs", +"ui/async-await/issues/issue-62009-1.rs", +"ui/async-await/issues/issue-62009-2.rs", +"ui/async-await/issues/issue-62097.rs", +"ui/async-await/issues/issue-62517-1.rs", +"ui/async-await/issues/issue-62517-2.rs", +"ui/async-await/issues/issue-63388-1.rs", +"ui/async-await/issues/issue-63388-2.rs", +"ui/async-await/issues/issue-63388-3.rs", +"ui/async-await/issues/issue-63388-4.rs", +"ui/async-await/issues/issue-64391-2.rs", +"ui/async-await/issues/issue-64433.rs", +"ui/async-await/issues/issue-64477-2.rs", +"ui/async-await/issues/issue-64477.rs", +"ui/async-await/issues/issue-64964.rs", +"ui/async-await/issues/issue-65159.rs", +"ui/async-await/issues/issue-65436-raw-ptr-not-send.rs", +"ui/async-await/issues/issue-66695-static-refs.rs", +"ui/async-await/issues/issue-66958-non-copy-infered-type-arg.rs", +"ui/async-await/issues/issue-67611-static-mut-refs.rs", +"ui/async-await/issues/issue-67893.rs", +"ui/async-await/issues/issue-69307-nested.rs", +"ui/async-await/issues/issue-69307.rs", +"ui/async-await/issues/issue-72312.rs", +"ui/async-await/issues/issue-78600.rs", +"ui/async-await/issues/issue-78654.rs", +"ui/async-await/issues/issue-78938-async-block.rs", +"ui/async-await/issues/issue-95307.rs", +"ui/async-await/return-type-notation/issue-110963-early.rs", +"ui/async-await/return-type-notation/issue-110963-late.rs", +"ui/async-await/track-caller/issue-105134.rs", +"ui/async-await/issue-73541-3.rs", +"ui/async-await/issue-73541.rs", +"ui/async-await/issue-101715.rs", +"ui/async-await/issue-105501.rs", +"ui/async-await/issue-107036.rs", +"ui/async-await/issue-108572.rs", +"ui/async-await/issue-54239-private-type-triggers-lint.rs", +"ui/async-await/issue-60709.rs", +"ui/async-await/issue-61076.rs", +"ui/async-await/issue-61452.rs", +"ui/async-await/issue-61793.rs", +"ui/async-await/issue-61949-self-return-type.rs", +"ui/async-await/issue-62658.rs", +"ui/async-await/issue-63832-await-short-temporary-lifetime-1.rs", +"ui/async-await/issue-63832-await-short-temporary-lifetime.rs", +"ui/async-await/issue-64130-1-sync.rs", +"ui/async-await/issue-64130-2-send.rs", +"ui/async-await/issue-64130-3-other.rs", +"ui/async-await/issue-64130-4-async-move.rs", +"ui/async-await/issue-64130-non-send-future-diags.rs", +"ui/async-await/issue-64391.rs", +"ui/async-await/issue-66312.rs", +"ui/async-await/issue-66387-if-without-else.rs", +"ui/async-await/issue-67252-unnamed-future.rs", +"ui/async-await/issue-67651.rs", +"ui/async-await/issue-67765-async-diagnostic.rs", +"ui/async-await/issue-68112.rs", +"ui/async-await/issue-68523-start.rs", +"ui/async-await/issue-68523.rs", +"ui/async-await/issue-69446-fnmut-capture.rs", +"ui/async-await/issue-70594.rs", +"ui/async-await/issue-70818.rs", +"ui/async-await/issue-71137.rs", +"ui/async-await/issue-72442.rs", +"ui/async-await/issue-72470-llvm-dominate.rs", +"ui/async-await/issue-72590-type-error-sized.rs", +"ui/async-await/issue-73050.rs", +"ui/async-await/issue-73137.rs", +"ui/async-await/issue-73541-1.rs", +"ui/async-await/issue-73541-2.rs", +"ui/async-await/issue-73741-type-err-drop-tracking.rs", +"ui/async-await/issue-73741-type-err.rs", +"ui/async-await/issue-74047.rs", +"ui/async-await/issue-74072-lifetime-name-annotations.rs", +"ui/async-await/issue-74497-lifetime-in-opaque.rs", +"ui/async-await/issue-75785-confusing-named-region.rs", +"ui/async-await/issue-76547.rs", +"ui/async-await/issue-77993-2.rs", +"ui/async-await/issue-78115.rs", +"ui/async-await/issue-84841.rs", +"ui/async-await/issue-86507.rs", +"ui/async-await/issue-93197.rs", +"ui/async-await/issue-93648.rs", +"ui/async-await/issue-98634.rs", +"ui/async-await/issue-70935-complex-spans.rs", +"ui/attributes/issue-100631.rs", +"ui/attributes/issue-105594-invalid-attr-validation.rs", +"ui/attributes/issue-90873.rs", +"ui/attributes/issue-40962.rs", +"ui/auto-traits/issue-23080.rs", +"ui/auto-traits/issue-84075.rs", +"ui/auto-traits/issue-23080-2.rs", +"ui/auxiliary/issue-13560-1.rs", +"ui/auxiliary/issue-13560-2.rs", +"ui/auxiliary/issue-13560-3.rs", +"ui/auxiliary/issue-16822.rs", +"ui/auxiliary/issue-18502.rs", +"ui/auxiliary/issue-24106.rs", +"ui/auxiliary/issue-76387.rs", +"ui/bench/issue-32062.rs", +"ui/binding/issue-53114-borrow-checks.rs", +"ui/binding/issue-53114-safety-checks.rs", +"ui/binop/issue-25916.rs", +"ui/binop/issue-28837.rs", +"ui/binop/issue-3820.rs", +"ui/binop/issue-77910-1.rs", +"ui/binop/issue-77910-2.rs", +"ui/binop/issue-93927.rs", +"ui/block-result/issue-11714.rs", +"ui/block-result/issue-13428.rs", +"ui/block-result/issue-13624.rs", +"ui/block-result/issue-20862.rs", +"ui/block-result/issue-22645.rs", +"ui/block-result/issue-3563.rs", +"ui/block-result/issue-5500.rs", +"ui/borrowck/issue-85765-closure.rs", +"ui/borrowck/issue-101119.rs", +"ui/borrowck/issue-102209.rs", +"ui/borrowck/issue-17545.rs", +"ui/borrowck/issue-17718-static-move.rs", +"ui/borrowck/issue-20801.rs", +"ui/borrowck/issue-23338-params-outlive-temps-of-body.rs", +"ui/borrowck/issue-24267-flow-exit.rs", +"ui/borrowck/issue-25793.rs", +"ui/borrowck/issue-29166.rs", +"ui/borrowck/issue-31287-drop-in-guard.rs", +"ui/borrowck/issue-33819.rs", +"ui/borrowck/issue-41962.rs", +"ui/borrowck/issue-42344.rs", +"ui/borrowck/issue-45983.rs", +"ui/borrowck/issue-46095.rs", +"ui/borrowck/issue-46471.rs", +"ui/borrowck/issue-47215-ice-from-drop-elab.rs", +"ui/borrowck/issue-47646.rs", +"ui/borrowck/issue-51117.rs", +"ui/borrowck/issue-51301.rs", +"ui/borrowck/issue-51348-multi-ref-mut-in-guard.rs", +"ui/borrowck/issue-52713-bug.rs", +"ui/borrowck/issue-53432-nested-closure-outlives-borrowed-value.rs", +"ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.rs", +"ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.rs", +"ui/borrowck/issue-54499-field-mutation-of-moved-out.rs", +"ui/borrowck/issue-54499-field-mutation-of-never-init.rs", +"ui/borrowck/issue-54597-reject-move-out-of-borrow-via-pat.rs", +"ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs", +"ui/borrowck/issue-58776-borrowck-scans-children.rs", +"ui/borrowck/issue-62007-assign-box.rs", +"ui/borrowck/issue-62007-assign-field.rs", +"ui/borrowck/issue-62107-match-arm-scopes.rs", +"ui/borrowck/issue-64453.rs", +"ui/borrowck/issue-69789-iterator-mut-suggestion.rs", +"ui/borrowck/issue-7573.rs", +"ui/borrowck/issue-81365-1.rs", +"ui/borrowck/issue-81365-10.rs", +"ui/borrowck/issue-81365-11.rs", +"ui/borrowck/issue-81365-2.rs", +"ui/borrowck/issue-81365-3.rs", +"ui/borrowck/issue-81365-4.rs", +"ui/borrowck/issue-81365-5.rs", +"ui/borrowck/issue-81365-6.rs", +"ui/borrowck/issue-81365-7.rs", +"ui/borrowck/issue-81365-8.rs", +"ui/borrowck/issue-81365-9.rs", +"ui/borrowck/issue-81899.rs", +"ui/borrowck/issue-82032.rs", +"ui/borrowck/issue-82462.rs", +"ui/borrowck/issue-83309-ice-immut-in-for-loop.rs", +"ui/borrowck/issue-83760.rs", +"ui/borrowck/issue-85581.rs", +"ui/borrowck/issue-85765.rs", +"ui/borrowck/issue-87456-point-to-closure.rs", +"ui/borrowck/issue-88434-minimal-example.rs", +"ui/borrowck/issue-88434-removal-index-should-be-less.rs", +"ui/borrowck/issue-91206.rs", +"ui/borrowck/issue-92015.rs", +"ui/borrowck/issue-92157.rs", +"ui/borrowck/issue-93078.rs", +"ui/borrowck/issue-111554.rs", +"ui/borrowck/issue-45199.rs", +"ui/borrowck/issue-103095.rs", +"ui/borrowck/issue-103250.rs", +"ui/borrowck/issue-103624.rs", +"ui/borrowck/issue-104639-lifetime-order.rs", +"ui/borrowck/issue-10876.rs", +"ui/borrowck/issue-109271-pass-self-into-closure.rs", +"ui/borrowck/issue-11493.rs", +"ui/borrowck/issue-17263.rs", +"ui/borrowck/issue-28934.rs", +"ui/borrowck/issue-36082.rs", +"ui/borrowck/issue-51415.rs", +"ui/borrowck/issue-52967-edition-2018-needs-two-phase-borrows.rs", +"ui/borrowck/issue-55552-ascribe-wildcard-to-structured-pattern.rs", +"ui/borrowck/issue-70919-drop-in-loop.rs", +"ui/borrowck/issue-71546.rs", +"ui/borrowck/issue-80772.rs", +"ui/borrowck/issue-82126-mismatched-subst-and-hir.rs", +"ui/borrowck/issue-83924.rs", +"ui/borrowck/issue-93093.rs", +"ui/borrowck/issue-95079-missing-move-in-nested-closure.rs", +"ui/box/issue-82446.rs", +"ui/box/issue-95036.rs", +"ui/c-variadic/issue-32201.rs", +"ui/c-variadic/issue-86053-2.rs", +"ui/c-variadic/issue-86053-1.rs", +"ui/cast/issue-106883-is-empty.rs", +"ui/cast/issue-10991.rs", +"ui/cast/issue-17444.rs", +"ui/cast/issue-85586.rs", +"ui/cast/issue-88621.rs", +"ui/cast/issue-84213.rs", +"ui/cast/issue-89497.rs", +"ui/closure-expected-type/issue-24421.rs", +"ui/closure_context/issue-26046-fn-mut.rs", +"ui/closure_context/issue-26046-fn-once.rs", +"ui/closure_context/issue-42065.rs", +"ui/closures/2229_closure_analysis/match/issue-87097.rs", +"ui/closures/2229_closure_analysis/match/issue-87426.rs", +"ui/closures/2229_closure_analysis/match/issue-87988.rs", +"ui/closures/2229_closure_analysis/match/issue-88331.rs", +"ui/closures/2229_closure_analysis/migrations/issue-78720.rs", +"ui/closures/2229_closure_analysis/migrations/issue-86753.rs", +"ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs", +"ui/closures/2229_closure_analysis/run_pass/issue-87378.rs", +"ui/closures/2229_closure_analysis/run_pass/issue-88372.rs", +"ui/closures/2229_closure_analysis/run_pass/issue-88431.rs", +"ui/closures/2229_closure_analysis/run_pass/issue-88476.rs", +"ui/closures/2229_closure_analysis/issue-87378.rs", +"ui/closures/2229_closure_analysis/issue-87987.rs", +"ui/closures/2229_closure_analysis/issue-88118-2.rs", +"ui/closures/2229_closure_analysis/issue-88476.rs", +"ui/closures/2229_closure_analysis/issue-89606.rs", +"ui/closures/2229_closure_analysis/issue-90465.rs", +"ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs", +"ui/closures/2229_closure_analysis/issue_88118.rs", +"ui/closures/issue-10398.rs", +"ui/closures/issue-109188.rs", +"ui/closures/issue-42463.rs", +"ui/closures/issue-52437.rs", +"ui/closures/issue-67123.rs", +"ui/closures/issue-6801.rs", +"ui/closures/issue-78720.rs", +"ui/closures/issue-80313-mutable-borrow-in-closure.rs", +"ui/closures/issue-80313-mutable-borrow-in-move-closure.rs", +"ui/closures/issue-80313-mutation-in-closure.rs", +"ui/closures/issue-80313-mutation-in-move-closure.rs", +"ui/closures/issue-81700-mut-borrow.rs", +"ui/closures/issue-82438-mut-without-upvar.rs", +"ui/closures/issue-84044-drop-non-mut.rs", +"ui/closures/issue-84128.rs", +"ui/closures/issue-868.rs", +"ui/closures/issue-90871.rs", +"ui/closures/issue-99565.rs", +"ui/closures/issue-111932.rs", +"ui/closures/issue-72408-nested-closures-exponential.rs", +"ui/closures/issue-101696.rs", +"ui/closures/issue-102089-multiple-opaque-cast.rs", +"ui/closures/issue-23012-supertrait-signature-inference.rs", +"ui/closures/issue-41366.rs", +"ui/closures/issue-46742.rs", +"ui/closures/issue-48109.rs", +"ui/closures/issue-68025.rs", +"ui/closures/issue-87461.rs", +"ui/closures/issue-87814-1.rs", +"ui/closures/issue-87814-2.rs", +"ui/closures/issue-97607.rs", +"ui/closures/issue-113087.rs", +"ui/cmse-nonsecure/cmse-nonsecure-entry/issue-83475.rs", +"ui/codegen/auxiliary/issue-97708-aux.rs", +"ui/codegen/issue-101585-128bit-repeat.rs", +"ui/codegen/issue-16602-1.rs", +"ui/codegen/issue-16602-2.rs", +"ui/codegen/issue-16602-3.rs", +"ui/codegen/issue-55976.rs", +"ui/codegen/issue-64401.rs", +"ui/codegen/issue-97708.rs", +"ui/codegen/issue-99551.rs", +"ui/codegen/issue-28950.rs", +"ui/codegen/issue-63787.rs", +"ui/codegen/issue-82859-slice-miscompile.rs", +"ui/codegen/issue-88043-bb-does-not-have-terminator.rs", +"ui/codemap_tests/issue-11715.rs", +"ui/codemap_tests/issue-28308.rs", +"ui/coercion/auxiliary/issue-39823.rs", +"ui/coercion/issue-14589.rs", +"ui/coercion/issue-39823.rs", +"ui/coercion/issue-53475.rs", +"ui/coercion/issue-73886.rs", +"ui/coercion/issue-3794.rs", +"ui/coercion/issue-101066.rs", +"ui/coercion/issue-36007.rs", +"ui/coercion/issue-37655.rs", +"ui/coercion/issue-88097.rs", +"ui/coherence/issue-85026.rs", +"ui/coherence/issue-99663-2.rs", +"ui/coherence/issue-99663.rs", +"ui/command/issue-10626.rs", +"ui/compare-method/issue-90444.rs", +"ui/conditional-compilation/issue-34028.rs", +"ui/confuse-field-and-method/issue-18343.rs", +"ui/confuse-field-and-method/issue-2392.rs", +"ui/confuse-field-and-method/issue-32128.rs", +"ui/confuse-field-and-method/issue-33784.rs", +"ui/const-generics/generic_arg_infer/issue-91614.rs", +"ui/const-generics/generic_const_exprs/auxiliary/issue-94287-aux.rs", +"ui/const-generics/generic_const_exprs/issue-100217.rs", +"ui/const-generics/generic_const_exprs/issue-102768.rs", +"ui/const-generics/generic_const_exprs/issue-105257.rs", +"ui/const-generics/generic_const_exprs/issue-105608.rs", +"ui/const-generics/generic_const_exprs/issue-69654.rs", +"ui/const-generics/generic_const_exprs/issue-73298.rs", +"ui/const-generics/generic_const_exprs/issue-73899.rs", +"ui/const-generics/generic_const_exprs/issue-74713.rs", +"ui/const-generics/generic_const_exprs/issue-76595.rs", +"ui/const-generics/generic_const_exprs/issue-79518-default_trait_method_normalization.rs", +"ui/const-generics/generic_const_exprs/issue-80742.rs", +"ui/const-generics/generic_const_exprs/issue-82268.rs", +"ui/const-generics/generic_const_exprs/issue-83765.rs", +"ui/const-generics/generic_const_exprs/issue-83972.rs", +"ui/const-generics/generic_const_exprs/issue-84669.rs", +"ui/const-generics/generic_const_exprs/issue-85848.rs", +"ui/const-generics/generic_const_exprs/issue-94287.rs", +"ui/const-generics/generic_const_exprs/issue-86710.rs", +"ui/const-generics/generic_const_exprs/issue-100360.rs", +"ui/const-generics/generic_const_exprs/issue-102074.rs", +"ui/const-generics/generic_const_exprs/issue-62504.rs", +"ui/const-generics/generic_const_exprs/issue-72787.rs", +"ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.rs", +"ui/const-generics/generic_const_exprs/issue-74634.rs", +"ui/const-generics/generic_const_exprs/issue-80561-incorrect-param-env.rs", +"ui/const-generics/generic_const_exprs/issue-84408.rs", +"ui/const-generics/generic_const_exprs/issue-89851.rs", +"ui/const-generics/generic_const_exprs/issue-90847.rs", +"ui/const-generics/generic_const_exprs/issue-94293.rs", +"ui/const-generics/generic_const_exprs/issue-97047-ice-1.rs", +"ui/const-generics/generic_const_exprs/issue-97047-ice-2.rs", +"ui/const-generics/generic_const_exprs/issue-99647.rs", +"ui/const-generics/generic_const_exprs/issue-99705.rs", +"ui/const-generics/generic_const_exprs/issue-109141.rs", +"ui/const-generics/generic_const_exprs/issue-96699.rs", +"ui/const-generics/infer/issue-77092.rs", +"ui/const-generics/issues/issue-105037.rs", +"ui/const-generics/issues/issue-56445-2.rs", +"ui/const-generics/issues/issue-56445-3.rs", +"ui/const-generics/issues/issue-61336-1.rs", +"ui/const-generics/issues/issue-61336-2.rs", +"ui/const-generics/issues/issue-61336.rs", +"ui/const-generics/issues/issue-61432.rs", +"ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs", +"ui/const-generics/issues/issue-67185-2.rs", +"ui/const-generics/issues/issue-68104-print-stack-overflow.rs", +"ui/const-generics/issues/issue-69654-run-pass.rs", +"ui/const-generics/issues/issue-70125-1.rs", +"ui/const-generics/issues/issue-70125-2.rs", +"ui/const-generics/issues/issue-70180-1-stalled_on.rs", +"ui/const-generics/issues/issue-70180-2-stalled_on.rs", +"ui/const-generics/issues/issue-71202.rs", +"ui/const-generics/issues/issue-72845.rs", +"ui/const-generics/issues/issue-73260.rs", +"ui/const-generics/issues/issue-76701-ty-param-in-const.rs", +"ui/const-generics/issues/issue-79674.rs", +"ui/const-generics/issues/issue-80062.rs", +"ui/const-generics/issues/issue-80375.rs", +"ui/const-generics/issues/issue-82956.rs", +"ui/const-generics/issues/issue-83249.rs", +"ui/const-generics/issues/issue-83288.rs", +"ui/const-generics/issues/issue-83466.rs", +"ui/const-generics/issues/issue-83765.rs", +"ui/const-generics/issues/issue-84659.rs", +"ui/const-generics/issues/issue-86530.rs", +"ui/const-generics/issues/issue-86535-2.rs", +"ui/const-generics/issues/issue-86535.rs", +"ui/const-generics/issues/issue-86820.rs", +"ui/const-generics/issues/issue-87470.rs", +"ui/const-generics/issues/issue-87493.rs", +"ui/const-generics/issues/issue-87964.rs", +"ui/const-generics/issues/issue-88997.rs", +"ui/const-generics/issues/issue-89146.rs", +"ui/const-generics/issues/issue-89320.rs", +"ui/const-generics/issues/issue-89334.rs", +"ui/const-generics/issues/issue-90318.rs", +"ui/const-generics/issues/issue-90364.rs", +"ui/const-generics/issues/issue-90455.rs", +"ui/const-generics/issues/issue-97634.rs", +"ui/const-generics/issues/issue-98629.rs", +"ui/const-generics/issues/issue-100313.rs", +"ui/const-generics/issues/issue-87076.rs", +"ui/const-generics/issues/issue-97278.rs", +"ui/const-generics/issues/issue-99641.rs", +"ui/const-generics/issues/issue-105821.rs", +"ui/const-generics/issues/issue-56445-1.rs", +"ui/const-generics/issues/issue-60818-struct-constructors.rs", +"ui/const-generics/issues/issue-61422.rs", +"ui/const-generics/issues/issue-62878.rs", +"ui/const-generics/issues/issue-63322-forbid-dyn.rs", +"ui/const-generics/issues/issue-64519.rs", +"ui/const-generics/issues/issue-66596-impl-trait-for-str-const-arg.rs", +"ui/const-generics/issues/issue-66906.rs", +"ui/const-generics/issues/issue-67185-1.rs", +"ui/const-generics/issues/issue-67375.rs", +"ui/const-generics/issues/issue-67739.rs", +"ui/const-generics/issues/issue-67945-1.rs", +"ui/const-generics/issues/issue-67945-2.rs", +"ui/const-generics/issues/issue-67945-3.rs", +"ui/const-generics/issues/issue-67945-4.rs", +"ui/const-generics/issues/issue-68366.rs", +"ui/const-generics/issues/issue-68596.rs", +"ui/const-generics/issues/issue-68615-adt.rs", +"ui/const-generics/issues/issue-68615-array.rs", +"ui/const-generics/issues/issue-70167.rs", +"ui/const-generics/issues/issue-70225.rs", +"ui/const-generics/issues/issue-70273-assoc-fn.rs", +"ui/const-generics/issues/issue-71169.rs", +"ui/const-generics/issues/issue-71381.rs", +"ui/const-generics/issues/issue-71382.rs", +"ui/const-generics/issues/issue-71547.rs", +"ui/const-generics/issues/issue-71611.rs", +"ui/const-generics/issues/issue-71986.rs", +"ui/const-generics/issues/issue-72352.rs", +"ui/const-generics/issues/issue-73120.rs", +"ui/const-generics/issues/issue-73491.rs", +"ui/const-generics/issues/issue-73727-static-reference-array-const-param.rs", +"ui/const-generics/issues/issue-74101.rs", +"ui/const-generics/issues/issue-74255.rs", +"ui/const-generics/issues/issue-74906.rs", +"ui/const-generics/issues/issue-74950.rs", +"ui/const-generics/issues/issue-75047.rs", +"ui/const-generics/issues/issue-75299.rs", +"ui/const-generics/issues/issue-85031-2.rs", +"ui/const-generics/issues/issue-86033.rs", +"ui/const-generics/issues/issue-88119.rs", +"ui/const-generics/issues/issue-88468.rs", +"ui/const-generics/issues/issue-89304.rs", +"ui/const-generics/issues/issue-92186.rs", +"ui/const-generics/issues/issue-96654.rs", +"ui/const-generics/parser-error-recovery/issue-89013-no-assoc.rs", +"ui/const-generics/parser-error-recovery/issue-89013-no-kw.rs", +"ui/const-generics/parser-error-recovery/issue-89013-type.rs", +"ui/const-generics/parser-error-recovery/issue-89013.rs", +"ui/const-generics/type-dependent/issue-61936.rs", +"ui/const-generics/type-dependent/issue-63695.rs", +"ui/const-generics/type-dependent/issue-69816.rs", +"ui/const-generics/type-dependent/issue-70507.rs", +"ui/const-generics/type-dependent/issue-71382.rs", +"ui/const-generics/type-dependent/issue-71805.rs", +"ui/const-generics/type-dependent/issue-67144-1.rs", +"ui/const-generics/type-dependent/issue-67144-2.rs", +"ui/const-generics/type-dependent/issue-70217.rs", +"ui/const-generics/type-dependent/issue-70586.rs", +"ui/const-generics/type-dependent/issue-71348.rs", +"ui/const-generics/type-dependent/issue-73730.rs", +"ui/const-generics/issue-46511.rs", +"ui/const-generics/issue-70408.rs", +"ui/const-generics/issue-93647.rs", +"ui/const-generics/issue-112505-overflow.rs", +"ui/const-generics/issue-66451.rs", +"ui/const-generics/issue-80471.rs", +"ui/const-generics/issue-102124.rs", +"ui/const-generics/issue-105689.rs", +"ui/const-generics/issue-106419-struct-with-multiple-const-params.rs", +"ui/const-generics/issue-97007.rs", +"ui/const_prop/issue-102553.rs", +"ui/const_prop/issue-86351.rs", +"ui/consts/auxiliary/issue-17718-aux.rs", +"ui/consts/auxiliary/issue-63226.rs", +"ui/consts/const-eval/issue-100878.rs", +"ui/consts/const-eval/issue-104390.rs", +"ui/consts/const-eval/issue-43197.rs", +"ui/consts/const-eval/issue-44578.rs", +"ui/consts/const-eval/issue-49296.rs", +"ui/consts/const-eval/issue-50814-2.rs", +"ui/consts/const-eval/issue-50814.rs", +"ui/consts/const-eval/issue-64908.rs", +"ui/consts/const-eval/issue-64970.rs", +"ui/consts/const-eval/issue-65394.rs", +"ui/consts/const-eval/issue-84957-const-str-as-bytes.rs", +"ui/consts/const-eval/issue-85155.rs", +"ui/consts/const-eval/issue-85907.rs", +"ui/consts/const-eval/issue-91827-extern-types.rs", +"ui/consts/const-eval/issue-52475.rs", +"ui/consts/const-eval/issue-70723.rs", +"ui/consts/const-eval/issue-47971.rs", +"ui/consts/const-eval/issue-50706.rs", +"ui/consts/const-eval/issue-51300.rs", +"ui/consts/const-eval/issue-53157.rs", +"ui/consts/const-eval/issue-53401.rs", +"ui/consts/const-eval/issue-55541.rs", +"ui/consts/const-eval/issue-70804-fn-subtyping.rs", +"ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier-2.rs", +"ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs", +"ui/consts/const-mut-refs/issue-76510.rs", +"ui/consts/const_in_pattern/issue-44333.rs", +"ui/consts/const_in_pattern/issue-62614.rs", +"ui/consts/const_in_pattern/issue-78057.rs", +"ui/consts/const_in_pattern/issue-53708.rs", +"ui/consts/const_in_pattern/issue-65466.rs", +"ui/consts/const_in_pattern/issue-73431.rs", +"ui/consts/control-flow/issue-46843.rs", +"ui/consts/control-flow/issue-50577.rs", +"ui/consts/extra-const-ub/issue-100771.rs", +"ui/consts/extra-const-ub/issue-101034.rs", +"ui/consts/issue-102117.rs", +"ui/consts/issue-103790.rs", +"ui/consts/issue-104609.rs", +"ui/consts/issue-104768.rs", +"ui/consts/issue-13902.rs", +"ui/consts/issue-17458.rs", +"ui/consts/issue-17718-borrow-interior.rs", +"ui/consts/issue-17718-const-bad-values.rs", +"ui/consts/issue-17718-const-borrow.rs", +"ui/consts/issue-17718-constants-not-static.rs", +"ui/consts/issue-17718-references.rs", +"ui/consts/issue-17718.rs", +"ui/consts/issue-17756.rs", +"ui/consts/issue-18294.rs", +"ui/consts/issue-19244.rs", +"ui/consts/issue-21562.rs", +"ui/consts/issue-21721.rs", +"ui/consts/issue-23833.rs", +"ui/consts/issue-23968-const-not-overflow.rs", +"ui/consts/issue-25826.rs", +"ui/consts/issue-27890.rs", +"ui/consts/issue-28113.rs", +"ui/consts/issue-29914-2.rs", +"ui/consts/issue-29914-3.rs", +"ui/consts/issue-29914.rs", +"ui/consts/issue-29927-1.rs", +"ui/consts/issue-29927.rs", +"ui/consts/issue-32829-2.rs", +"ui/consts/issue-32829.rs", +"ui/consts/issue-33537.rs", +"ui/consts/issue-34784.rs", +"ui/consts/issue-36163.rs", +"ui/consts/issue-37222.rs", +"ui/consts/issue-37550.rs", +"ui/consts/issue-37991.rs", +"ui/consts/issue-39974.rs", +"ui/consts/issue-43105.rs", +"ui/consts/issue-44415.rs", +"ui/consts/issue-46553.rs", +"ui/consts/issue-50439.rs", +"ui/consts/issue-52023-array-size-pointer-cast.rs", +"ui/consts/issue-52060.rs", +"ui/consts/issue-54224.rs", +"ui/consts/issue-54348.rs", +"ui/consts/issue-54582.rs", +"ui/consts/issue-54954.rs", +"ui/consts/issue-56164.rs", +"ui/consts/issue-58435-ice-with-assoc-const.rs", +"ui/consts/issue-64506.rs", +"ui/consts/issue-64662.rs", +"ui/consts/issue-66693-panic-in-array-len.rs", +"ui/consts/issue-66693.rs", +"ui/consts/issue-68542-closure-in-array-len.rs", +"ui/consts/issue-69191-ice-on-uninhabited-enum-field.rs", +"ui/consts/issue-69310-array-size-lit-wrong-ty.rs", +"ui/consts/issue-69312.rs", +"ui/consts/issue-69488.rs", +"ui/consts/issue-69532.rs", +"ui/consts/issue-70773-mir-typeck-lt-norm.rs", +"ui/consts/issue-70942-trait-vs-impl-mismatch.rs", +"ui/consts/issue-73976-monomorphic.rs", +"ui/consts/issue-73976-polymorphic.rs", +"ui/consts/issue-76064.rs", +"ui/consts/issue-77062-large-zst-array.rs", +"ui/consts/issue-78655.rs", +"ui/consts/issue-79137-toogeneric.rs", +"ui/consts/issue-83182.rs", +"ui/consts/issue-87046.rs", +"ui/consts/issue-90762.rs", +"ui/consts/issue-90878-2.rs", +"ui/consts/issue-90878-3.rs", +"ui/consts/issue-90878.rs", +"ui/consts/issue-91434.rs", +"ui/consts/issue-94675.rs", +"ui/consts/issue-104155.rs", +"ui/consts/issue-104396.rs", +"ui/consts/issue-13837.rs", +"ui/consts/issue-16538.rs", +"ui/consts/issue-28822.rs", +"ui/consts/issue-29798.rs", +"ui/consts/issue-33903.rs", +"ui/consts/issue-3521.rs", +"ui/consts/issue-37550-1.rs", +"ui/consts/issue-39161-bogus-error.rs", +"ui/consts/issue-47789.rs", +"ui/consts/issue-54387.rs", +"ui/consts/issue-62045.rs", +"ui/consts/issue-63226.rs", +"ui/consts/issue-63952.rs", +"ui/consts/issue-64059.rs", +"ui/consts/issue-65348.rs", +"ui/consts/issue-66342.rs", +"ui/consts/issue-66345.rs", +"ui/consts/issue-66397.rs", +"ui/consts/issue-66787.rs", +"ui/consts/issue-67529.rs", +"ui/consts/issue-67640.rs", +"ui/consts/issue-67641.rs", +"ui/consts/issue-67696-const-prop-ice.rs", +"ui/consts/issue-67862.rs", +"ui/consts/issue-68264-overflow.rs", +"ui/consts/issue-68684.rs", +"ui/consts/issue-6991.rs", +"ui/consts/issue-79137-monomorphic.rs", +"ui/consts/issue-79152-const-array-index.rs", +"ui/consts/issue-79690.rs", +"ui/consts/issue-88071.rs", +"ui/consts/issue-88649.rs", +"ui/consts/issue-89088.rs", +"ui/consts/issue-90870.rs", +"ui/consts/issue-91560.rs", +"ui/consts/issue-94371.rs", +"ui/consts/issue-96169.rs", +"ui/consts/issue-17074.rs", +"ui/cross-crate/issue-64872/issue-64872.rs", +"ui/cycle-trait/issue-12511.rs", +"ui/debuginfo/issue-105386-debuginfo-ub.rs", +"ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs", +"ui/deprecation/issue-84637-deprecated-associated-function.rs", +"ui/derived-errors/issue-30580.rs", +"ui/derived-errors/issue-31997-1.rs", +"ui/derived-errors/issue-31997.rs", +"ui/derives/issue-36617.rs", +"ui/derives/issue-43023.rs", +"ui/derives/issue-91492.rs", +"ui/derives/issue-91550.rs", +"ui/derives/issue-97343.rs", +"ui/deriving/issue-103157.rs", +"ui/deriving/issue-15689-1.rs", +"ui/deriving/issue-19358.rs", +"ui/deriving/issue-3935.rs", +"ui/deriving/issue-58319.rs", +"ui/deriving/issue-105101.rs", +"ui/deriving/issue-15689-2.rs", +"ui/deriving/issue-6341.rs", +"ui/deriving/issue-89188-gat-hrtb.rs", +"ui/did_you_mean/issue-103909.rs", +"ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.rs", +"ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.rs", +"ui/did_you_mean/issue-31424.rs", +"ui/did_you_mean/issue-34126.rs", +"ui/did_you_mean/issue-34337.rs", +"ui/did_you_mean/issue-35937.rs", +"ui/did_you_mean/issue-36798.rs", +"ui/did_you_mean/issue-36798_unknown_field.rs", +"ui/did_you_mean/issue-37139.rs", +"ui/did_you_mean/issue-38054-do-not-show-unresolved-names.rs", +"ui/did_you_mean/issue-38147-1.rs", +"ui/did_you_mean/issue-38147-2.rs", +"ui/did_you_mean/issue-38147-3.rs", +"ui/did_you_mean/issue-38147-4.rs", +"ui/did_you_mean/issue-39544.rs", +"ui/did_you_mean/issue-39802-show-5-trait-impls.rs", +"ui/did_you_mean/issue-40006.rs", +"ui/did_you_mean/issue-40396.rs", +"ui/did_you_mean/issue-40823.rs", +"ui/did_you_mean/issue-42599_available_fields_note.rs", +"ui/did_you_mean/issue-42764.rs", +"ui/did_you_mean/issue-43871-enum-instead-of-variant.rs", +"ui/did_you_mean/issue-46718-struct-pattern-dotdotdot.rs", +"ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.rs", +"ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.rs", +"ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.rs", +"ui/did_you_mean/issue-53280-expected-float-found-integer-literal.rs", +"ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs", +"ui/did_you_mean/issue-56028-there-is-an-enum-variant.rs", +"ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs", +"ui/did_you_mean/issue-93210-ignore-doc-hidden.rs", +"ui/did_you_mean/issue-41679-tilde-bitwise-negation-attempt.rs", +"ui/did_you_mean/issue-54109-without-witness.rs", +"ui/drop/auxiliary/issue-10028.rs", +"ui/drop/issue-10028.rs", +"ui/drop/issue-21486.rs", +"ui/drop/issue-23338-ensure-param-drop-order.rs", +"ui/drop/issue-2734.rs", +"ui/drop/issue-2735-2.rs", +"ui/drop/issue-2735-3.rs", +"ui/drop/issue-2735.rs", +"ui/drop/issue-30018-nopanic.rs", +"ui/drop/issue-35546.rs", +"ui/drop/issue-48962.rs", +"ui/drop/issue-90752-raw-ptr-shenanigans.rs", +"ui/drop/issue-90752.rs", +"ui/drop/issue-979.rs", +"ui/drop/issue-100276.rs", +"ui/drop/issue-103107.rs", +"ui/drop/issue-110682.rs", +"ui/drop/issue-17718-const-destructors.rs", +"ui/dropck/issue-24805-dropck-itemless.rs", +"ui/dropck/issue-28498-ugeh-with-lifetime-param.rs", +"ui/dropck/issue-28498-ugeh-with-passed-to-fn.rs", +"ui/dropck/issue-28498-ugeh-with-trait-bound.rs", +"ui/dropck/issue-29844.rs", +"ui/dropck/issue-34053.rs", +"ui/dropck/issue-38868.rs", +"ui/dropck/issue-54943-1.rs", +"ui/dropck/issue-54943-2.rs", +"ui/dst/issue-90528-unsizing-suggestion-1.rs", +"ui/dst/issue-90528-unsizing-suggestion-2.rs", +"ui/dst/issue-90528-unsizing-suggestion-3.rs", +"ui/dst/issue-90528-unsizing-suggestion-4.rs", +"ui/dyn-keyword/issue-56327-dyn-trait-in-macro-is-okay.rs", +"ui/dyn-star/issue-102430.rs", +"ui/empty/issue-37026.rs", +"ui/enum-discriminant/auxiliary/issue-41394.rs", +"ui/enum-discriminant/issue-104519.rs", +"ui/enum-discriminant/issue-41394-rpass.rs", +"ui/enum-discriminant/issue-41394.rs", +"ui/enum-discriminant/issue-43398.rs", +"ui/enum-discriminant/issue-51582.rs", +"ui/enum-discriminant/issue-61696.rs", +"ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs", +"ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs", +"ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs", +"ui/enum-discriminant/issue-70509-partial_eq.rs", +"ui/enum-discriminant/issue-72554.rs", +"ui/enum-discriminant/issue-90038.rs", +"ui/enum-discriminant/issue-50689.rs", +"ui/enum-discriminant/issue-46519.rs", +"ui/enum/issue-42747.rs", +"ui/enum/issue-67945-1.rs", +"ui/enum/issue-67945-2.rs", +"ui/enum/issue-1821.rs", +"ui/error-codes/e0119/auxiliary/issue-23563-a.rs", +"ui/error-codes/e0119/issue-23563.rs", +"ui/error-codes/e0119/issue-27403.rs", +"ui/error-codes/e0119/issue-28981.rs", +"ui/errors/issue-99572-impl-trait-on-pointer.rs", +"ui/errors/issue-104621-extern-bad-file.rs", +"ui/errors/issue-104621-extern-not-file.rs", +"ui/errors/issue-89280-emitter-overflow-splice-lines.rs", +"ui/expr/if/issue-4201.rs", +"ui/extenv/issue-55897.rs", +"ui/extenv/issue-110547.rs", +"ui/extern/auxiliary/issue-80074-macro.rs", +"ui/extern/issue-10025.rs", +"ui/extern/issue-10763.rs", +"ui/extern/issue-10764-rpass.rs", +"ui/extern/issue-13655.rs", +"ui/extern/issue-36122-accessing-externed-dst.rs", +"ui/extern/issue-112363-extern-item-where-clauses-debug-ice.rs", +"ui/extern/issue-1251.rs", +"ui/extern/issue-28324.rs", +"ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs", +"ui/extern/issue-64655-extern-rust-must-allow-unwind.rs", +"ui/extern/issue-80074.rs", +"ui/extern/issue-95829.rs", +"ui/feature-gates/issue-43106-gating-of-bench.rs", +"ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs", +"ui/feature-gates/issue-43106-gating-of-derive-2.rs", +"ui/feature-gates/issue-43106-gating-of-derive.rs", +"ui/feature-gates/issue-43106-gating-of-macro_use.rs", +"ui/feature-gates/issue-43106-gating-of-proc_macro_derive.rs", +"ui/feature-gates/issue-43106-gating-of-stable.rs", +"ui/feature-gates/issue-43106-gating-of-test.rs", +"ui/feature-gates/issue-43106-gating-of-unstable.rs", +"ui/feature-gates/issue-49983-see-issue-0.rs", +"ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs", +"ui/feature-gates/issue-43106-gating-of-deprecated.rs", +"ui/feature-gates/issue-43106-gating-of-macro_escape.rs", +"ui/fmt/issue-103826.rs", +"ui/fmt/issue-104142.rs", +"ui/fmt/issue-75307.rs", +"ui/fmt/issue-86085.rs", +"ui/fmt/issue-89173.rs", +"ui/fmt/issue-91556.rs", +"ui/fn/issue-3044.rs", +"ui/fn/issue-3099.rs", +"ui/fn/issue-3904.rs", +"ui/fn/issue-39259.rs", +"ui/fn/issue-80179.rs", +"ui/for-loop-while/issue-2216.rs", +"ui/for-loop-while/issue-51345.rs", +"ui/for-loop-while/issue-69841.rs", +"ui/for-loop-while/issue-1257.rs", +"ui/for/issue-20605.rs", +"ui/foreign/issue-91370-foreign-fn-block-impl.rs", +"ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs", +"ui/foreign/issue-99276-same-type-lifetimes.rs", +"ui/function-pointer/issue-102289.rs", +"ui/functions-closures/closure-expected-type/issue-38714.rs", +"ui/generator/issue-113279.rs", +"ui/generator/issue-44197.rs", +"ui/generator/issue-48048.rs", +"ui/generator/issue-52398.rs", +"ui/generator/issue-64620-yield-array-element.rs", +"ui/generator/issue-69039.rs", +"ui/generator/issue-88653.rs", +"ui/generator/issue-91477.rs", +"ui/generator/issue-102645.rs", +"ui/generator/issue-105084.rs", +"ui/generator/issue-110929-generator-conflict-error-ice.rs", +"ui/generator/issue-45729-unsafe-in-generator.rs", +"ui/generator/issue-52304.rs", +"ui/generator/issue-53548-1.rs", +"ui/generator/issue-53548.rs", +"ui/generator/issue-57017.rs", +"ui/generator/issue-57084.rs", +"ui/generator/issue-57478.rs", +"ui/generator/issue-58888.rs", +"ui/generator/issue-61442-stmt-expr-with-drop.rs", +"ui/generator/issue-62506-two_awaits.rs", +"ui/generator/issue-68112.rs", +"ui/generator/issue-69017.rs", +"ui/generator/issue-87142.rs", +"ui/generator/issue-93161.rs", +"ui/generic-associated-types/bugs/issue-87735.rs", +"ui/generic-associated-types/bugs/issue-87755.rs", +"ui/generic-associated-types/bugs/issue-87803.rs", +"ui/generic-associated-types/bugs/issue-88382.rs", +"ui/generic-associated-types/bugs/issue-88460.rs", +"ui/generic-associated-types/bugs/issue-88526.rs", +"ui/generic-associated-types/bugs/issue-91762.rs", +"ui/generic-associated-types/bugs/issue-100013.rs", +"ui/generic-associated-types/bugs/issue-80626.rs", +"ui/generic-associated-types/issue-101020.rs", +"ui/generic-associated-types/issue-102114.rs", +"ui/generic-associated-types/issue-102335-gat.rs", +"ui/generic-associated-types/issue-47206-where-clause.rs", +"ui/generic-associated-types/issue-67510.rs", +"ui/generic-associated-types/issue-68641-check-gat-bounds.rs", +"ui/generic-associated-types/issue-68642-broken-llvm-ir.rs", +"ui/generic-associated-types/issue-68643-broken-mir.rs", +"ui/generic-associated-types/issue-68644-codegen-selection.rs", +"ui/generic-associated-types/issue-68645-codegen-fulfillment.rs", +"ui/generic-associated-types/issue-68648-2.rs", +"ui/generic-associated-types/issue-68656-unsized-values.rs", +"ui/generic-associated-types/issue-70304.rs", +"ui/generic-associated-types/issue-71176.rs", +"ui/generic-associated-types/issue-74684-1.rs", +"ui/generic-associated-types/issue-74684-2.rs", +"ui/generic-associated-types/issue-74816.rs", +"ui/generic-associated-types/issue-74824.rs", +"ui/generic-associated-types/issue-76826.rs", +"ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs", +"ui/generic-associated-types/issue-79636-1.rs", +"ui/generic-associated-types/issue-79636-2.rs", +"ui/generic-associated-types/issue-80433.rs", +"ui/generic-associated-types/issue-81487.rs", +"ui/generic-associated-types/issue-81712-cyclic-traits.rs", +"ui/generic-associated-types/issue-81862.rs", +"ui/generic-associated-types/issue-84931.rs", +"ui/generic-associated-types/issue-86787.rs", +"ui/generic-associated-types/issue-87258_a.rs", +"ui/generic-associated-types/issue-87258_b.rs", +"ui/generic-associated-types/issue-87429-associated-type-default.rs", +"ui/generic-associated-types/issue-87429-specialization.rs", +"ui/generic-associated-types/issue-91139.rs", +"ui/generic-associated-types/issue-91883.rs", +"ui/generic-associated-types/issue-92033.rs", +"ui/generic-associated-types/issue-95305.rs", +"ui/generic-associated-types/issue-102333.rs", +"ui/generic-associated-types/issue-58694-parameter-out-of-range.rs", +"ui/generic-associated-types/issue-62326-parameter-out-of-range.rs", +"ui/generic-associated-types/issue-67424.rs", +"ui/generic-associated-types/issue-67510-pass.rs", +"ui/generic-associated-types/issue-68648-1.rs", +"ui/generic-associated-types/issue-68649-pass.rs", +"ui/generic-associated-types/issue-68653.rs", +"ui/generic-associated-types/issue-70303.rs", +"ui/generic-associated-types/issue-76407.rs", +"ui/generic-associated-types/issue-76535.rs", +"ui/generic-associated-types/issue-78671.rs", +"ui/generic-associated-types/issue-79422.rs", +"ui/generic-associated-types/issue-80433-reduced.rs", +"ui/generic-associated-types/issue-85921.rs", +"ui/generic-associated-types/issue-86218-2.rs", +"ui/generic-associated-types/issue-86218.rs", +"ui/generic-associated-types/issue-86483.rs", +"ui/generic-associated-types/issue-87429-2.rs", +"ui/generic-associated-types/issue-87429.rs", +"ui/generic-associated-types/issue-87748.rs", +"ui/generic-associated-types/issue-87750.rs", +"ui/generic-associated-types/issue-88287.rs", +"ui/generic-associated-types/issue-88360.rs", +"ui/generic-associated-types/issue-88405.rs", +"ui/generic-associated-types/issue-88459.rs", +"ui/generic-associated-types/issue-89008.rs", +"ui/generic-associated-types/issue-89352.rs", +"ui/generic-associated-types/issue-90014.rs", +"ui/generic-associated-types/issue-90729.rs", +"ui/generic-associated-types/issue-92096.rs", +"ui/generic-associated-types/issue-92280.rs", +"ui/generic-associated-types/issue-92954.rs", +"ui/generic-associated-types/issue-93141.rs", +"ui/generic-associated-types/issue-93262.rs", +"ui/generic-associated-types/issue-93340.rs", +"ui/generic-associated-types/issue-93341.rs", +"ui/generic-associated-types/issue-93342.rs", +"ui/generic-associated-types/issue-93874.rs", +"ui/generic-associated-types/issue-88595.rs", +"ui/generic-associated-types/issue-90014-tait.rs", +"ui/generic-associated-types/issue-90014-tait2.rs", +"ui/generics/issue-106694.rs", +"ui/generics/issue-1112.rs", +"ui/generics/issue-2936.rs", +"ui/generics/issue-32498.rs", +"ui/generics/issue-333.rs", +"ui/generics/issue-59508-1.rs", +"ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.rs", +"ui/generics/issue-61631-default-type-param-cannot-reference-self.rs", +"ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs", +"ui/generics/issue-79605.rs", +"ui/generics/issue-80512-param-reordering-with-defaults.rs", +"ui/generics/issue-94432-garbage-ice.rs", +"ui/generics/issue-98432.rs", +"ui/generics/issue-59508.rs", +"ui/generics/issue-94923.rs", +"ui/generics/issue-95208-ignore-qself.rs", +"ui/generics/issue-95208.rs", +"ui/hygiene/issue-15221.rs", +"ui/hygiene/issue-40847.rs", +"ui/hygiene/issue-77523-def-site-async-await.rs", +"ui/hygiene/issue-32922.rs", +"ui/hygiene/issue-44128.rs", +"ui/hygiene/issue-47311.rs", +"ui/hygiene/issue-47312.rs", +"ui/hygiene/issue-61574-const-parameters.rs", +"ui/impl-trait/explicit-generic-args-with-impl-trait/issue-87718.rs", +"ui/impl-trait/in-trait/issue-102140.rs", +"ui/impl-trait/in-trait/issue-102301.rs", +"ui/impl-trait/in-trait/issue-102571.rs", +"ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.rs", +"ui/impl-trait/issues/issue-54600.rs", +"ui/impl-trait/issues/issue-54840.rs", +"ui/impl-trait/issues/issue-54895.rs", +"ui/impl-trait/issues/issue-57979-deeply-nested-impl-trait-in-assoc-proj.rs", +"ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs", +"ui/impl-trait/issues/issue-57979-nested-impl-trait-in-assoc-proj.rs", +"ui/impl-trait/issues/issue-58504.rs", +"ui/impl-trait/issues/issue-58956.rs", +"ui/impl-trait/issues/issue-62742.rs", +"ui/impl-trait/issues/issue-67830.rs", +"ui/impl-trait/issues/issue-70971.rs", +"ui/impl-trait/issues/issue-79099.rs", +"ui/impl-trait/issues/issue-82139.rs", +"ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs", +"ui/impl-trait/issues/issue-84073.rs", +"ui/impl-trait/issues/issue-84919.rs", +"ui/impl-trait/issues/issue-86642.rs", +"ui/impl-trait/issues/issue-86719.rs", +"ui/impl-trait/issues/issue-87295.rs", +"ui/impl-trait/issues/issue-87340.rs", +"ui/impl-trait/issues/issue-88236-2.rs", +"ui/impl-trait/issues/issue-88236.rs", +"ui/impl-trait/issues/issue-99348-impl-compatibility.rs", +"ui/impl-trait/issues/issue-104815.rs", +"ui/impl-trait/issues/issue-105826.rs", +"ui/impl-trait/issues/issue-42479.rs", +"ui/impl-trait/issues/issue-49376.rs", +"ui/impl-trait/issues/issue-52128.rs", +"ui/impl-trait/issues/issue-53457.rs", +"ui/impl-trait/issues/issue-55608-captures-empty-region.rs", +"ui/impl-trait/issues/issue-57464-unexpected-regions.rs", +"ui/impl-trait/issues/issue-77987.rs", +"ui/impl-trait/issues/issue-83919.rs", +"ui/impl-trait/issues/issue-86201.rs", +"ui/impl-trait/issues/issue-86800.rs", +"ui/impl-trait/issues/issue-89312.rs", +"ui/impl-trait/issues/issue-92305.rs", +"ui/impl-trait/issues/issue-93788.rs", +"ui/impl-trait/issues/issue-65581.rs", +"ui/impl-trait/issues/issue-70877.rs", +"ui/impl-trait/issues/issue-74282.rs", +"ui/impl-trait/issues/issue-78722-2.rs", +"ui/impl-trait/issues/issue-78722.rs", +"ui/impl-trait/issue-100075-2.rs", +"ui/impl-trait/issue-100075.rs", +"ui/impl-trait/issue-35668.rs", +"ui/impl-trait/issue-36792.rs", +"ui/impl-trait/issue-49685.rs", +"ui/impl-trait/issue-51185.rs", +"ui/impl-trait/issue-54966.rs", +"ui/impl-trait/issue-55872-1.rs", +"ui/impl-trait/issue-55872.rs", +"ui/impl-trait/issue-72911.rs", +"ui/impl-trait/issue-86465.rs", +"ui/impl-trait/issue-87450.rs", +"ui/impl-trait/issue-99073-2.rs", +"ui/impl-trait/issue-99073.rs", +"ui/impl-trait/issue-100187.rs", +"ui/impl-trait/issue-102605.rs", +"ui/impl-trait/issue-103181-1.rs", +"ui/impl-trait/issue-103599.rs", +"ui/impl-trait/issue-108591.rs", +"ui/impl-trait/issue-108592.rs", +"ui/impl-trait/issue-46959.rs", +"ui/impl-trait/issue-49556.rs", +"ui/impl-trait/issue-49579.rs", +"ui/impl-trait/issue-55872-2.rs", +"ui/impl-trait/issue-56445.rs", +"ui/impl-trait/issue-68532.rs", +"ui/impl-trait/issue-99642-2.rs", +"ui/impl-trait/issue-99642.rs", +"ui/impl-trait/issue-99914.rs", +"ui/impl-trait/issue-103181-2.rs", +"ui/impl-trait/issue-55872-3.rs", +"ui/implied-bounds/issue-100690.rs", +"ui/implied-bounds/issue-101951.rs", +"ui/implied-bounds/issue-110161.rs", +"ui/imports/auxiliary/issue-36881-aux.rs", +"ui/imports/auxiliary/issue-52891.rs", +"ui/imports/auxiliary/issue-55811.rs", +"ui/imports/auxiliary/issue-56125.rs", +"ui/imports/auxiliary/issue-59764.rs", +"ui/imports/auxiliary/issue-85992-extern-1.rs", +"ui/imports/auxiliary/issue-85992-extern-2.rs", +"ui/imports/issue-26873-multifile/issue-26873-multifile.rs", +"ui/imports/issue-26873-multifile/issue-26873-onefile.rs", +"ui/imports/issue-45829/auxiliary/issue-45829-a.rs", +"ui/imports/issue-45829/auxiliary/issue-45829-b.rs", +"ui/imports/issue-45829/issue-45829.rs", +"ui/imports/issue-113953.rs", +"ui/imports/issue-109343.rs", +"ui/imports/issue-13404.rs", +"ui/imports/issue-1697.rs", +"ui/imports/issue-19498.rs", +"ui/imports/issue-24081.rs", +"ui/imports/issue-25396.rs", +"ui/imports/issue-26886.rs", +"ui/imports/issue-28388-1.rs", +"ui/imports/issue-28388-2.rs", +"ui/imports/issue-2937.rs", +"ui/imports/issue-30560.rs", +"ui/imports/issue-31212.rs", +"ui/imports/issue-32833.rs", +"ui/imports/issue-33464.rs", +"ui/imports/issue-36881.rs", +"ui/imports/issue-37887.rs", +"ui/imports/issue-38293.rs", +"ui/imports/issue-4366-2.rs", +"ui/imports/issue-4366.rs", +"ui/imports/issue-47623.rs", +"ui/imports/issue-4865-1.rs", +"ui/imports/issue-4865-2.rs", +"ui/imports/issue-4865-3.rs", +"ui/imports/issue-53269.rs", +"ui/imports/issue-53512.rs", +"ui/imports/issue-53565.rs", +"ui/imports/issue-55457.rs", +"ui/imports/issue-55884-1.rs", +"ui/imports/issue-57015.rs", +"ui/imports/issue-8208.rs", +"ui/imports/issue-8640.rs", +"ui/imports/issue-55884-2.rs", +"ui/imports/issue-109148.rs", +"ui/imports/issue-18083.rs", +"ui/imports/issue-24883.rs", +"ui/imports/issue-26930.rs", +"ui/imports/issue-28134.rs", +"ui/imports/issue-32119.rs", +"ui/imports/issue-32222.rs", +"ui/imports/issue-32354-suggest-import-rename.rs", +"ui/imports/issue-45799-bad-extern-crate-rename-suggestion-formatting.rs", +"ui/imports/issue-52891.rs", +"ui/imports/issue-53140.rs", +"ui/imports/issue-55811.rs", +"ui/imports/issue-56125.rs", +"ui/imports/issue-56263.rs", +"ui/imports/issue-57539.rs", +"ui/imports/issue-59764.rs", +"ui/imports/issue-62767.rs", +"ui/imports/issue-68103.rs", +"ui/imports/issue-99695-b.rs", +"ui/imports/issue-99695.rs", +"ui/imports/issue-85992.rs", +"ui/inference/need_type_info/issue-103053.rs", +"ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.rs", +"ui/inference/need_type_info/issue-109905.rs", +"ui/inference/need_type_info/issue-113264-incorrect-impl-trait-in-path-suggestion.rs", +"ui/inference/issue-103587.rs", +"ui/inference/issue-104649.rs", +"ui/inference/issue-107090.rs", +"ui/inference/issue-36053.rs", +"ui/inference/issue-70082.rs", +"ui/inference/issue-71309.rs", +"ui/inference/issue-71584.rs", +"ui/inference/issue-71732.rs", +"ui/inference/issue-72616.rs", +"ui/inference/issue-72690.rs", +"ui/inference/issue-80816.rs", +"ui/inference/issue-81522.rs", +"ui/inference/issue-83606.rs", +"ui/inference/issue-86162-1.rs", +"ui/inference/issue-86162-2.rs", +"ui/inference/issue-28935.rs", +"ui/inference/issue-70703.rs", +"ui/inference/issue-80409.rs", +"ui/inference/issue-113354.rs", +"ui/infinite/issue-41731-infinite-macro-print.rs", +"ui/infinite/issue-41731-infinite-macro-println.rs", +"ui/intrinsics/issue-28575.rs", +"ui/intrinsics/issue-84297-reifying-copy.rs", +"ui/issues/auxiliary/issue-11224.rs", +"ui/issues/auxiliary/issue-11508.rs", +"ui/issues/auxiliary/issue-11529.rs", +"ui/issues/auxiliary/issue-11680.rs", +"ui/issues/auxiliary/issue-12133-dylib.rs", +"ui/issues/auxiliary/issue-12133-dylib2.rs", +"ui/issues/auxiliary/issue-12133-rlib.rs", +"ui/issues/auxiliary/issue-12612-1.rs", +"ui/issues/auxiliary/issue-12612-2.rs", +"ui/issues/auxiliary/issue-12660-aux.rs", +"ui/issues/auxiliary/issue-13507.rs", +"ui/issues/auxiliary/issue-13620-1.rs", +"ui/issues/auxiliary/issue-13620-2.rs", +"ui/issues/auxiliary/issue-13872-1.rs", +"ui/issues/auxiliary/issue-13872-2.rs", +"ui/issues/auxiliary/issue-13872-3.rs", +"ui/issues/auxiliary/issue-14344-1.rs", +"ui/issues/auxiliary/issue-14344-2.rs", +"ui/issues/auxiliary/issue-14421.rs", +"ui/issues/auxiliary/issue-14422.rs", +"ui/issues/auxiliary/issue-15562.rs", +"ui/issues/auxiliary/issue-16643.rs", +"ui/issues/auxiliary/issue-16725.rs", +"ui/issues/auxiliary/issue-17662.rs", +"ui/issues/auxiliary/issue-18501.rs", +"ui/issues/auxiliary/issue-18514.rs", +"ui/issues/auxiliary/issue-18711.rs", +"ui/issues/auxiliary/issue-18913-1.rs", +"ui/issues/auxiliary/issue-18913-2.rs", +"ui/issues/auxiliary/issue-1920.rs", +"ui/issues/auxiliary/issue-19293.rs", +"ui/issues/auxiliary/issue-19340-1.rs", +"ui/issues/auxiliary/issue-20389.rs", +"ui/issues/auxiliary/issue-21202.rs", +"ui/issues/auxiliary/issue-2170-lib.rs", +"ui/issues/auxiliary/issue-2316-a.rs", +"ui/issues/auxiliary/issue-2316-b.rs", +"ui/issues/auxiliary/issue-2380.rs", +"ui/issues/auxiliary/issue-2414-a.rs", +"ui/issues/auxiliary/issue-2414-b.rs", +"ui/issues/auxiliary/issue-2472-b.rs", +"ui/issues/auxiliary/issue-25185-1.rs", +"ui/issues/auxiliary/issue-25185-2.rs", +"ui/issues/auxiliary/issue-2526.rs", +"ui/issues/auxiliary/issue-25467.rs", +"ui/issues/auxiliary/issue-2631-a.rs", +"ui/issues/auxiliary/issue-2723-a.rs", +"ui/issues/auxiliary/issue-29181.rs", +"ui/issues/auxiliary/issue-29265.rs", +"ui/issues/auxiliary/issue-29485.rs", +"ui/issues/auxiliary/issue-3012-1.rs", +"ui/issues/auxiliary/issue-30123-aux.rs", +"ui/issues/auxiliary/issue-3136-a.rs", +"ui/issues/auxiliary/issue-31702-1.rs", +"ui/issues/auxiliary/issue-34796-aux.rs", +"ui/issues/auxiliary/issue-36954.rs", +"ui/issues/auxiliary/issue-38190.rs", +"ui/issues/auxiliary/issue-38226-aux.rs", +"ui/issues/auxiliary/issue-3979-traits.rs", +"ui/issues/auxiliary/issue-41053.rs", +"ui/issues/auxiliary/issue-41549.rs", +"ui/issues/auxiliary/issue-42007-s.rs", +"ui/issues/auxiliary/issue-4208-cc.rs", +"ui/issues/auxiliary/issue-4545.rs", +"ui/issues/auxiliary/issue-48984-aux.rs", +"ui/issues/auxiliary/issue-49544.rs", +"ui/issues/auxiliary/issue-51798.rs", +"ui/issues/auxiliary/issue-52489.rs", +"ui/issues/auxiliary/issue-5518.rs", +"ui/issues/auxiliary/issue-5521.rs", +"ui/issues/auxiliary/issue-56943.rs", +"ui/issues/auxiliary/issue-57271-lib.rs", +"ui/issues/auxiliary/issue-5844-aux.rs", +"ui/issues/auxiliary/issue-7178.rs", +"ui/issues/auxiliary/issue-73112.rs", +"ui/issues/auxiliary/issue-7899.rs", +"ui/issues/auxiliary/issue-8044.rs", +"ui/issues/auxiliary/issue-8259.rs", +"ui/issues/auxiliary/issue-8401.rs", +"ui/issues/auxiliary/issue-9123.rs", +"ui/issues/auxiliary/issue-9155.rs", +"ui/issues/auxiliary/issue-9188.rs", +"ui/issues/auxiliary/issue-9906.rs", +"ui/issues/auxiliary/issue-9968.rs", +"ui/issues/auxiliary/issue-111011.rs", +"ui/issues/auxiliary/issue-31702-2.rs", +"ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-lib.rs", +"ui/issues/issue-24687-embed-debuginfo/auxiliary/issue-24687-mbcs-in-comments.rs", +"ui/issues/issue-37311-type-length-limit/issue-37311.rs", +"ui/issues/issue-38875/auxiliary/issue-38875-b.rs", +"ui/issues/issue-38875/issue-38875.rs", +"ui/issues/issue-40402-ref-hints/issue-40402-1.rs", +"ui/issues/issue-40402-ref-hints/issue-40402-2.rs", +"ui/issues/issue-41652/auxiliary/issue-41652-b.rs", +"ui/issues/issue-41652/issue-41652.rs", +"ui/issues/issue-70093/issue-70093-link-directives.rs", +"ui/issues/issue-70093/issue-70093.rs", +"ui/issues/issue-77218/issue-77218-2.rs", +"ui/issues/issue-77218/issue-77218.rs", +"ui/issues/issue-100605.rs", +"ui/issues/issue-10228.rs", +"ui/issues/issue-10291.rs", +"ui/issues/issue-102964.rs", +"ui/issues/issue-10412.rs", +"ui/issues/issue-10436.rs", +"ui/issues/issue-10465.rs", +"ui/issues/issue-10545.rs", +"ui/issues/issue-10638.rs", +"ui/issues/issue-10656.rs", +"ui/issues/issue-10682.rs", +"ui/issues/issue-10683.rs", +"ui/issues/issue-10718.rs", +"ui/issues/issue-10734.rs", +"ui/issues/issue-10764.rs", +"ui/issues/issue-10767.rs", +"ui/issues/issue-10802.rs", +"ui/issues/issue-10806.rs", +"ui/issues/issue-10877.rs", +"ui/issues/issue-11004.rs", +"ui/issues/issue-11192.rs", +"ui/issues/issue-11205.rs", +"ui/issues/issue-11224.rs", +"ui/issues/issue-11267.rs", +"ui/issues/issue-11374.rs", +"ui/issues/issue-11382.rs", +"ui/issues/issue-11508.rs", +"ui/issues/issue-11529.rs", +"ui/issues/issue-11552.rs", +"ui/issues/issue-11593.rs", +"ui/issues/issue-11677.rs", +"ui/issues/issue-11680.rs", +"ui/issues/issue-11681.rs", +"ui/issues/issue-11692-1.rs", +"ui/issues/issue-11692-2.rs", +"ui/issues/issue-11771.rs", +"ui/issues/issue-11820.rs", +"ui/issues/issue-11844.rs", +"ui/issues/issue-11873.rs", +"ui/issues/issue-11958.rs", +"ui/issues/issue-12028.rs", +"ui/issues/issue-12033.rs", +"ui/issues/issue-12041.rs", +"ui/issues/issue-12127.rs", +"ui/issues/issue-12133-1.rs", +"ui/issues/issue-12133-2.rs", +"ui/issues/issue-12187-1.rs", +"ui/issues/issue-12187-2.rs", +"ui/issues/issue-12285.rs", +"ui/issues/issue-12567.rs", +"ui/issues/issue-12612.rs", +"ui/issues/issue-12660.rs", +"ui/issues/issue-12677.rs", +"ui/issues/issue-12699.rs", +"ui/issues/issue-12744.rs", +"ui/issues/issue-12860.rs", +"ui/issues/issue-12863.rs", +"ui/issues/issue-12909.rs", +"ui/issues/issue-13027.rs", +"ui/issues/issue-13033.rs", +"ui/issues/issue-13058.rs", +"ui/issues/issue-13204.rs", +"ui/issues/issue-13214.rs", +"ui/issues/issue-13259-windows-tcb-trash.rs", +"ui/issues/issue-13264.rs", +"ui/issues/issue-13323.rs", +"ui/issues/issue-13359.rs", +"ui/issues/issue-13407.rs", +"ui/issues/issue-13434.rs", +"ui/issues/issue-13446.rs", +"ui/issues/issue-13466.rs", +"ui/issues/issue-13482.rs", +"ui/issues/issue-13497-2.rs", +"ui/issues/issue-13497.rs", +"ui/issues/issue-13507-2.rs", +"ui/issues/issue-1362.rs", +"ui/issues/issue-13620.rs", +"ui/issues/issue-13665.rs", +"ui/issues/issue-13763.rs", +"ui/issues/issue-13808.rs", +"ui/issues/issue-13847.rs", +"ui/issues/issue-13867.rs", +"ui/issues/issue-13872.rs", +"ui/issues/issue-14091-2.rs", +"ui/issues/issue-14091.rs", +"ui/issues/issue-14092.rs", +"ui/issues/issue-14229.rs", +"ui/issues/issue-14285.rs", +"ui/issues/issue-14308.rs", +"ui/issues/issue-14344.rs", +"ui/issues/issue-14366.rs", +"ui/issues/issue-14382.rs", +"ui/issues/issue-14393.rs", +"ui/issues/issue-14399.rs", +"ui/issues/issue-14421.rs", +"ui/issues/issue-14422.rs", +"ui/issues/issue-1448-2.rs", +"ui/issues/issue-1451.rs", +"ui/issues/issue-14541.rs", +"ui/issues/issue-1460.rs", +"ui/issues/issue-14721.rs", +"ui/issues/issue-1476.rs", +"ui/issues/issue-14821.rs", +"ui/issues/issue-14845.rs", +"ui/issues/issue-14853.rs", +"ui/issues/issue-14865.rs", +"ui/issues/issue-14875.rs", +"ui/issues/issue-14915.rs", +"ui/issues/issue-14919.rs", +"ui/issues/issue-15034.rs", +"ui/issues/issue-15043.rs", +"ui/issues/issue-15063.rs", +"ui/issues/issue-15094.rs", +"ui/issues/issue-15104.rs", +"ui/issues/issue-15129-rpass.rs", +"ui/issues/issue-15155.rs", +"ui/issues/issue-15167.rs", +"ui/issues/issue-15189.rs", +"ui/issues/issue-15207.rs", +"ui/issues/issue-15260.rs", +"ui/issues/issue-15381.rs", +"ui/issues/issue-15444.rs", +"ui/issues/issue-15523-big.rs", +"ui/issues/issue-15523.rs", +"ui/issues/issue-15562.rs", +"ui/issues/issue-15571.rs", +"ui/issues/issue-15673.rs", +"ui/issues/issue-15756.rs", +"ui/issues/issue-15763.rs", +"ui/issues/issue-15774.rs", +"ui/issues/issue-15783.rs", +"ui/issues/issue-15793.rs", +"ui/issues/issue-15858.rs", +"ui/issues/issue-15896.rs", +"ui/issues/issue-15965.rs", +"ui/issues/issue-16048.rs", +"ui/issues/issue-16149.rs", +"ui/issues/issue-16151.rs", +"ui/issues/issue-16250.rs", +"ui/issues/issue-16256.rs", +"ui/issues/issue-16278.rs", +"ui/issues/issue-16338.rs", +"ui/issues/issue-16401.rs", +"ui/issues/issue-16441.rs", +"ui/issues/issue-16452.rs", +"ui/issues/issue-16492.rs", +"ui/issues/issue-16530.rs", +"ui/issues/issue-16560.rs", +"ui/issues/issue-16562.rs", +"ui/issues/issue-1660.rs", +"ui/issues/issue-16643.rs", +"ui/issues/issue-16648.rs", +"ui/issues/issue-16671.rs", +"ui/issues/issue-16683.rs", +"ui/issues/issue-16725.rs", +"ui/issues/issue-16739.rs", +"ui/issues/issue-16745.rs", +"ui/issues/issue-16774.rs", +"ui/issues/issue-16783.rs", +"ui/issues/issue-16819.rs", +"ui/issues/issue-16922-rpass.rs", +"ui/issues/issue-16922.rs", +"ui/issues/issue-16939.rs", +"ui/issues/issue-1696.rs", +"ui/issues/issue-16966.rs", +"ui/issues/issue-17001.rs", +"ui/issues/issue-17033.rs", +"ui/issues/issue-17068.rs", +"ui/issues/issue-17216.rs", +"ui/issues/issue-17252.rs", +"ui/issues/issue-17302.rs", +"ui/issues/issue-17322.rs", +"ui/issues/issue-17336.rs", +"ui/issues/issue-17337.rs", +"ui/issues/issue-17351.rs", +"ui/issues/issue-17361.rs", +"ui/issues/issue-17373.rs", +"ui/issues/issue-17385.rs", +"ui/issues/issue-17405.rs", +"ui/issues/issue-17431-1.rs", +"ui/issues/issue-17431-2.rs", +"ui/issues/issue-17431-3.rs", +"ui/issues/issue-17431-4.rs", +"ui/issues/issue-17431-5.rs", +"ui/issues/issue-17431-6.rs", +"ui/issues/issue-17431-7.rs", +"ui/issues/issue-17441.rs", +"ui/issues/issue-17450.rs", +"ui/issues/issue-17503.rs", +"ui/issues/issue-17546.rs", +"ui/issues/issue-17551.rs", +"ui/issues/issue-17651.rs", +"ui/issues/issue-17662.rs", +"ui/issues/issue-17734.rs", +"ui/issues/issue-17740.rs", +"ui/issues/issue-17758.rs", +"ui/issues/issue-17771.rs", +"ui/issues/issue-17800.rs", +"ui/issues/issue-17816.rs", +"ui/issues/issue-17877.rs", +"ui/issues/issue-17897.rs", +"ui/issues/issue-17904-2.rs", +"ui/issues/issue-17905-2.rs", +"ui/issues/issue-17905.rs", +"ui/issues/issue-17933.rs", +"ui/issues/issue-17954.rs", +"ui/issues/issue-17959.rs", +"ui/issues/issue-17994.rs", +"ui/issues/issue-17999.rs", +"ui/issues/issue-18058.rs", +"ui/issues/issue-18107.rs", +"ui/issues/issue-18110.rs", +"ui/issues/issue-18119.rs", +"ui/issues/issue-18159.rs", +"ui/issues/issue-18173.rs", +"ui/issues/issue-18183.rs", +"ui/issues/issue-18232.rs", +"ui/issues/issue-18352.rs", +"ui/issues/issue-18353.rs", +"ui/issues/issue-18423.rs", +"ui/issues/issue-18446.rs", +"ui/issues/issue-18464.rs", +"ui/issues/issue-18501.rs", +"ui/issues/issue-18514.rs", +"ui/issues/issue-18532.rs", +"ui/issues/issue-18539.rs", +"ui/issues/issue-18566.rs", +"ui/issues/issue-18611.rs", +"ui/issues/issue-18685.rs", +"ui/issues/issue-18711.rs", +"ui/issues/issue-18767.rs", +"ui/issues/issue-18783.rs", +"ui/issues/issue-18819.rs", +"ui/issues/issue-18845.rs", +"ui/issues/issue-18859.rs", +"ui/issues/issue-18913.rs", +"ui/issues/issue-18919.rs", +"ui/issues/issue-18952.rs", +"ui/issues/issue-18959.rs", +"ui/issues/issue-1900.rs", +"ui/issues/issue-19001.rs", +"ui/issues/issue-19086.rs", +"ui/issues/issue-19127.rs", +"ui/issues/issue-19135.rs", +"ui/issues/issue-1920-1.rs", +"ui/issues/issue-1920-2.rs", +"ui/issues/issue-1920-3.rs", +"ui/issues/issue-19244-1.rs", +"ui/issues/issue-19244-2.rs", +"ui/issues/issue-19293.rs", +"ui/issues/issue-19340-1.rs", +"ui/issues/issue-19340-2.rs", +"ui/issues/issue-19367.rs", +"ui/issues/issue-19380.rs", +"ui/issues/issue-19404.rs", +"ui/issues/issue-19482.rs", +"ui/issues/issue-19499.rs", +"ui/issues/issue-19521.rs", +"ui/issues/issue-19692.rs", +"ui/issues/issue-19707.rs", +"ui/issues/issue-19734.rs", +"ui/issues/issue-1974.rs", +"ui/issues/issue-19811-escape-unicode.rs", +"ui/issues/issue-19922.rs", +"ui/issues/issue-19991.rs", +"ui/issues/issue-20055-box-trait.rs", +"ui/issues/issue-20055-box-unsized-array.rs", +"ui/issues/issue-20162.rs", +"ui/issues/issue-20174.rs", +"ui/issues/issue-20225.rs", +"ui/issues/issue-20261.rs", +"ui/issues/issue-20313-rpass.rs", +"ui/issues/issue-20313.rs", +"ui/issues/issue-20389.rs", +"ui/issues/issue-20413.rs", +"ui/issues/issue-20427.rs", +"ui/issues/issue-20433.rs", +"ui/issues/issue-20544.rs", +"ui/issues/issue-20575.rs", +"ui/issues/issue-20616.rs", +"ui/issues/issue-20676.rs", +"ui/issues/issue-20714.rs", +"ui/issues/issue-2074.rs", +"ui/issues/issue-20772.rs", +"ui/issues/issue-20797.rs", +"ui/issues/issue-20803.rs", +"ui/issues/issue-20831-debruijn.rs", +"ui/issues/issue-20847.rs", +"ui/issues/issue-20939.rs", +"ui/issues/issue-20953.rs", +"ui/issues/issue-21033.rs", +"ui/issues/issue-21160.rs", +"ui/issues/issue-21174.rs", +"ui/issues/issue-21177.rs", +"ui/issues/issue-21202.rs", +"ui/issues/issue-21291.rs", +"ui/issues/issue-21306.rs", +"ui/issues/issue-21332.rs", +"ui/issues/issue-21361.rs", +"ui/issues/issue-21384.rs", +"ui/issues/issue-21400.rs", +"ui/issues/issue-21449.rs", +"ui/issues/issue-2150.rs", +"ui/issues/issue-2151.rs", +"ui/issues/issue-21546.rs", +"ui/issues/issue-21554.rs", +"ui/issues/issue-21596.rs", +"ui/issues/issue-21600.rs", +"ui/issues/issue-21634.rs", +"ui/issues/issue-21655.rs", +"ui/issues/issue-2170-exe.rs", +"ui/issues/issue-21701.rs", +"ui/issues/issue-21763.rs", +"ui/issues/issue-21837.rs", +"ui/issues/issue-21891.rs", +"ui/issues/issue-2190-1.rs", +"ui/issues/issue-21909.rs", +"ui/issues/issue-21922.rs", +"ui/issues/issue-21946.rs", +"ui/issues/issue-21950.rs", +"ui/issues/issue-21974.rs", +"ui/issues/issue-22008.rs", +"ui/issues/issue-22034.rs", +"ui/issues/issue-22036.rs", +"ui/issues/issue-2214.rs", +"ui/issues/issue-22258.rs", +"ui/issues/issue-22289.rs", +"ui/issues/issue-22312.rs", +"ui/issues/issue-22346.rs", +"ui/issues/issue-22370.rs", +"ui/issues/issue-22403.rs", +"ui/issues/issue-22426.rs", +"ui/issues/issue-22434.rs", +"ui/issues/issue-22468.rs", +"ui/issues/issue-22577.rs", +"ui/issues/issue-22599.rs", +"ui/issues/issue-22629.rs", +"ui/issues/issue-22638.rs", +"ui/issues/issue-22644.rs", +"ui/issues/issue-22684.rs", +"ui/issues/issue-22706.rs", +"ui/issues/issue-2281-part1.rs", +"ui/issues/issue-2284.rs", +"ui/issues/issue-22864-1.rs", +"ui/issues/issue-22864-2.rs", +"ui/issues/issue-22872.rs", +"ui/issues/issue-22874.rs", +"ui/issues/issue-2288.rs", +"ui/issues/issue-22886.rs", +"ui/issues/issue-22894.rs", +"ui/issues/issue-22933-2.rs", +"ui/issues/issue-22992-2.rs", +"ui/issues/issue-22992.rs", +"ui/issues/issue-23024.rs", +"ui/issues/issue-23036.rs", +"ui/issues/issue-23041.rs", +"ui/issues/issue-23046.rs", +"ui/issues/issue-23073.rs", +"ui/issues/issue-23122-1.rs", +"ui/issues/issue-23122-2.rs", +"ui/issues/issue-2316-c.rs", +"ui/issues/issue-23173.rs", +"ui/issues/issue-23189.rs", +"ui/issues/issue-23217.rs", +"ui/issues/issue-23253.rs", +"ui/issues/issue-23261.rs", +"ui/issues/issue-23281.rs", +"ui/issues/issue-23302-1.rs", +"ui/issues/issue-23302-2.rs", +"ui/issues/issue-23302-3.rs", +"ui/issues/issue-23304-1.rs", +"ui/issues/issue-23304-2.rs", +"ui/issues/issue-23311.rs", +"ui/issues/issue-23336.rs", +"ui/issues/issue-23406.rs", +"ui/issues/issue-23433.rs", +"ui/issues/issue-23485.rs", +"ui/issues/issue-23491.rs", +"ui/issues/issue-23543.rs", +"ui/issues/issue-23544.rs", +"ui/issues/issue-23550.rs", +"ui/issues/issue-23589.rs", +"ui/issues/issue-23611-enum-swap-in-drop.rs", +"ui/issues/issue-23649-1.rs", +"ui/issues/issue-23649-2.rs", +"ui/issues/issue-23649-3.rs", +"ui/issues/issue-23699.rs", +"ui/issues/issue-23781.rs", +"ui/issues/issue-2380-b.rs", +"ui/issues/issue-2383.rs", +"ui/issues/issue-23891.rs", +"ui/issues/issue-23898.rs", +"ui/issues/issue-23958.rs", +"ui/issues/issue-23966.rs", +"ui/issues/issue-23992.rs", +"ui/issues/issue-24013.rs", +"ui/issues/issue-24036.rs", +"ui/issues/issue-24086.rs", +"ui/issues/issue-2414-c.rs", +"ui/issues/issue-2428.rs", +"ui/issues/issue-24308.rs", +"ui/issues/issue-24322.rs", +"ui/issues/issue-24352.rs", +"ui/issues/issue-24353.rs", +"ui/issues/issue-24357.rs", +"ui/issues/issue-24363.rs", +"ui/issues/issue-24365.rs", +"ui/issues/issue-24424.rs", +"ui/issues/issue-24446.rs", +"ui/issues/issue-2445-b.rs", +"ui/issues/issue-2445.rs", +"ui/issues/issue-24533.rs", +"ui/issues/issue-24589.rs", +"ui/issues/issue-2463.rs", +"ui/issues/issue-24682.rs", +"ui/issues/issue-2472.rs", +"ui/issues/issue-24779.rs", +"ui/issues/issue-24819.rs", +"ui/issues/issue-2487-a.rs", +"ui/issues/issue-24947.rs", +"ui/issues/issue-24954.rs", +"ui/issues/issue-25076.rs", +"ui/issues/issue-25089.rs", +"ui/issues/issue-25145.rs", +"ui/issues/issue-25185.rs", +"ui/issues/issue-2526-a.rs", +"ui/issues/issue-25279.rs", +"ui/issues/issue-25343.rs", +"ui/issues/issue-25368.rs", +"ui/issues/issue-25386.rs", +"ui/issues/issue-25439.rs", +"ui/issues/issue-25467.rs", +"ui/issues/issue-25497.rs", +"ui/issues/issue-2550.rs", +"ui/issues/issue-25515.rs", +"ui/issues/issue-25549-multiple-drop.rs", +"ui/issues/issue-25679.rs", +"ui/issues/issue-25693.rs", +"ui/issues/issue-25746-bool-transmute.rs", +"ui/issues/issue-25757.rs", +"ui/issues/issue-25810.rs", +"ui/issues/issue-2590.rs", +"ui/issues/issue-25901.rs", +"ui/issues/issue-26056.rs", +"ui/issues/issue-26093.rs", +"ui/issues/issue-26127.rs", +"ui/issues/issue-26217.rs", +"ui/issues/issue-26237.rs", +"ui/issues/issue-26262.rs", +"ui/issues/issue-2631-b.rs", +"ui/issues/issue-2642.rs", +"ui/issues/issue-26468.rs", +"ui/issues/issue-26472.rs", +"ui/issues/issue-26619.rs", +"ui/issues/issue-26641.rs", +"ui/issues/issue-26655.rs", +"ui/issues/issue-26709.rs", +"ui/issues/issue-26802.rs", +"ui/issues/issue-26805.rs", +"ui/issues/issue-26812.rs", +"ui/issues/issue-26905-rpass.rs", +"ui/issues/issue-26905.rs", +"ui/issues/issue-26948.rs", +"ui/issues/issue-27008.rs", +"ui/issues/issue-27033.rs", +"ui/issues/issue-27042.rs", +"ui/issues/issue-27054-primitive-binary-ops.rs", +"ui/issues/issue-27078.rs", +"ui/issues/issue-2708.rs", +"ui/issues/issue-2723-b.rs", +"ui/issues/issue-27240.rs", +"ui/issues/issue-27268.rs", +"ui/issues/issue-27340.rs", +"ui/issues/issue-27401-dropflag-reinit.rs", +"ui/issues/issue-27592.rs", +"ui/issues/issue-27639.rs", +"ui/issues/issue-27815.rs", +"ui/issues/issue-27842.rs", +"ui/issues/issue-27859.rs", +"ui/issues/issue-27942.rs", +"ui/issues/issue-27949.rs", +"ui/issues/issue-27997.rs", +"ui/issues/issue-28105.rs", +"ui/issues/issue-28109.rs", +"ui/issues/issue-28181.rs", +"ui/issues/issue-2823.rs", +"ui/issues/issue-28344.rs", +"ui/issues/issue-28433.rs", +"ui/issues/issue-28472.rs", +"ui/issues/issue-2848.rs", +"ui/issues/issue-2849.rs", +"ui/issues/issue-28498-must-work-ex1.rs", +"ui/issues/issue-28498-must-work-ex2.rs", +"ui/issues/issue-28498-ugeh-ex1.rs", +"ui/issues/issue-28550.rs", +"ui/issues/issue-28568.rs", +"ui/issues/issue-28586.rs", +"ui/issues/issue-28625.rs", +"ui/issues/issue-28777.rs", +"ui/issues/issue-28828.rs", +"ui/issues/issue-28839.rs", +"ui/issues/issue-2895.rs", +"ui/issues/issue-28971.rs", +"ui/issues/issue-28983.rs", +"ui/issues/issue-28992-empty.rs", +"ui/issues/issue-2904.rs", +"ui/issues/issue-29053.rs", +"ui/issues/issue-29071-2.rs", +"ui/issues/issue-29092.rs", +"ui/issues/issue-29147-rpass.rs", +"ui/issues/issue-29147.rs", +"ui/issues/issue-29181.rs", +"ui/issues/issue-2935.rs", +"ui/issues/issue-29466.rs", +"ui/issues/issue-29485.rs", +"ui/issues/issue-2951.rs", +"ui/issues/issue-29522.rs", +"ui/issues/issue-29540.rs", +"ui/issues/issue-29663.rs", +"ui/issues/issue-29668.rs", +"ui/issues/issue-29723.rs", +"ui/issues/issue-29746.rs", +"ui/issues/issue-29821.rs", +"ui/issues/issue-29861.rs", +"ui/issues/issue-2989.rs", +"ui/issues/issue-29948.rs", +"ui/issues/issue-2995.rs", +"ui/issues/issue-30007.rs", +"ui/issues/issue-30018-panic.rs", +"ui/issues/issue-30081.rs", +"ui/issues/issue-3012-2.rs", +"ui/issues/issue-30123.rs", +"ui/issues/issue-3021-b.rs", +"ui/issues/issue-3021-d.rs", +"ui/issues/issue-30236.rs", +"ui/issues/issue-30255.rs", +"ui/issues/issue-3026.rs", +"ui/issues/issue-3037.rs", +"ui/issues/issue-30371.rs", +"ui/issues/issue-3038.rs", +"ui/issues/issue-30490.rs", +"ui/issues/issue-3052.rs", +"ui/issues/issue-30530.rs", +"ui/issues/issue-30589.rs", +"ui/issues/issue-30615.rs", +"ui/issues/issue-30756.rs", +"ui/issues/issue-30891.rs", +"ui/issues/issue-3091.rs", +"ui/issues/issue-31011.rs", +"ui/issues/issue-3109.rs", +"ui/issues/issue-3121.rs", +"ui/issues/issue-31267-additional.rs", +"ui/issues/issue-31267.rs", +"ui/issues/issue-31299.rs", +"ui/issues/issue-3136-b.rs", +"ui/issues/issue-31511.rs", +"ui/issues/issue-3154.rs", +"ui/issues/issue-31702.rs", +"ui/issues/issue-31769.rs", +"ui/issues/issue-31776.rs", +"ui/issues/issue-31910.rs", +"ui/issues/issue-32004.rs", +"ui/issues/issue-32008.rs", +"ui/issues/issue-32086.rs", +"ui/issues/issue-3214.rs", +"ui/issues/issue-3220.rs", +"ui/issues/issue-32292.rs", +"ui/issues/issue-32323.rs", +"ui/issues/issue-32326.rs", +"ui/issues/issue-32377.rs", +"ui/issues/issue-32389.rs", +"ui/issues/issue-32518.rs", +"ui/issues/issue-32655.rs", +"ui/issues/issue-32709.rs", +"ui/issues/issue-32782.rs", +"ui/issues/issue-32805.rs", +"ui/issues/issue-3290.rs", +"ui/issues/issue-32950.rs", +"ui/issues/issue-32995-2.rs", +"ui/issues/issue-32995.rs", +"ui/issues/issue-33187.rs", +"ui/issues/issue-33202.rs", +"ui/issues/issue-33287.rs", +"ui/issues/issue-33293.rs", +"ui/issues/issue-33387.rs", +"ui/issues/issue-3344.rs", +"ui/issues/issue-33461.rs", +"ui/issues/issue-33504.rs", +"ui/issues/issue-33525.rs", +"ui/issues/issue-33571.rs", +"ui/issues/issue-33687.rs", +"ui/issues/issue-33770.rs", +"ui/issues/issue-3389.rs", +"ui/issues/issue-33992.rs", +"ui/issues/issue-34047.rs", +"ui/issues/issue-34074.rs", +"ui/issues/issue-34209.rs", +"ui/issues/issue-34229.rs", +"ui/issues/issue-3429.rs", +"ui/issues/issue-34334.rs", +"ui/issues/issue-34349.rs", +"ui/issues/issue-34373.rs", +"ui/issues/issue-34427.rs", +"ui/issues/issue-3447.rs", +"ui/issues/issue-34503.rs", +"ui/issues/issue-34571.rs", +"ui/issues/issue-3477.rs", +"ui/issues/issue-34796.rs", +"ui/issues/issue-3500.rs", +"ui/issues/issue-35139.rs", +"ui/issues/issue-35241.rs", +"ui/issues/issue-35423.rs", +"ui/issues/issue-3556.rs", +"ui/issues/issue-3559.rs", +"ui/issues/issue-35600.rs", +"ui/issues/issue-3563-3.rs", +"ui/issues/issue-3574.rs", +"ui/issues/issue-35815.rs", +"ui/issues/issue-35988.rs", +"ui/issues/issue-36023.rs", +"ui/issues/issue-36036-associated-type-layout.rs", +"ui/issues/issue-36260.rs", +"ui/issues/issue-36278-prefix-nesting.rs", +"ui/issues/issue-36299.rs", +"ui/issues/issue-36400.rs", +"ui/issues/issue-36401.rs", +"ui/issues/issue-36474.rs", +"ui/issues/issue-3668.rs", +"ui/issues/issue-36744-bitcast-args-if-needed.rs", +"ui/issues/issue-36786-resolve-call.rs", +"ui/issues/issue-3680.rs", +"ui/issues/issue-36816.rs", +"ui/issues/issue-36836.rs", +"ui/issues/issue-36936.rs", +"ui/issues/issue-36954.rs", +"ui/issues/issue-3702-2.rs", +"ui/issues/issue-3702.rs", +"ui/issues/issue-3707.rs", +"ui/issues/issue-37109.rs", +"ui/issues/issue-3743.rs", +"ui/issues/issue-3753.rs", +"ui/issues/issue-37534.rs", +"ui/issues/issue-37576.rs", +"ui/issues/issue-3763.rs", +"ui/issues/issue-37686.rs", +"ui/issues/issue-37725.rs", +"ui/issues/issue-37733.rs", +"ui/issues/issue-3779.rs", +"ui/issues/issue-37884.rs", +"ui/issues/issue-38190.rs", +"ui/issues/issue-38412.rs", +"ui/issues/issue-38437.rs", +"ui/issues/issue-38458.rs", +"ui/issues/issue-3847.rs", +"ui/issues/issue-38556.rs", +"ui/issues/issue-38727.rs", +"ui/issues/issue-3874.rs", +"ui/issues/issue-38763.rs", +"ui/issues/issue-3878.rs", +"ui/issues/issue-38857.rs", +"ui/issues/issue-38919.rs", +"ui/issues/issue-38942.rs", +"ui/issues/issue-3895.rs", +"ui/issues/issue-38954.rs", +"ui/issues/issue-38987.rs", +"ui/issues/issue-39175.rs", +"ui/issues/issue-39211.rs", +"ui/issues/issue-39548.rs", +"ui/issues/issue-39687.rs", +"ui/issues/issue-39709.rs", +"ui/issues/issue-3979-generics.rs", +"ui/issues/issue-3979-xcrate.rs", +"ui/issues/issue-3979.rs", +"ui/issues/issue-39808.rs", +"ui/issues/issue-39827.rs", +"ui/issues/issue-39848.rs", +"ui/issues/issue-3993.rs", +"ui/issues/issue-39970.rs", +"ui/issues/issue-40000.rs", +"ui/issues/issue-40085.rs", +"ui/issues/issue-40235.rs", +"ui/issues/issue-40288-2.rs", +"ui/issues/issue-40288.rs", +"ui/issues/issue-40408.rs", +"ui/issues/issue-40510-1.rs", +"ui/issues/issue-40510-3.rs", +"ui/issues/issue-40610.rs", +"ui/issues/issue-40749.rs", +"ui/issues/issue-40827.rs", +"ui/issues/issue-40845.rs", +"ui/issues/issue-40861.rs", +"ui/issues/issue-40883.rs", +"ui/issues/issue-40951.rs", +"ui/issues/issue-41053.rs", +"ui/issues/issue-41139.rs", +"ui/issues/issue-41213.rs", +"ui/issues/issue-41229-ref-str.rs", +"ui/issues/issue-41479.rs", +"ui/issues/issue-41498.rs", +"ui/issues/issue-41549.rs", +"ui/issues/issue-41604.rs", +"ui/issues/issue-41677.rs", +"ui/issues/issue-41696.rs", +"ui/issues/issue-41726.rs", +"ui/issues/issue-41742.rs", +"ui/issues/issue-41744.rs", +"ui/issues/issue-41849-variance-req.rs", +"ui/issues/issue-41880.rs", +"ui/issues/issue-41888.rs", +"ui/issues/issue-41974.rs", +"ui/issues/issue-42007.rs", +"ui/issues/issue-4208.rs", +"ui/issues/issue-42106.rs", +"ui/issues/issue-42148.rs", +"ui/issues/issue-4228.rs", +"ui/issues/issue-42312.rs", +"ui/issues/issue-42453.rs", +"ui/issues/issue-4252.rs", +"ui/issues/issue-42552.rs", +"ui/issues/issue-4265.rs", +"ui/issues/issue-42755.rs", +"ui/issues/issue-42796.rs", +"ui/issues/issue-42880.rs", +"ui/issues/issue-43162.rs", +"ui/issues/issue-43205.rs", +"ui/issues/issue-43250.rs", +"ui/issues/issue-43291.rs", +"ui/issues/issue-4333.rs", +"ui/issues/issue-4335.rs", +"ui/issues/issue-43355.rs", +"ui/issues/issue-43420-no-over-suggest.rs", +"ui/issues/issue-43424.rs", +"ui/issues/issue-43431.rs", +"ui/issues/issue-43692.rs", +"ui/issues/issue-43853.rs", +"ui/issues/issue-4387.rs", +"ui/issues/issue-43910.rs", +"ui/issues/issue-43923.rs", +"ui/issues/issue-43925.rs", +"ui/issues/issue-43926.rs", +"ui/issues/issue-43988.rs", +"ui/issues/issue-44023.rs", +"ui/issues/issue-44078.rs", +"ui/issues/issue-44255.rs", +"ui/issues/issue-44405.rs", +"ui/issues/issue-4517.rs", +"ui/issues/issue-4541.rs", +"ui/issues/issue-4542.rs", +"ui/issues/issue-4545.rs", +"ui/issues/issue-45510.rs", +"ui/issues/issue-45730.rs", +"ui/issues/issue-45801.rs", +"ui/issues/issue-45965.rs", +"ui/issues/issue-46069.rs", +"ui/issues/issue-46101.rs", +"ui/issues/issue-46302.rs", +"ui/issues/issue-46311.rs", +"ui/issues/issue-46332.rs", +"ui/issues/issue-46438.rs", +"ui/issues/issue-46471-1.rs", +"ui/issues/issue-46472.rs", +"ui/issues/issue-46604.rs", +"ui/issues/issue-46771.rs", +"ui/issues/issue-46983.rs", +"ui/issues/issue-47073-zero-padded-tuple-struct-indices.rs", +"ui/issues/issue-47094.rs", +"ui/issues/issue-47184.rs", +"ui/issues/issue-4734.rs", +"ui/issues/issue-4735.rs", +"ui/issues/issue-4736.rs", +"ui/issues/issue-47377.rs", +"ui/issues/issue-47380.rs", +"ui/issues/issue-47486.rs", +"ui/issues/issue-4759-1.rs", +"ui/issues/issue-4759.rs", +"ui/issues/issue-47638.rs", +"ui/issues/issue-47715.rs", +"ui/issues/issue-47725.rs", +"ui/issues/issue-48006.rs", +"ui/issues/issue-48132.rs", +"ui/issues/issue-48159.rs", +"ui/issues/issue-48276.rs", +"ui/issues/issue-48364.rs", +"ui/issues/issue-48728.rs", +"ui/issues/issue-4875.rs", +"ui/issues/issue-48838.rs", +"ui/issues/issue-48984.rs", +"ui/issues/issue-49298.rs", +"ui/issues/issue-4935.rs", +"ui/issues/issue-49632.rs", +"ui/issues/issue-4968.rs", +"ui/issues/issue-4972.rs", +"ui/issues/issue-49824.rs", +"ui/issues/issue-49854.rs", +"ui/issues/issue-49919.rs", +"ui/issues/issue-49934-errors.rs", +"ui/issues/issue-49934.rs", +"ui/issues/issue-49955.rs", +"ui/issues/issue-49973.rs", +"ui/issues/issue-5008-borrowed-traitobject-method-call.rs", +"ui/issues/issue-50403.rs", +"ui/issues/issue-50415.rs", +"ui/issues/issue-50442.rs", +"ui/issues/issue-50581.rs", +"ui/issues/issue-50582.rs", +"ui/issues/issue-50585.rs", +"ui/issues/issue-50600.rs", +"ui/issues/issue-50618.rs", +"ui/issues/issue-5062.rs", +"ui/issues/issue-50688.rs", +"ui/issues/issue-50714-1.rs", +"ui/issues/issue-50714.rs", +"ui/issues/issue-50761.rs", +"ui/issues/issue-50781.rs", +"ui/issues/issue-50802.rs", +"ui/issues/issue-5100.rs", +"ui/issues/issue-51022.rs", +"ui/issues/issue-51044.rs", +"ui/issues/issue-51102.rs", +"ui/issues/issue-51116.rs", +"ui/issues/issue-51154.rs", +"ui/issues/issue-51515.rs", +"ui/issues/issue-5153.rs", +"ui/issues/issue-51632-try-desugar-incompatible-types.rs", +"ui/issues/issue-51874.rs", +"ui/issues/issue-5192.rs", +"ui/issues/issue-51947.rs", +"ui/issues/issue-52049.rs", +"ui/issues/issue-52126-assign-op-invariance.rs", +"ui/issues/issue-52262.rs", +"ui/issues/issue-5239-1.rs", +"ui/issues/issue-5239-2.rs", +"ui/issues/issue-52533.rs", +"ui/issues/issue-52717.rs", +"ui/issues/issue-5280.rs", +"ui/issues/issue-5315.rs", +"ui/issues/issue-5321-immediates-with-bare-self.rs", +"ui/issues/issue-53251.rs", +"ui/issues/issue-53275.rs", +"ui/issues/issue-53300.rs", +"ui/issues/issue-53348.rs", +"ui/issues/issue-53498.rs", +"ui/issues/issue-5358-1.rs", +"ui/issues/issue-53728.rs", +"ui/issues/issue-53843.rs", +"ui/issues/issue-54044.rs", +"ui/issues/issue-54062.rs", +"ui/issues/issue-5439.rs", +"ui/issues/issue-54410.rs", +"ui/issues/issue-54477-reduced-2.rs", +"ui/issues/issue-54696.rs", +"ui/issues/issue-5518.rs", +"ui/issues/issue-5521.rs", +"ui/issues/issue-55376.rs", +"ui/issues/issue-55380.rs", +"ui/issues/issue-5550.rs", +"ui/issues/issue-5554.rs", +"ui/issues/issue-55587.rs", +"ui/issues/issue-55731.rs", +"ui/issues/issue-56199.rs", +"ui/issues/issue-56237.rs", +"ui/issues/issue-5666.rs", +"ui/issues/issue-56806.rs", +"ui/issues/issue-56835.rs", +"ui/issues/issue-56870.rs", +"ui/issues/issue-5688.rs", +"ui/issues/issue-56943.rs", +"ui/issues/issue-5708.rs", +"ui/issues/issue-5718.rs", +"ui/issues/issue-57198-pass.rs", +"ui/issues/issue-57271.rs", +"ui/issues/issue-57362-1.rs", +"ui/issues/issue-57362-2.rs", +"ui/issues/issue-5741.rs", +"ui/issues/issue-57741-1.rs", +"ui/issues/issue-57781.rs", +"ui/issues/issue-57924.rs", +"ui/issues/issue-58712.rs", +"ui/issues/issue-58734.rs", +"ui/issues/issue-5884.rs", +"ui/issues/issue-58857.rs", +"ui/issues/issue-5917.rs", +"ui/issues/issue-59488.rs", +"ui/issues/issue-59494.rs", +"ui/issues/issue-5988.rs", +"ui/issues/issue-5997-enum.rs", +"ui/issues/issue-5997-struct.rs", +"ui/issues/issue-5997.rs", +"ui/issues/issue-60218.rs", +"ui/issues/issue-60622.rs", +"ui/issues/issue-60989.rs", +"ui/issues/issue-61106.rs", +"ui/issues/issue-61108.rs", +"ui/issues/issue-6117.rs", +"ui/issues/issue-6130.rs", +"ui/issues/issue-61475.rs", +"ui/issues/issue-6153.rs", +"ui/issues/issue-61623.rs", +"ui/issues/issue-61894.rs", +"ui/issues/issue-62375.rs", +"ui/issues/issue-62480.rs", +"ui/issues/issue-6318.rs", +"ui/issues/issue-6344-let.rs", +"ui/issues/issue-6344-match.rs", +"ui/issues/issue-63983.rs", +"ui/issues/issue-64559.rs", +"ui/issues/issue-64792-bad-unicode-ctor.rs", +"ui/issues/issue-65131.rs", +"ui/issues/issue-65230.rs", +"ui/issues/issue-65462.rs", +"ui/issues/issue-6596-2.rs", +"ui/issues/issue-66353.rs", +"ui/issues/issue-6642.rs", +"ui/issues/issue-66667-function-cmp-cycle.rs", +"ui/issues/issue-66702-break-outside-loop-val.rs", +"ui/issues/issue-66706.rs", +"ui/issues/issue-66923-show-error-for-correct-call.rs", +"ui/issues/issue-67039-unsound-pin-partialeq.rs", +"ui/issues/issue-6738.rs", +"ui/issues/issue-67535.rs", +"ui/issues/issue-68010-large-zst-consts.rs", +"ui/issues/issue-68696-catch-during-unwind.rs", +"ui/issues/issue-6892.rs", +"ui/issues/issue-69130.rs", +"ui/issues/issue-6919.rs", +"ui/issues/issue-69306.rs", +"ui/issues/issue-6936.rs", +"ui/issues/issue-69455.rs", +"ui/issues/issue-69602-type-err-during-codegen-ice.rs", +"ui/issues/issue-69683.rs", +"ui/issues/issue-7012.rs", +"ui/issues/issue-70381.rs", +"ui/issues/issue-7044.rs", +"ui/issues/issue-7061.rs", +"ui/issues/issue-70673.rs", +"ui/issues/issue-70724-add_type_neq_err_label-unwrap.rs", +"ui/issues/issue-7092.rs", +"ui/issues/issue-71406.rs", +"ui/issues/issue-71676-2.rs", +"ui/issues/issue-7178.rs", +"ui/issues/issue-72076.rs", +"ui/issues/issue-72278.rs", +"ui/issues/issue-7246.rs", +"ui/issues/issue-72839-error-overflow.rs", +"ui/issues/issue-72933-match-stack-overflow.rs", +"ui/issues/issue-73112.rs", +"ui/issues/issue-7344.rs", +"ui/issues/issue-7364.rs", +"ui/issues/issue-74082.rs", +"ui/issues/issue-74564-if-expr-stack-overflow.rs", +"ui/issues/issue-7519-match-unit-in-arg.rs", +"ui/issues/issue-75283.rs", +"ui/issues/issue-7563.rs", +"ui/issues/issue-75704.rs", +"ui/issues/issue-7575.rs", +"ui/issues/issue-7607-1.rs", +"ui/issues/issue-76077.rs", +"ui/issues/issue-76191.rs", +"ui/issues/issue-7660.rs", +"ui/issues/issue-7663.rs", +"ui/issues/issue-7784.rs", +"ui/issues/issue-77919.rs", +"ui/issues/issue-78192.rs", +"ui/issues/issue-78622.rs", +"ui/issues/issue-7867.rs", +"ui/issues/issue-78957.rs", +"ui/issues/issue-7899.rs", +"ui/issues/issue-7911.rs", +"ui/issues/issue-7950.rs", +"ui/issues/issue-7970a.rs", +"ui/issues/issue-8044.rs", +"ui/issues/issue-80607.rs", +"ui/issues/issue-8248.rs", +"ui/issues/issue-8249.rs", +"ui/issues/issue-8259.rs", +"ui/issues/issue-8391.rs", +"ui/issues/issue-8401.rs", +"ui/issues/issue-8498.rs", +"ui/issues/issue-8506.rs", +"ui/issues/issue-86756.rs", +"ui/issues/issue-87199.rs", +"ui/issues/issue-8727.rs", +"ui/issues/issue-87490.rs", +"ui/issues/issue-8761.rs", +"ui/issues/issue-8767.rs", +"ui/issues/issue-8783.rs", +"ui/issues/issue-8860.rs", +"ui/issues/issue-8898.rs", +"ui/issues/issue-9047.rs", +"ui/issues/issue-9123.rs", +"ui/issues/issue-9129.rs", +"ui/issues/issue-9155.rs", +"ui/issues/issue-9188.rs", +"ui/issues/issue-9243.rs", +"ui/issues/issue-9259.rs", +"ui/issues/issue-9382.rs", +"ui/issues/issue-9446.rs", +"ui/issues/issue-9575.rs", +"ui/issues/issue-9719.rs", +"ui/issues/issue-9725.rs", +"ui/issues/issue-9737.rs", +"ui/issues/issue-9814.rs", +"ui/issues/issue-98299.rs", +"ui/issues/issue-9837.rs", +"ui/issues/issue-9906.rs", +"ui/issues/issue-9918.rs", +"ui/issues/issue-9942.rs", +"ui/issues/issue-9951.rs", +"ui/issues/issue-9968.rs", +"ui/issues/issue-99838.rs", +"ui/issues/issue-11047.rs", +"ui/issues/issue-11709.rs", +"ui/issues/issue-20644.rs", +"ui/issues/issue-23808.rs", +"ui/issues/issue-26997.rs", +"ui/issues/issue-28600.rs", +"ui/issues/issue-3656.rs", +"ui/issues/issue-50811.rs", +"ui/issues/issue-51907.rs", +"ui/issues/issue-5754.rs", +"ui/issues/issue-10396.rs", +"ui/issues/issue-10456.rs", +"ui/issues/issue-106755.rs", +"ui/issues/issue-10853.rs", +"ui/issues/issue-10902.rs", +"ui/issues/issue-11085.rs", +"ui/issues/issue-11384.rs", +"ui/issues/issue-11592.rs", +"ui/issues/issue-11740.rs", +"ui/issues/issue-11869.rs", +"ui/issues/issue-12729.rs", +"ui/issues/issue-12920.rs", +"ui/issues/issue-13105.rs", +"ui/issues/issue-13167.rs", +"ui/issues/issue-13202.rs", +"ui/issues/issue-13405.rs", +"ui/issues/issue-13482-2.rs", +"ui/issues/issue-13703.rs", +"ui/issues/issue-13775.rs", +"ui/issues/issue-14082.rs", +"ui/issues/issue-14254.rs", +"ui/issues/issue-14330.rs", +"ui/issues/issue-14901.rs", +"ui/issues/issue-14959.rs", +"ui/issues/issue-15734.rs", +"ui/issues/issue-15735.rs", +"ui/issues/issue-16596.rs", +"ui/issues/issue-16668.rs", +"ui/issues/issue-16994.rs", +"ui/issues/issue-17121.rs", +"ui/issues/issue-17732.rs", +"ui/issues/issue-17746.rs", +"ui/issues/issue-17904.rs", +"ui/issues/issue-18088.rs", +"ui/issues/issue-18188.rs", +"ui/issues/issue-18446-2.rs", +"ui/issues/issue-18576.rs", +"ui/issues/issue-18738.rs", +"ui/issues/issue-18809.rs", +"ui/issues/issue-18906.rs", +"ui/issues/issue-18988.rs", +"ui/issues/issue-19037.rs", +"ui/issues/issue-19097.rs", +"ui/issues/issue-19098.rs", +"ui/issues/issue-19100.rs", +"ui/issues/issue-19102.rs", +"ui/issues/issue-19129-1.rs", +"ui/issues/issue-19129-2.rs", +"ui/issues/issue-19398.rs", +"ui/issues/issue-19479.rs", +"ui/issues/issue-19601.rs", +"ui/issues/issue-1962.rs", +"ui/issues/issue-19631.rs", +"ui/issues/issue-19632.rs", +"ui/issues/issue-19850.rs", +"ui/issues/issue-19982.rs", +"ui/issues/issue-20009.rs", +"ui/issues/issue-20186.rs", +"ui/issues/issue-20396.rs", +"ui/issues/issue-20414.rs", +"ui/issues/issue-20454.rs", +"ui/issues/issue-20763-1.rs", +"ui/issues/issue-20763-2.rs", +"ui/issues/issue-20971.rs", +"ui/issues/issue-21140.rs", +"ui/issues/issue-21174-2.rs", +"ui/issues/issue-21245.rs", +"ui/issues/issue-21402.rs", +"ui/issues/issue-21622.rs", +"ui/issues/issue-22356.rs", +"ui/issues/issue-22471.rs", +"ui/issues/issue-22603.rs", +"ui/issues/issue-22673.rs", +"ui/issues/issue-22777.rs", +"ui/issues/issue-22781.rs", +"ui/issues/issue-22789.rs", +"ui/issues/issue-22814.rs", +"ui/issues/issue-22933-1.rs", +"ui/issues/issue-2311-2.rs", +"ui/issues/issue-2311.rs", +"ui/issues/issue-2312.rs", +"ui/issues/issue-23354-2.rs", +"ui/issues/issue-23354.rs", +"ui/issues/issue-23442.rs", +"ui/issues/issue-23477.rs", +"ui/issues/issue-24161.rs", +"ui/issues/issue-24227.rs", +"ui/issues/issue-24389.rs", +"ui/issues/issue-24434.rs", +"ui/issues/issue-2470-bounds-check-overflow.rs", +"ui/issues/issue-24945-repeat-dash-opts.rs", +"ui/issues/issue-2502.rs", +"ui/issues/issue-25180.rs", +"ui/issues/issue-25394.rs", +"ui/issues/issue-25579.rs", +"ui/issues/issue-26095.rs", +"ui/issues/issue-2611-3.rs", +"ui/issues/issue-26186.rs", +"ui/issues/issue-26205.rs", +"ui/issues/issue-26484.rs", +"ui/issues/issue-26614.rs", +"ui/issues/issue-26646.rs", +"ui/issues/issue-27105.rs", +"ui/issues/issue-27281.rs", +"ui/issues/issue-27433.rs", +"ui/issues/issue-2761.rs", +"ui/issues/issue-27697.rs", +"ui/issues/issue-27889.rs", +"ui/issues/issue-28279.rs", +"ui/issues/issue-28561.rs", +"ui/issues/issue-28776.rs", +"ui/issues/issue-28936.rs", +"ui/issues/issue-28999.rs", +"ui/issues/issue-29030.rs", +"ui/issues/issue-29037.rs", +"ui/issues/issue-29048.rs", +"ui/issues/issue-29071.rs", +"ui/issues/issue-29265.rs", +"ui/issues/issue-29276.rs", +"ui/issues/issue-29516.rs", +"ui/issues/issue-29710.rs", +"ui/issues/issue-29740.rs", +"ui/issues/issue-29743.rs", +"ui/issues/issue-29857.rs", +"ui/issues/issue-3029.rs", +"ui/issues/issue-30380.rs", +"ui/issues/issue-31260.rs", +"ui/issues/issue-3149.rs", +"ui/issues/issue-32122-1.rs", +"ui/issues/issue-32122-2.rs", +"ui/issues/issue-32324.rs", +"ui/issues/issue-32797.rs", +"ui/issues/issue-33096.rs", +"ui/issues/issue-33241.rs", +"ui/issues/issue-33941.rs", +"ui/issues/issue-3424.rs", +"ui/issues/issue-34418.rs", +"ui/issues/issue-34569.rs", +"ui/issues/issue-34721.rs", +"ui/issues/issue-34751.rs", +"ui/issues/issue-34780.rs", +"ui/issues/issue-34839.rs", +"ui/issues/issue-3521-2.rs", +"ui/issues/issue-35976.rs", +"ui/issues/issue-36075.rs", +"ui/issues/issue-3609.rs", +"ui/issues/issue-36116.rs", +"ui/issues/issue-36379.rs", +"ui/issues/issue-3668-2.rs", +"ui/issues/issue-36839.rs", +"ui/issues/issue-36856.rs", +"ui/issues/issue-37051.rs", +"ui/issues/issue-37131.rs", +"ui/issues/issue-37510.rs", +"ui/issues/issue-37598.rs", +"ui/issues/issue-37665.rs", +"ui/issues/issue-38160.rs", +"ui/issues/issue-38226.rs", +"ui/issues/issue-38381.rs", +"ui/issues/issue-3888-2.rs", +"ui/issues/issue-39089.rs", +"ui/issues/issue-39367.rs", +"ui/issues/issue-39467.rs", +"ui/issues/issue-3979-2.rs", +"ui/issues/issue-3991.rs", +"ui/issues/issue-39984.rs", +"ui/issues/issue-40136.rs", +"ui/issues/issue-4025.rs", +"ui/issues/issue-40350.rs", +"ui/issues/issue-40510-2.rs", +"ui/issues/issue-40510-4.rs", +"ui/issues/issue-40782.rs", +"ui/issues/issue-41272.rs", +"ui/issues/issue-41298.rs", +"ui/issues/issue-41628.rs", +"ui/issues/issue-41936-variance-coerce-unsized-cycle.rs", +"ui/issues/issue-41998.rs", +"ui/issues/issue-42210.rs", +"ui/issues/issue-42467.rs", +"ui/issues/issue-42956.rs", +"ui/issues/issue-43057.rs", +"ui/issues/issue-43357.rs", +"ui/issues/issue-43483.rs", +"ui/issues/issue-43806.rs", +"ui/issues/issue-44056.rs", +"ui/issues/issue-44216-add-instant.rs", +"ui/issues/issue-44216-add-system-time.rs", +"ui/issues/issue-44216-sub-instant.rs", +"ui/issues/issue-44216-sub-system-time.rs", +"ui/issues/issue-44239.rs", +"ui/issues/issue-44247.rs", +"ui/issues/issue-4464.rs", +"ui/issues/issue-44730.rs", +"ui/issues/issue-44851.rs", +"ui/issues/issue-45425.rs", +"ui/issues/issue-45562.rs", +"ui/issues/issue-45697-1.rs", +"ui/issues/issue-45697.rs", +"ui/issues/issue-45731.rs", +"ui/issues/issue-46756-consider-borrowing-cast-or-binexpr.rs", +"ui/issues/issue-46855.rs", +"ui/issues/issue-46964.rs", +"ui/issues/issue-47309.rs", +"ui/issues/issue-47364.rs", +"ui/issues/issue-47673.rs", +"ui/issues/issue-47703-1.rs", +"ui/issues/issue-47703-tuple.rs", +"ui/issues/issue-47703.rs", +"ui/issues/issue-47722.rs", +"ui/issues/issue-48131.rs", +"ui/issues/issue-4830.rs", +"ui/issues/issue-49544.rs", +"ui/issues/issue-50187.rs", +"ui/issues/issue-50411.rs", +"ui/issues/issue-50471.rs", +"ui/issues/issue-50518.rs", +"ui/issues/issue-50571.rs", +"ui/issues/issue-5067.rs", +"ui/issues/issue-51655.rs", +"ui/issues/issue-51798.rs", +"ui/issues/issue-52489.rs", +"ui/issues/issue-53333.rs", +"ui/issues/issue-53419.rs", +"ui/issues/issue-53568.rs", +"ui/issues/issue-54094.rs", +"ui/issues/issue-54462-mutable-noalias-correctness.rs", +"ui/issues/issue-5572.rs", +"ui/issues/issue-56128.rs", +"ui/issues/issue-56175.rs", +"ui/issues/issue-56229.rs", +"ui/issues/issue-57156.rs", +"ui/issues/issue-57162.rs", +"ui/issues/issue-57399-self-return-impl-trait.rs", +"ui/issues/issue-57741.rs", +"ui/issues/issue-58212.rs", +"ui/issues/issue-58344.rs", +"ui/issues/issue-58375-monomorphize-default-impls.rs", +"ui/issues/issue-5844.rs", +"ui/issues/issue-58463.rs", +"ui/issues/issue-5900.rs", +"ui/issues/issue-59020.rs", +"ui/issues/issue-59326.rs", +"ui/issues/issue-5950.rs", +"ui/issues/issue-59756.rs", +"ui/issues/issue-64430.rs", +"ui/issues/issue-64593.rs", +"ui/issues/issue-6557.rs", +"ui/issues/issue-65634-raw-ident-suggestion.rs", +"ui/issues/issue-66308.rs", +"ui/issues/issue-66768.rs", +"ui/issues/issue-68951.rs", +"ui/issues/issue-6898.rs", +"ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs", +"ui/issues/issue-69225-layout-repeated-checked-add.rs", +"ui/issues/issue-70746.rs", +"ui/issues/issue-71676-1.rs", +"ui/issues/issue-72002.rs", +"ui/issues/issue-7268.rs", +"ui/issues/issue-73229.rs", +"ui/issues/issue-76042.rs", +"ui/issues/issue-7607-2.rs", +"ui/issues/issue-76077-1.rs", +"ui/issues/issue-7673-cast-generically-implemented-trait.rs", +"ui/issues/issue-81584.rs", +"ui/issues/issue-8171-default-method-self-inherit-builtin-trait.rs", +"ui/issues/issue-81918.rs", +"ui/issues/issue-82833-slice-miscompile.rs", +"ui/issues/issue-83048.rs", +"ui/issues/issue-8398.rs", +"ui/issues/issue-8521.rs", +"ui/issues/issue-8578.rs", +"ui/issues/issue-87707.rs", +"ui/issues/issue-88150.rs", +"ui/issues/issue-9110.rs", +"ui/issues/issue-91489.rs", +"ui/issues/issue-9249.rs", +"ui/issues/issue-92741.rs", +"ui/issues/issue-12133-3.rs", +"ui/issues/issue-18389.rs", +"ui/issues/issue-35570.rs", +"ui/issues/issue-51714.rs", +"ui/issues/issue-5883.rs", +"ui/issues/issue-67552.rs", +"ui/issues/issue-85461.rs", +"ui/iterators/issue-28098.rs", +"ui/iterators/issue-58952-filter-type-length.rs", +"ui/lang-items/issue-31076.rs", +"ui/lang-items/issue-83471.rs", +"ui/lang-items/issue-86238.rs", +"ui/lang-items/issue-87573.rs", +"ui/lang-items/issue-19660.rs", +"ui/late-bound-lifetimes/issue-36381.rs", +"ui/late-bound-lifetimes/issue-80618.rs", +"ui/late-bound-lifetimes/issue-47511.rs", +"ui/layout/issue-60431-unsized-tail-behind-projection.rs", +"ui/layout/issue-84108.rs", +"ui/layout/issue-113941.rs", +"ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs", +"ui/layout/issue-96185-overaligned-enum.rs", +"ui/layout/issue-112048-unsizing-field-order.rs", +"ui/layout/issue-112048-unsizing-niche.rs", +"ui/let-else/issue-94176.rs", +"ui/let-else/issue-100103.rs", +"ui/let-else/issue-102317.rs", +"ui/let-else/issue-99975.rs", +"ui/lifetimes/auxiliary/issue-91763-aux.rs", +"ui/lifetimes/lifetime-errors/issue_74400.rs", +"ui/lifetimes/issue-105675.rs", +"ui/lifetimes/issue-107492-default-value-for-lifetime.rs", +"ui/lifetimes/issue-107988.rs", +"ui/lifetimes/issue-17728.rs", +"ui/lifetimes/issue-26638.rs", +"ui/lifetimes/issue-34979.rs", +"ui/lifetimes/issue-36744-without-calls.rs", +"ui/lifetimes/issue-55796.rs", +"ui/lifetimes/issue-64173-unused-lifetimes.rs", +"ui/lifetimes/issue-79187-2.rs", +"ui/lifetimes/issue-79187.rs", +"ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs", +"ui/lifetimes/issue-83907-invalid-fn-like-path.rs", +"ui/lifetimes/issue-90600-expected-return-static-indirect.rs", +"ui/lifetimes/issue-91763.rs", +"ui/lifetimes/issue-95023.rs", +"ui/lifetimes/issue-97193.rs", +"ui/lifetimes/issue-97194.rs", +"ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs", +"ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs", +"ui/lifetimes/issue-105227.rs", +"ui/lifetimes/issue-105507.rs", +"ui/lifetimes/issue-54378.rs", +"ui/lifetimes/issue-67498.rs", +"ui/lifetimes/issue-69314.rs", +"ui/lifetimes/issue-70917-lifetimes-in-fn-def.rs", +"ui/lifetimes/issue-76168-hr-outlives-2.rs", +"ui/lifetimes/issue-76168-hr-outlives.rs", +"ui/lifetimes/issue-77175.rs", +"ui/lifetimes/issue-83737-binders-across-types.rs", +"ui/lifetimes/issue-83737-erasing-bound-vars.rs", +"ui/lifetimes/issue-84398.rs", +"ui/lifetimes/issue-84604.rs", +"ui/lifetimes/issue-90170-elision-mismatch.rs", +"ui/lifetimes/issue-93911.rs", +"ui/limits/issue-75158-64.rs", +"ui/limits/issue-55878.rs", +"ui/limits/issue-69485-var-size-diffs-too-large.rs", +"ui/limits/issue-15919-32.rs", +"ui/limits/issue-15919-64.rs", +"ui/limits/issue-17913.rs", +"ui/limits/issue-56762.rs", +"ui/linkage-attr/issue-109144.rs", +"ui/linkage-attr/issue-10755.rs", +"ui/lint/dead-code/issue-59003.rs", +"ui/lint/dead-code/issue-68408-false-positive.rs", +"ui/lint/dead-code/issue-85071-2.rs", +"ui/lint/dead-code/issue-85071.rs", +"ui/lint/dead-code/issue-85255.rs", +"ui/lint/must_not_suspend/issue-89562.rs", +"ui/lint/unused/issue-105061-array-lint.rs", +"ui/lint/unused/issue-105061-should-lint.rs", +"ui/lint/unused/issue-105061.rs", +"ui/lint/unused/issue-30730.rs", +"ui/lint/unused/issue-46576.rs", +"ui/lint/unused/issue-59896.rs", +"ui/lint/unused/issue-67691-unused-field-in-or-pattern.rs", +"ui/lint/unused/issue-74883-unused-paren-baren-yield.rs", +"ui/lint/unused/issue-85913.rs", +"ui/lint/unused/issue-90807-unused-paren-error.rs", +"ui/lint/unused/issue-92751.rs", +"ui/lint/unused/issue-96606.rs", +"ui/lint/unused/issue-103320-must-use-ops.rs", +"ui/lint/unused/issue-104397.rs", +"ui/lint/unused/issue-47390-unused-variable-in-struct-pattern.rs", +"ui/lint/unused/issue-54180-unused-ref-field.rs", +"ui/lint/unused/issue-54538-unused-parens-lint.rs", +"ui/lint/unused/issue-70041.rs", +"ui/lint/unused/issue-71290-unused-paren-binop.rs", +"ui/lint/unused/issue-81314-unused-span-ident.rs", +"ui/lint/unused/issue-88519-unused-paren.rs", +"ui/lint/unused/issue-90807-unused-paren.rs", +"ui/lint/use-redundant/issue-92904.rs", +"ui/lint/issue-104392.rs", +"ui/lint/issue-106991.rs", +"ui/lint/issue-109152.rs", +"ui/lint/issue-111359.rs", +"ui/lint/issue-14309.rs", +"ui/lint/issue-17718-const-naming.rs", +"ui/lint/issue-1866.rs", +"ui/lint/issue-20343.rs", +"ui/lint/issue-30302.rs", +"ui/lint/issue-34798.rs", +"ui/lint/issue-35075.rs", +"ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs", +"ui/lint/issue-63364.rs", +"ui/lint/issue-66362-no-snake-case-warning-for-field-puns.rs", +"ui/lint/issue-79744.rs", +"ui/lint/issue-97094.rs", +"ui/lint/issue-101284.rs", +"ui/lint/issue-102705.rs", +"ui/lint/issue-103317.rs", +"ui/lint/issue-103435-extra-parentheses.rs", +"ui/lint/issue-104897.rs", +"ui/lint/issue-108155.rs", +"ui/lint/issue-109529.rs", +"ui/lint/issue-110573.rs", +"ui/lint/issue-112489.rs", +"ui/lint/issue-14837.rs", +"ui/lint/issue-31924-non-snake-ffi.rs", +"ui/lint/issue-47775-nested-macro-unnecessary-parens-arg.rs", +"ui/lint/issue-54099-camel-case-underscore-types.rs", +"ui/lint/issue-57410-1.rs", +"ui/lint/issue-57410.rs", +"ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs", +"ui/lint/issue-79546-fuel-ice.rs", +"ui/lint/issue-80988.rs", +"ui/lint/issue-81218.rs", +"ui/lint/issue-83477.rs", +"ui/lint/issue-86600-lint-twice.rs", +"ui/lint/issue-87274-paren-parent.rs", +"ui/lint/issue-89469.rs", +"ui/lint/issue-90614-accept-allow-text-direction-codepoint-in-comment-lint.rs", +"ui/lint/issue-99387.rs", +"ui/loops/issue-50576.rs", +"ui/loops/issue-82916.rs", +"ui/lowering/issue-96847.rs", +"ui/lto/issue-105637.rs", +"ui/lto/issue-100772.rs", +"ui/lto/issue-11154.rs", +"ui/macros/auxiliary/issue-100199.rs", +"ui/macros/auxiliary/issue-19163.rs", +"ui/macros/auxiliary/issue-40469.rs", +"ui/macros/auxiliary/issue-75982.rs", +"ui/macros/issue-100199.rs", +"ui/macros/issue-102878.rs", +"ui/macros/issue-103529.rs", +"ui/macros/issue-104769-concat_bytes-invalid-literal.rs", +"ui/macros/issue-105011.rs", +"ui/macros/issue-10536.rs", +"ui/macros/issue-106837.rs", +"ui/macros/issue-109237.rs", +"ui/macros/issue-111749.rs", +"ui/macros/issue-16098.rs", +"ui/macros/issue-19163.rs", +"ui/macros/issue-21356.rs", +"ui/macros/issue-22463.rs", +"ui/macros/issue-25274.rs", +"ui/macros/issue-25385.rs", +"ui/macros/issue-26094.rs", +"ui/macros/issue-26322.rs", +"ui/macros/issue-29084.rs", +"ui/macros/issue-30143.rs", +"ui/macros/issue-33185.rs", +"ui/macros/issue-34421-mac-expr-bad-stmt-good-add-semi.rs", +"ui/macros/issue-35450.rs", +"ui/macros/issue-37175.rs", +"ui/macros/issue-38715.rs", +"ui/macros/issue-39388.rs", +"ui/macros/issue-39404.rs", +"ui/macros/issue-40469.rs", +"ui/macros/issue-40770.rs", +"ui/macros/issue-41776.rs", +"ui/macros/issue-41803.rs", +"ui/macros/issue-44127.rs", +"ui/macros/issue-5060.rs", +"ui/macros/issue-51848.rs", +"ui/macros/issue-52169.rs", +"ui/macros/issue-54441.rs", +"ui/macros/issue-58490.rs", +"ui/macros/issue-61033-1.rs", +"ui/macros/issue-61033-2.rs", +"ui/macros/issue-61053-different-kleene.rs", +"ui/macros/issue-61053-duplicate-binder.rs", +"ui/macros/issue-61053-missing-repetition.rs", +"ui/macros/issue-61053-unbound.rs", +"ui/macros/issue-6596-1.rs", +"ui/macros/issue-68060.rs", +"ui/macros/issue-69396-const-no-type-in-macro.rs", +"ui/macros/issue-78325-inconsistent-resolution.rs", +"ui/macros/issue-78333.rs", +"ui/macros/issue-81006.rs", +"ui/macros/issue-83340.rs", +"ui/macros/issue-83344.rs", +"ui/macros/issue-84195-lint-anon-const.rs", +"ui/macros/issue-84632-eager-expansion-recursion-limit.rs", +"ui/macros/issue-86865.rs", +"ui/macros/issue-8709.rs", +"ui/macros/issue-8851.rs", +"ui/macros/issue-92267.rs", +"ui/macros/issue-98790.rs", +"ui/macros/issue-112342-1.rs", +"ui/macros/issue-112342-2.rs", +"ui/macros/issue-2804-2.rs", +"ui/macros/issue-34171.rs", +"ui/macros/issue-42954.rs", +"ui/macros/issue-57597.rs", +"ui/macros/issue-63102.rs", +"ui/macros/issue-68058.rs", +"ui/macros/issue-69838-mods-relative-to-included-path.rs", +"ui/macros/issue-70446.rs", +"ui/macros/issue-75982-foreign-macro-weird-mod.rs", +"ui/macros/issue-77475.rs", +"ui/macros/issue-78892-substitution-in-statement-attr.rs", +"ui/macros/issue-84429-matches-edition.rs", +"ui/macros/issue-86082-option-env-invalid-char.rs", +"ui/macros/issue-87877.rs", +"ui/macros/issue-88206.rs", +"ui/macros/issue-88228.rs", +"ui/macros/issue-95267.rs", +"ui/macros/issue-95533.rs", +"ui/macros/issue-98466-allow.rs", +"ui/macros/issue-98466.rs", +"ui/macros/issue-99261.rs", +"ui/macros/issue-99265.rs", +"ui/macros/issue-99907.rs", +"ui/malformed/issue-69341-malformed-derive-inert.rs", +"ui/malformed/issue-107423-unused-delim-only-one-no-pair.rs", +"ui/marker_trait_attr/issue-61651-type-mismatch.rs", +"ui/match/issue-11319.rs", +"ui/match/issue-11940.rs", +"ui/match/issue-12552.rs", +"ui/match/issue-18060.rs", +"ui/match/issue-26251.rs", +"ui/match/issue-26996.rs", +"ui/match/issue-27021.rs", +"ui/match/issue-33498.rs", +"ui/match/issue-41255.rs", +"ui/match/issue-42679.rs", +"ui/match/issue-46920-byte-array-patterns.rs", +"ui/match/issue-5530.rs", +"ui/match/issue-56685.rs", +"ui/match/issue-72680.rs", +"ui/match/issue-72896.rs", +"ui/match/issue-74050-end-span.rs", +"ui/match/issue-82866.rs", +"ui/match/issue-91058.rs", +"ui/match/issue-92100.rs", +"ui/match/issue-112438.rs", +"ui/match/issue-70972-dyn-trait.rs", +"ui/match/issue-82392.rs", +"ui/match/issue-84434.rs", +"ui/match/issue-113012.rs", +"ui/methods/issues/issue-105732.rs", +"ui/methods/issues/issue-61525.rs", +"ui/methods/issues/issue-84495.rs", +"ui/methods/issues/issue-90315.rs", +"ui/methods/issues/issue-94581.rs", +"ui/mir/auxiliary/issue_76375_aux.rs", +"ui/mir/validate/issue-95978-validator-lifetime-comparison.rs", +"ui/mir/issue-102389.rs", +"ui/mir/issue-107678-projection-with-lifetime.rs", +"ui/mir/issue-29227.rs", +"ui/mir/issue-67947.rs", +"ui/mir/issue-75419-validation-impl-trait.rs", +"ui/mir/issue-76803-branches-not-same.rs", +"ui/mir/issue-77359-simplify-arm-identity.rs", +"ui/mir/issue-83499-input-output-iteration-ice.rs", +"ui/mir/issue-89485.rs", +"ui/mir/issue-92893.rs", +"ui/mir/issue-112269.rs", +"ui/mir/issue-80949.rs", +"ui/mir/issue-101844.rs", +"ui/mir/issue-105809.rs", +"ui/mir/issue-106062.rs", +"ui/mir/issue-107691.rs", +"ui/mir/issue-109004-drop-large-array.rs", +"ui/mir/issue-109743.rs", +"ui/mir/issue-46845.rs", +"ui/mir/issue-60390.rs", +"ui/mir/issue-66851.rs", +"ui/mir/issue-66930.rs", +"ui/mir/issue-67639-normalization-ice.rs", +"ui/mir/issue-67710-inline-projection.rs", +"ui/mir/issue-68841.rs", +"ui/mir/issue-71793-inline-args-storage.rs", +"ui/mir/issue-73914.rs", +"ui/mir/issue-74739.rs", +"ui/mir/issue-75053.rs", +"ui/mir/issue-76248.rs", +"ui/mir/issue-76375.rs", +"ui/mir/issue-76740-copy-propagation.rs", +"ui/mir/issue-77002.rs", +"ui/mir/issue-77911.rs", +"ui/mir/issue-78496.rs", +"ui/mir/issue-91745.rs", +"ui/mir/issue-99852.rs", +"ui/mir/issue-99866.rs", +"ui/mir/issue66339.rs", +"ui/mismatched_types/issue-19109.rs", +"ui/mismatched_types/issue-26480.rs", +"ui/mismatched_types/issue-35030.rs", +"ui/mismatched_types/issue-36053-2.rs", +"ui/mismatched_types/issue-38371-unfixable.rs", +"ui/mismatched_types/issue-47706-trait.rs", +"ui/mismatched_types/issue-47706.rs", +"ui/mismatched_types/issue-74918-missing-lifetime.rs", +"ui/mismatched_types/issue-75361-mismatched-impl.rs", +"ui/mismatched_types/issue-84976.rs", +"ui/mismatched_types/issue-112036.rs", +"ui/mismatched_types/issue-106182.rs", +"ui/mismatched_types/issue-38371.rs", +"ui/missing-trait-bounds/auxiliary/issue-69725.rs", +"ui/missing-trait-bounds/issue-35677.rs", +"ui/missing-trait-bounds/issue-69725.rs", +"ui/modules/issue-56411.rs", +"ui/modules/issue-107649.rs", +"ui/modules/issue-56411-aux.rs", +"ui/moves/issue-22536-copy-mustnt-zero.rs", +"ui/moves/issue-46099-move-in-macro.rs", +"ui/moves/issue-72649-uninit-in-loop.rs", +"ui/moves/issue-75904-move-closure-loop.rs", +"ui/moves/issue-99470-move-out-of-some.rs", +"ui/never_type/issue-10176.rs", +"ui/never_type/issue-13352.rs", +"ui/never_type/issue-2149.rs", +"ui/never_type/issue-51506.rs", +"ui/never_type/issue-52443.rs", +"ui/never_type/issue-96335.rs", +"ui/never_type/issue-44402.rs", +"ui/never_type/issue-5500-1.rs", +"ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs", +"ui/nll/polonius/issue-46589.rs", +"ui/nll/relate_tys/issue-48071.rs", +"ui/nll/ty-outlives/issue-53789-1.rs", +"ui/nll/ty-outlives/issue-53789-2.rs", +"ui/nll/ty-outlives/issue-55756.rs", +"ui/nll/user-annotations/issue-54124.rs", +"ui/nll/user-annotations/issue-55241.rs", +"ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs", +"ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs", +"ui/nll/user-annotations/issue-54570-bootstrapping.rs", +"ui/nll/user-annotations/issue-55219.rs", +"ui/nll/issue-21232-partial-init-and-erroneous-use.rs", +"ui/nll/issue-21232-partial-init-and-use.rs", +"ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs", +"ui/nll/issue-27282-move-match-input-into-guard.rs", +"ui/nll/issue-27282-move-ref-mut-into-guard.rs", +"ui/nll/issue-27282-mutate-before-diverging-arm-1.rs", +"ui/nll/issue-27282-mutate-before-diverging-arm-2.rs", +"ui/nll/issue-27282-mutate-before-diverging-arm-3.rs", +"ui/nll/issue-27282-mutation-in-guard.rs", +"ui/nll/issue-27282-reborrow-ref-mut-in-guard.rs", +"ui/nll/issue-27868.rs", +"ui/nll/issue-30438-a.rs", +"ui/nll/issue-30438-b.rs", +"ui/nll/issue-30438-c.rs", +"ui/nll/issue-31567.rs", +"ui/nll/issue-42574-diagnostic-in-nested-closure.rs", +"ui/nll/issue-45157.rs", +"ui/nll/issue-45696-long-live-borrows-in-boxes.rs", +"ui/nll/issue-45696-no-variant-box-recur.rs", +"ui/nll/issue-45696-scribble-on-boxed-borrow.rs", +"ui/nll/issue-46023.rs", +"ui/nll/issue-46036.rs", +"ui/nll/issue-46589.rs", +"ui/nll/issue-47153-generic-const.rs", +"ui/nll/issue-47388.rs", +"ui/nll/issue-47470.rs", +"ui/nll/issue-47589.rs", +"ui/nll/issue-48070.rs", +"ui/nll/issue-48238.rs", +"ui/nll/issue-48623-closure.rs", +"ui/nll/issue-48623-generator.rs", +"ui/nll/issue-48697.rs", +"ui/nll/issue-48803.rs", +"ui/nll/issue-50343.rs", +"ui/nll/issue-50461-used-mut-from-moves.rs", +"ui/nll/issue-50716.rs", +"ui/nll/issue-51191.rs", +"ui/nll/issue-51244.rs", +"ui/nll/issue-51268.rs", +"ui/nll/issue-51512.rs", +"ui/nll/issue-52057.rs", +"ui/nll/issue-52059-report-when-borrow-and-drop-conflict.rs", +"ui/nll/issue-52086.rs", +"ui/nll/issue-52113.rs", +"ui/nll/issue-52213.rs", +"ui/nll/issue-52533-1.rs", +"ui/nll/issue-52534-1.rs", +"ui/nll/issue-52534-2.rs", +"ui/nll/issue-52534.rs", +"ui/nll/issue-52663-span-decl-captured-variable.rs", +"ui/nll/issue-52663-trait-object.rs", +"ui/nll/issue-52669.rs", +"ui/nll/issue-52742.rs", +"ui/nll/issue-53040.rs", +"ui/nll/issue-53123-raw-pointer-cast.rs", +"ui/nll/issue-53773.rs", +"ui/nll/issue-53807.rs", +"ui/nll/issue-54189.rs", +"ui/nll/issue-54302-cases.rs", +"ui/nll/issue-54302.rs", +"ui/nll/issue-54382-use-span-of-tail-of-block.rs", +"ui/nll/issue-54556-niconii.rs", +"ui/nll/issue-54556-stephaneyfx.rs", +"ui/nll/issue-54556-temps-in-tail-diagnostic.rs", +"ui/nll/issue-54556-used-vs-unused-tails.rs", +"ui/nll/issue-54556-wrap-it-up.rs", +"ui/nll/issue-54779-anon-static-lifetime.rs", +"ui/nll/issue-54943.rs", +"ui/nll/issue-55394.rs", +"ui/nll/issue-55401.rs", +"ui/nll/issue-55511.rs", +"ui/nll/issue-55850.rs", +"ui/nll/issue-57100.rs", +"ui/nll/issue-57265-return-type-wf-check.rs", +"ui/nll/issue-57280-1-flipped.rs", +"ui/nll/issue-57642-higher-ranked-subtype.rs", +"ui/nll/issue-57843.rs", +"ui/nll/issue-57960.rs", +"ui/nll/issue-57989.rs", +"ui/nll/issue-58053.rs", +"ui/nll/issue-58299.rs", +"ui/nll/issue-62007-assign-const-index.rs", +"ui/nll/issue-62007-assign-differing-fields.rs", +"ui/nll/issue-67007-escaping-data.rs", +"ui/nll/issue-68550.rs", +"ui/nll/issue-69114-static-mut-ty.rs", +"ui/nll/issue-69114-static-ty.rs", +"ui/nll/issue-73159-rpit-static.rs", +"ui/nll/issue-75777.rs", +"ui/nll/issue-95272.rs", +"ui/nll/issue-97997.rs", +"ui/nll/issue-98170.rs", +"ui/nll/issue-98589-closures-relate-named-regions.rs", +"ui/nll/issue-98693.rs", +"ui/nll/issue-112604-closure-output-normalize.rs", +"ui/nll/issue-16223.rs", +"ui/nll/issue-21114-ebfull.rs", +"ui/nll/issue-21114-kixunil.rs", +"ui/nll/issue-22323-temp-destruction.rs", +"ui/nll/issue-27583.rs", +"ui/nll/issue-30104.rs", +"ui/nll/issue-32382-index-assoc-type-with-lifetime.rs", +"ui/nll/issue-43058.rs", +"ui/nll/issue-47022.rs", +"ui/nll/issue-48179.rs", +"ui/nll/issue-50716-1.rs", +"ui/nll/issue-51345-2.rs", +"ui/nll/issue-51351.rs", +"ui/nll/issue-51770.rs", +"ui/nll/issue-52078.rs", +"ui/nll/issue-52992.rs", +"ui/nll/issue-53119.rs", +"ui/nll/issue-53570.rs", +"ui/nll/issue-54943-3.rs", +"ui/nll/issue-55288.rs", +"ui/nll/issue-55344.rs", +"ui/nll/issue-55651.rs", +"ui/nll/issue-55825-const-fn.rs", +"ui/nll/issue-57280-1.rs", +"ui/nll/issue-57280.rs", +"ui/nll/issue-61311-normalize.rs", +"ui/nll/issue-61320-normalize.rs", +"ui/nll/issue-61424.rs", +"ui/nll/issue-63154-normalize.rs", +"ui/nll/issue-78561.rs", +"ui/numbers-arithmetic/issue-8460.rs", +"ui/numbers-arithmetic/issue-105626.rs", +"ui/numbers-arithmetic/issue-8460-const.rs", +"ui/object-safety/issue-19538.rs", +"ui/object-safety/issue-102762.rs", +"ui/object-safety/issue-102933.rs", +"ui/object-safety/issue-106247.rs", +"ui/on-unimplemented/issue-104140.rs", +"ui/or-patterns/issue-64879-trailing-before-guard.rs", +"ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs", +"ui/or-patterns/issue-67514-irrefutable-param.rs", +"ui/or-patterns/issue-68785-irrefutable-param-with-at.rs", +"ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs", +"ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs", +"ui/overloaded/issue-14958.rs", +"ui/packed/issue-27060-2.rs", +"ui/packed/issue-27060.rs", +"ui/packed/issue-46152.rs", +"ui/panics/issue-47429-short-backtraces.rs", +"ui/parser/issues/auxiliary/issue-21146-inc.rs", +"ui/parser/issues/auxiliary/issue-89971-outer-attr-following-inner-attr-ice.rs", +"ui/parser/issues/auxiliary/issue-94340-inc.rs", +"ui/parser/issues/issue-101540.rs", +"ui/parser/issues/issue-102182-impl-trait-recover.rs", +"ui/parser/issues/issue-10392.rs", +"ui/parser/issues/issue-104088.rs", +"ui/parser/issues/issue-104367.rs", +"ui/parser/issues/issue-10636-1.rs", +"ui/parser/issues/issue-108242-semicolon-recovery.rs", +"ui/parser/issues/issue-110014.rs", +"ui/parser/issues/issue-111416.rs", +"ui/parser/issues/issue-13483.rs", +"ui/parser/issues/issue-14303.rs", +"ui/parser/issues/issue-15914.rs", +"ui/parser/issues/issue-15980.rs", +"ui/parser/issues/issue-1655.rs", +"ui/parser/issues/issue-17718-const-mut.rs", +"ui/parser/issues/issue-17904-2.rs", +"ui/parser/issues/issue-1802-1.rs", +"ui/parser/issues/issue-1802-2.rs", +"ui/parser/issues/issue-19096.rs", +"ui/parser/issues/issue-19398.rs", +"ui/parser/issues/issue-20616-1.rs", +"ui/parser/issues/issue-20616-2.rs", +"ui/parser/issues/issue-20616-3.rs", +"ui/parser/issues/issue-20616-4.rs", +"ui/parser/issues/issue-20616-5.rs", +"ui/parser/issues/issue-20616-6.rs", +"ui/parser/issues/issue-20616-7.rs", +"ui/parser/issues/issue-20616-8.rs", +"ui/parser/issues/issue-20616-9.rs", +"ui/parser/issues/issue-20711-2.rs", +"ui/parser/issues/issue-20711.rs", +"ui/parser/issues/issue-21153.rs", +"ui/parser/issues/issue-21475.rs", +"ui/parser/issues/issue-22647.rs", +"ui/parser/issues/issue-22712.rs", +"ui/parser/issues/issue-2354-1.rs", +"ui/parser/issues/issue-2354.rs", +"ui/parser/issues/issue-23620-invalid-escapes.rs", +"ui/parser/issues/issue-24197.rs", +"ui/parser/issues/issue-24375.rs", +"ui/parser/issues/issue-24780.rs", +"ui/parser/issues/issue-27255.rs", +"ui/parser/issues/issue-31804.rs", +"ui/parser/issues/issue-32214.rs", +"ui/parser/issues/issue-32446.rs", +"ui/parser/issues/issue-32501.rs", +"ui/parser/issues/issue-32505.rs", +"ui/parser/issues/issue-33262.rs", +"ui/parser/issues/issue-33413.rs", +"ui/parser/issues/issue-33418.rs", +"ui/parser/issues/issue-33455.rs", +"ui/parser/issues/issue-34222-1.rs", +"ui/parser/issues/issue-34255-1.rs", +"ui/parser/issues/issue-41155.rs", +"ui/parser/issues/issue-43196.rs", +"ui/parser/issues/issue-43692.rs", +"ui/parser/issues/issue-44021.rs", +"ui/parser/issues/issue-44406.rs", +"ui/parser/issues/issue-45296.rs", +"ui/parser/issues/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.rs", +"ui/parser/issues/issue-48508-aux.rs", +"ui/parser/issues/issue-49040.rs", +"ui/parser/issues/issue-51602.rs", +"ui/parser/issues/issue-52496.rs", +"ui/parser/issues/issue-5544-a.rs", +"ui/parser/issues/issue-5544-b.rs", +"ui/parser/issues/issue-56031.rs", +"ui/parser/issues/issue-57198.rs", +"ui/parser/issues/issue-5806.rs", +"ui/parser/issues/issue-58856-1.rs", +"ui/parser/issues/issue-58856-2.rs", +"ui/parser/issues/issue-59418.rs", +"ui/parser/issues/issue-60075.rs", +"ui/parser/issues/issue-62546.rs", +"ui/parser/issues/issue-62660.rs", +"ui/parser/issues/issue-62881.rs", +"ui/parser/issues/issue-62895.rs", +"ui/parser/issues/issue-62913.rs", +"ui/parser/issues/issue-64732.rs", +"ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.rs", +"ui/parser/issues/issue-65257-invalid-var-decl-recovery.rs", +"ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs", +"ui/parser/issues/issue-6610.rs", +"ui/parser/issues/issue-66357-unexpected-unreachable.rs", +"ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.rs", +"ui/parser/issues/issue-68000-unicode-ident-after-missing-comma.rs", +"ui/parser/issues/issue-68890-2.rs", +"ui/parser/issues/issue-68890.rs", +"ui/parser/issues/issue-69259.rs", +"ui/parser/issues/issue-70388-recover-dotdotdot-rest-pat.rs", +"ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.rs", +"ui/parser/issues/issue-70552-ascription-in-parens-after-call.rs", +"ui/parser/issues/issue-70583-block-is-empty-1.rs", +"ui/parser/issues/issue-70583-block-is-empty-2.rs", +"ui/parser/issues/issue-7222.rs", +"ui/parser/issues/issue-72253.rs", +"ui/parser/issues/issue-72373.rs", +"ui/parser/issues/issue-73568-lifetime-after-mut.rs", +"ui/parser/issues/issue-7970b.rs", +"ui/parser/issues/issue-81806.rs", +"ui/parser/issues/issue-83639.rs", +"ui/parser/issues/issue-84117.rs", +"ui/parser/issues/issue-84148-1.rs", +"ui/parser/issues/issue-8537.rs", +"ui/parser/issues/issue-86895.rs", +"ui/parser/issues/issue-87086-colon-path-sep.rs", +"ui/parser/issues/issue-87635.rs", +"ui/parser/issues/issue-87812-path.rs", +"ui/parser/issues/issue-87812.rs", +"ui/parser/issues/issue-88818.rs", +"ui/parser/issues/issue-89388.rs", +"ui/parser/issues/issue-89574.rs", +"ui/parser/issues/issue-89971-outer-attr-following-inner-attr-ice.rs", +"ui/parser/issues/issue-90993.rs", +"ui/parser/issues/issue-91461.rs", +"ui/parser/issues/issue-93282.rs", +"ui/parser/issues/issue-93867.rs", +"ui/parser/issues/issue-94340.rs", +"ui/parser/issues/issue-111148.rs", +"ui/parser/issues/issue-111692.rs", +"ui/parser/issues/issue-10392-2.rs", +"ui/parser/issues/issue-105209.rs", +"ui/parser/issues/issue-10636-2.rs", +"ui/parser/issues/issue-14303-fncall.rs", +"ui/parser/issues/issue-17904.rs", +"ui/parser/issues/issue-21146.rs", +"ui/parser/issues/issue-30318.rs", +"ui/parser/issues/issue-3036.rs", +"ui/parser/issues/issue-35813-postfix-after-cast.rs", +"ui/parser/issues/issue-46186.rs", +"ui/parser/issues/issue-48137-macros-cannot-interpolate-impl-items.rs", +"ui/parser/issues/issue-48508.rs", +"ui/parser/issues/issue-48636.rs", +"ui/parser/issues/issue-54521-1.rs", +"ui/parser/issues/issue-54521-2.rs", +"ui/parser/issues/issue-54521-3.rs", +"ui/parser/issues/issue-57684.rs", +"ui/parser/issues/issue-57819.rs", +"ui/parser/issues/issue-58094-missing-right-square-bracket.rs", +"ui/parser/issues/issue-62524.rs", +"ui/parser/issues/issue-62554.rs", +"ui/parser/issues/issue-62894.rs", +"ui/parser/issues/issue-62973.rs", +"ui/parser/issues/issue-63115-range-pat-interpolated.rs", +"ui/parser/issues/issue-63116.rs", +"ui/parser/issues/issue-63135.rs", +"ui/parser/issues/issue-65041-empty-vis-matcher-in-enum.rs", +"ui/parser/issues/issue-65041-empty-vis-matcher-in-trait.rs", +"ui/parser/issues/issue-66473.rs", +"ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs", +"ui/parser/issues/issue-68629.rs", +"ui/parser/issues/issue-68730.rs", +"ui/parser/issues/issue-68788-in-trait-item-propagation.rs", +"ui/parser/issues/issue-70050-ntliteral-accepts-negated-lit.rs", +"ui/parser/issues/issue-70388-without-witness.rs", +"ui/parser/issues/issue-75599.rs", +"ui/parser/issues/issue-76437-async.rs", +"ui/parser/issues/issue-76437-const-async-unsafe.rs", +"ui/parser/issues/issue-76437-const-async.rs", +"ui/parser/issues/issue-76437-const.rs", +"ui/parser/issues/issue-76437-pub-crate-unsafe.rs", +"ui/parser/issues/issue-76437-unsafe.rs", +"ui/parser/issues/issue-76597.rs", +"ui/parser/issues/issue-84104.rs", +"ui/parser/issues/issue-84148-2.rs", +"ui/parser/issues/issue-87197-missing-semicolon.rs", +"ui/parser/issues/issue-88276-unary-plus.rs", +"ui/parser/issues/issue-88583-union-as-ident.rs", +"ui/parser/issues/issue-88770.rs", +"ui/parser/issues/issue-89396.rs", +"ui/parser/issues/issue-112458.rs", +"ui/parser/macro/issue-33569.rs", +"ui/parser/macro/issue-37113.rs", +"ui/parser/macro/issue-37234.rs", +"ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs", +"ui/parser/shebang/issue-71471-ignore-tidy.rs", +"ui/parser/issue-102806.rs", +"ui/parser/issue-103143.rs", +"ui/parser/issue-103425.rs", +"ui/parser/issue-103748-ICE-wrong-braces.rs", +"ui/parser/issue-104620.rs", +"ui/parser/issue-104867-inc-dec-2.rs", +"ui/parser/issue-104867-inc-dec.rs", +"ui/parser/issue-108495-dec.rs", +"ui/parser/issue-17718-parse-const.rs", +"ui/parser/issue-39616.rs", +"ui/parser/issue-49257.rs", +"ui/parser/issue-61858.rs", +"ui/parser/issue-68091-unicode-ident-after-if.rs", +"ui/parser/issue-68092-unicode-ident-after-incomplete-expr.rs", +"ui/parser/issue-68987-unmatch-issue-1.rs", +"ui/parser/issue-68987-unmatch-issue-2.rs", +"ui/parser/issue-68987-unmatch-issue-3.rs", +"ui/parser/issue-68987-unmatch-issue.rs", +"ui/parser/issue-87694-duplicated-pub.rs", +"ui/parser/issue-87694-misplaced-pub.rs", +"ui/parser/issue-90728.rs", +"ui/parser/issue-91421.rs", +"ui/parser/issue-100197-mut-let.rs", +"ui/parser/issue-101477-enum.rs", +"ui/parser/issue-101477-let.rs", +"ui/parser/issue-103381.rs", +"ui/parser/issue-103451.rs", +"ui/parser/issue-105366.rs", +"ui/parser/issue-105634.rs", +"ui/parser/issue-107705.rs", +"ui/parser/issue-112188.rs", +"ui/parser/issue-81804.rs", +"ui/parser/issue-81827.rs", +"ui/parser/issue-99625-enum-struct-mutually-exclusive.rs", +"ui/parser/issue-99910-const-let-mutually-exclusive.rs", +"ui/parser/issue-113342.rs", +"ui/pattern/move-ref-patterns/issue-53840.rs", +"ui/pattern/usefulness/issue-12116.rs", +"ui/pattern/usefulness/issue-12369.rs", +"ui/pattern/usefulness/issue-13727.rs", +"ui/pattern/usefulness/issue-15129.rs", +"ui/pattern/usefulness/issue-2111.rs", +"ui/pattern/usefulness/issue-30240-b.rs", +"ui/pattern/usefulness/issue-30240-rpass.rs", +"ui/pattern/usefulness/issue-30240.rs", +"ui/pattern/usefulness/issue-3096-1.rs", +"ui/pattern/usefulness/issue-3096-2.rs", +"ui/pattern/usefulness/issue-31221.rs", +"ui/pattern/usefulness/issue-31561.rs", +"ui/pattern/usefulness/issue-35609.rs", +"ui/pattern/usefulness/issue-39362.rs", +"ui/pattern/usefulness/issue-40221.rs", +"ui/pattern/usefulness/issue-4321.rs", +"ui/pattern/usefulness/issue-50900.rs", +"ui/pattern/usefulness/issue-56379.rs", +"ui/pattern/usefulness/issue-57472.rs", +"ui/pattern/usefulness/issue-72377.rs", +"ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs", +"ui/pattern/usefulness/issue-82772-match-box-as-struct.rs", +"ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs", +"ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs", +"ui/pattern/usefulness/issue-66501.rs", +"ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs", +"ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs", +"ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs", +"ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs", +"ui/pattern/usefulness/issue-88747.rs", +"ui/pattern/usefulness/issue-3601.rs", +"ui/pattern/issue-10392.rs", +"ui/pattern/issue-106552.rs", +"ui/pattern/issue-11577.rs", +"ui/pattern/issue-12582.rs", +"ui/pattern/issue-14221.rs", +"ui/pattern/issue-15080.rs", +"ui/pattern/issue-17718-patterns.rs", +"ui/pattern/issue-22546.rs", +"ui/pattern/issue-27320.rs", +"ui/pattern/issue-52240.rs", +"ui/pattern/issue-6449.rs", +"ui/pattern/issue-66270-pat-struct-parser-recovery.rs", +"ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs", +"ui/pattern/issue-67776-match-same-name-enum-variant-refs.rs", +"ui/pattern/issue-68393-let-pat-assoc-constant.rs", +"ui/pattern/issue-72574-1.rs", +"ui/pattern/issue-72574-2.rs", +"ui/pattern/issue-74539.rs", +"ui/pattern/issue-74702.rs", +"ui/pattern/issue-80186-mut-binding-help-suggestion.rs", +"ui/pattern/issue-8351-1.rs", +"ui/pattern/issue-8351-2.rs", +"ui/pattern/issue-88074-pat-range-type-inference-err.rs", +"ui/pattern/issue-92074-macro-ice.rs", +"ui/pattern/issue-95878.rs", +"ui/pattern/issue-72565.rs", +"ui/pattern/issue-94866.rs", +"ui/pattern/issue-106862.rs", +"ui/pattern/issue-74954.rs", +"ui/pattern/issue-88074-pat-range-type-inference.rs", +"ui/pattern/issue-110508.rs", +"ui/polymorphization/issue-74614.rs", +"ui/polymorphization/issue-74636.rs", +"ui/privacy/auxiliary/issue-17718-const-privacy.rs", +"ui/privacy/auxiliary/issue-57264-1.rs", +"ui/privacy/auxiliary/issue-57264-2.rs", +"ui/privacy/auxiliary/issue-75907.rs", +"ui/privacy/auxiliary/issue-92755.rs", +"ui/privacy/issue-13641.rs", +"ui/privacy/issue-17718-const-privacy.rs", +"ui/privacy/issue-29161.rs", +"ui/privacy/issue-30079.rs", +"ui/privacy/issue-46209-private-enum-variant-reexport.rs", +"ui/privacy/issue-75062-fieldless-tuple-struct.rs", +"ui/privacy/issue-75906.rs", +"ui/privacy/issue-75907.rs", +"ui/privacy/issue-75907_b.rs", +"ui/privacy/issue-79593.rs", +"ui/privacy/issue-92755.rs", +"ui/privacy/issue-111220-2-tuple-struct-fields-projection.rs", +"ui/privacy/issue-111220-tuple-struct-fields.rs", +"ui/privacy/issue-57264-1.rs", +"ui/privacy/issue-57264-2.rs", +"ui/proc-macro/auxiliary/issue-104884.rs", +"ui/proc-macro/auxiliary/issue-38586.rs", +"ui/proc-macro/auxiliary/issue-39889.rs", +"ui/proc-macro/auxiliary/issue-42708.rs", +"ui/proc-macro/auxiliary/issue-50061.rs", +"ui/proc-macro/auxiliary/issue-50493.rs", +"ui/proc-macro/auxiliary/issue-66286.rs", +"ui/proc-macro/auxiliary/issue-75801.rs", +"ui/proc-macro/auxiliary/issue-79242.rs", +"ui/proc-macro/auxiliary/issue-79825.rs", +"ui/proc-macro/auxiliary/issue-83510.rs", +"ui/proc-macro/auxiliary/issue-91800-macro.rs", +"ui/proc-macro/auxiliary/issue-59191.rs", +"ui/proc-macro/issue-104884-trait-impl-sugg-err.rs", +"ui/proc-macro/issue-36935.rs", +"ui/proc-macro/issue-37788.rs", +"ui/proc-macro/issue-38586.rs", +"ui/proc-macro/issue-39889.rs", +"ui/proc-macro/issue-42708.rs", +"ui/proc-macro/issue-50061.rs", +"ui/proc-macro/issue-50493.rs", +"ui/proc-macro/issue-53481.rs", +"ui/proc-macro/issue-66286.rs", +"ui/proc-macro/issue-75801.rs", +"ui/proc-macro/issue-81543-item-parse-err.rs", +"ui/proc-macro/issue-83469-global-alloc-invalid-stmt.rs", +"ui/proc-macro/issue-83510.rs", +"ui/proc-macro/issue-91800.rs", +"ui/proc-macro/issue-59191-replace-root-with-fn.rs", +"ui/proc-macro/issue-73933-procedural-masquerade.rs", +"ui/proc-macro/issue-75734-pp-paren.rs", +"ui/proc-macro/issue-75930-derive-cfg.rs", +"ui/proc-macro/issue-76182-leading-vert-pat.rs", +"ui/proc-macro/issue-76270-panic-in-libproc-macro.rs", +"ui/proc-macro/issue-78675-captured-inner-attrs.rs", +"ui/proc-macro/issue-79148.rs", +"ui/proc-macro/issue-79242-slow-retokenize-check.rs", +"ui/proc-macro/issue-79825.rs", +"ui/proc-macro/issue-80760-empty-stmt.rs", +"ui/proc-macro/issue-81007-item-attrs.rs", +"ui/proc-macro/issue-81555.rs", +"ui/proc-macro/issue-86781-bad-inner-doc.rs", +"ui/process/issue-13304.rs", +"ui/process/issue-14456.rs", +"ui/process/issue-14940.rs", +"ui/process/issue-16272.rs", +"ui/process/issue-20091.rs", +"ui/ptr_ops/issue-80309-safe.rs", +"ui/ptr_ops/issue-80309.rs", +"ui/pub/issue-33174-restricted-type-in-public-interface.rs", +"ui/query-system/issue-83479.rs", +"ui/range/issue-54505-no-std.rs", +"ui/range/issue-73553-misinterp-range-literal.rs", +"ui/range/issue-54505-no-literals.rs", +"ui/range/issue-54505.rs", +"ui/reachable/auxiliary/issue-11225-1.rs", +"ui/reachable/auxiliary/issue-11225-2.rs", +"ui/reachable/auxiliary/issue-11225-3.rs", +"ui/reachable/issue-11225-1.rs", +"ui/reachable/issue-11225-2.rs", +"ui/reachable/issue-11225-3.rs", +"ui/reachable/issue-948.rs", +"ui/recursion/issue-26548-recursion-via-normalize.rs", +"ui/recursion/issue-38591-non-regular-dropck-recursion.rs", +"ui/recursion/issue-86784.rs", +"ui/recursion/issue-83150.rs", +"ui/recursion/issue-95134.rs", +"ui/recursion_limit/issue-105700.rs", +"ui/recursion_limit/issue-40003.rs", +"ui/regions/issue-101280.rs", +"ui/regions/issue-102374.rs", +"ui/regions/issue-102392.rs", +"ui/regions/issue-12470.rs", +"ui/regions/issue-26448-1.rs", +"ui/regions/issue-28848.rs", +"ui/regions/issue-5243.rs", +"ui/regions/issue-6157.rs", +"ui/regions/issue-11612.rs", +"ui/regions/issue-21520.rs", +"ui/regions/issue-24085.rs", +"ui/regions/issue-26448-2.rs", +"ui/regions/issue-26448-3.rs", +"ui/regions/issue-56537-closure-uses-region-from-container.rs", +"ui/regions/issue-72051-member-region-hang.rs", +"ui/regions/issue-78262.rs", +"ui/repr/issue-83505-repr-simd.rs", +"ui/repr/issue-83921-ice.rs", +"ui/resolve/auxiliary/issue-19452-aux.rs", +"ui/resolve/auxiliary/issue-21221-3.rs", +"ui/resolve/auxiliary/issue-21221-4.rs", +"ui/resolve/auxiliary/issue-30535.rs", +"ui/resolve/auxiliary/issue-3907.rs", +"ui/resolve/auxiliary/issue-80079.rs", +"ui/resolve/auxiliary/issue-112831-aux.rs", +"ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs", +"ui/resolve/issue-100365.rs", +"ui/resolve/issue-101749-2.rs", +"ui/resolve/issue-10200.rs", +"ui/resolve/issue-102946.rs", +"ui/resolve/issue-103202.rs", +"ui/resolve/issue-103474.rs", +"ui/resolve/issue-104700-inner_scope.rs", +"ui/resolve/issue-105069.rs", +"ui/resolve/issue-107563-ambiguous-glob-reexports.rs", +"ui/resolve/issue-108529.rs", +"ui/resolve/issue-109250.rs", +"ui/resolve/issue-12796.rs", +"ui/resolve/issue-14254.rs", +"ui/resolve/issue-16058.rs", +"ui/resolve/issue-17518.rs", +"ui/resolve/issue-18252.rs", +"ui/resolve/issue-19452.rs", +"ui/resolve/issue-21221-1.rs", +"ui/resolve/issue-21221-2.rs", +"ui/resolve/issue-21221-3.rs", +"ui/resolve/issue-21221-4.rs", +"ui/resolve/issue-22692.rs", +"ui/resolve/issue-2330.rs", +"ui/resolve/issue-23305.rs", +"ui/resolve/issue-2356.rs", +"ui/resolve/issue-23716.rs", +"ui/resolve/issue-24968.rs", +"ui/resolve/issue-26545.rs", +"ui/resolve/issue-3021-c.rs", +"ui/resolve/issue-3021.rs", +"ui/resolve/issue-30535.rs", +"ui/resolve/issue-3099-a.rs", +"ui/resolve/issue-3099-b.rs", +"ui/resolve/issue-31845.rs", +"ui/resolve/issue-33876.rs", +"ui/resolve/issue-35675.rs", +"ui/resolve/issue-3907-2.rs", +"ui/resolve/issue-3907.rs", +"ui/resolve/issue-39226.rs", +"ui/resolve/issue-39559-2.rs", +"ui/resolve/issue-39559.rs", +"ui/resolve/issue-42944.rs", +"ui/resolve/issue-49074.rs", +"ui/resolve/issue-5035-2.rs", +"ui/resolve/issue-5035.rs", +"ui/resolve/issue-50599.rs", +"ui/resolve/issue-5099.rs", +"ui/resolve/issue-54379.rs", +"ui/resolve/issue-55673.rs", +"ui/resolve/issue-5927.rs", +"ui/resolve/issue-60057.rs", +"ui/resolve/issue-65025-extern-static-parent-generics.rs", +"ui/resolve/issue-65035-static-with-parent-generics.rs", +"ui/resolve/issue-6702.rs", +"ui/resolve/issue-69401-trait-fn-no-body-ty-local.rs", +"ui/resolve/issue-73427.rs", +"ui/resolve/issue-80079.rs", +"ui/resolve/issue-81508.rs", +"ui/resolve/issue-82156.rs", +"ui/resolve/issue-82865.rs", +"ui/resolve/issue-85348.rs", +"ui/resolve/issue-88472.rs", +"ui/resolve/issue-90113.rs", +"ui/resolve/issue-109153.rs", +"ui/resolve/issue-101749.rs", +"ui/resolve/issue-111312.rs", +"ui/resolve/issue-111727.rs", +"ui/resolve/issue-112472-multi-generics-suggestion.rs", +"ui/resolve/issue-112831.rs", +"ui/resolve/issue-57523.rs", +"ui/resolve/issue-70736-async-fn-no-body-def-collector.rs", +"ui/resolve/issue-85671.rs", +"ui/return/issue-82612-return-mutable-reference.rs", +"ui/return/issue-64620.rs", +"ui/return/issue-86188-return-not-in-fn-body.rs", +"ui/rfcs/rfc-1937-termination-trait/issue-103052-1.rs", +"ui/rfcs/rfc-1937-termination-trait/issue-103052-2.rs", +"ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.rs", +"ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs", +"ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs", +"ui/rfcs/rfc-2396-target_feature-11/issue-99876.rs", +"ui/rfcs/rfc-2396-target_feature-11/issue-108655-inline-always-closure.rs", +"ui/rfcs/rfc-2528-type-changing-struct-update/issue-92010-trait-bound-not-satisfied.rs", +"ui/rfcs/rfc-2528-type-changing-struct-update/issue-96878.rs", +"ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs", +"ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs", +"ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs", +"ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs", +"ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs", +"ui/rfcs/rfc-2093-infer-outlives/issue-54467.rs", +"ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs", +"ui/rfcs/rfc-2497-if-let-chains/issue-88498.rs", +"ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs", +"ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs", +"ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs", +"ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-102156.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-102985.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-88155.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-90052.rs", +"ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs", +"ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-103677.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-92111.rs", +"ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs", +"ui/rust-2018/uniform-paths/auxiliary/issue-55779-extern-trait.rs", +"ui/rust-2018/uniform-paths/auxiliary/issue-56596-2.rs", +"ui/rust-2018/uniform-paths/auxiliary/issue-56596.rs", +"ui/rust-2018/uniform-paths/auxiliary/issue-87932-a.rs", +"ui/rust-2018/uniform-paths/issue-54253.rs", +"ui/rust-2018/uniform-paths/issue-55779.rs", +"ui/rust-2018/uniform-paths/issue-56596-2.rs", +"ui/rust-2018/uniform-paths/issue-56596.rs", +"ui/rust-2018/uniform-paths/issue-87932.rs", +"ui/rust-2018/issue-51008-1.rs", +"ui/rust-2018/issue-51008.rs", +"ui/rust-2018/issue-52202-use-suggestions.rs", +"ui/rust-2018/issue-54006.rs", +"ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs", +"ui/sanitize/issue-111184-generator-witness.rs", +"ui/sanitize/issue-72154-lifetime-markers.rs", +"ui/self/issue-61882-2.rs", +"ui/self/issue-61882.rs", +"ui/simd/intrinsic/issue-85855.rs", +"ui/simd/issue-17170.rs", +"ui/simd/issue-32947.rs", +"ui/simd/issue-39720.rs", +"ui/simd/issue-85915-simd-ptrs.rs", +"ui/simd/issue-89193.rs", +"ui/simd/issue-105439.rs", +"ui/single-use-lifetime/issue-104440.rs", +"ui/single-use-lifetime/issue-107998.rs", +"ui/span/issue28498-reject-ex1.rs", +"ui/span/issue28498-reject-lifetime-param.rs", +"ui/span/issue28498-reject-passed-to-fn.rs", +"ui/span/issue28498-reject-trait-bound.rs", +"ui/span/issue-11925.rs", +"ui/span/issue-23338-locals-die-before-temps-of-body.rs", +"ui/span/issue-23729.rs", +"ui/span/issue-23827.rs", +"ui/span/issue-24356.rs", +"ui/span/issue-24805-dropck-child-has-items-via-parent.rs", +"ui/span/issue-24805-dropck-trait-has-items.rs", +"ui/span/issue-24895-copy-clone-dropck.rs", +"ui/span/issue-25199.rs", +"ui/span/issue-26656.rs", +"ui/span/issue-27522.rs", +"ui/span/issue-29106.rs", +"ui/span/issue-29595.rs", +"ui/span/issue-33884.rs", +"ui/span/issue-34264.rs", +"ui/span/issue-35987.rs", +"ui/span/issue-36537.rs", +"ui/span/issue-37767.rs", +"ui/span/issue-39018.rs", +"ui/span/issue-39698.rs", +"ui/span/issue-40157.rs", +"ui/span/issue-43927-non-ADT-derive.rs", +"ui/span/issue-81800.rs", +"ui/span/issue-107353.rs", +"ui/span/issue-15480.rs", +"ui/span/issue-24690.rs", +"ui/span/issue-42234-unknown-receiver-type.rs", +"ui/span/issue-71363.rs", +"ui/specialization/min_specialization/issue-79224.rs", +"ui/specialization/issue-111232.rs", +"ui/specialization/issue-33017.rs", +"ui/specialization/issue-38091-2.rs", +"ui/specialization/issue-38091.rs", +"ui/specialization/issue-39448.rs", +"ui/specialization/issue-44861.rs", +"ui/specialization/issue-50452-fail.rs", +"ui/specialization/issue-50452.rs", +"ui/specialization/issue-51892.rs", +"ui/specialization/issue-52050.rs", +"ui/specialization/issue-59435.rs", +"ui/specialization/issue-68830-spurious-diagnostics.rs", +"ui/specialization/issue-35376.rs", +"ui/specialization/issue-36804.rs", +"ui/specialization/issue-39618.rs", +"ui/specialization/issue-40582.rs", +"ui/specialization/issue-43037.rs", +"ui/specialization/issue-45814.rs", +"ui/specialization/issue-63716-parse-async.rs", +"ui/specialization/issue-70442.rs", +"ui/stability-attribute/issue-106589.rs", +"ui/stability-attribute/issue-109177.rs", +"ui/stability-attribute/issue-28075.rs", +"ui/stability-attribute/issue-28388-3.rs", +"ui/stability-attribute/issue-99286-stable-intrinsics.rs", +"ui/static/auxiliary/issue_24843.rs", +"ui/static/issue-18118-2.rs", +"ui/static/issue-18118.rs", +"ui/static/issue-34194.rs", +"ui/static/issue-5216.rs", +"ui/static/issue-24843.rs", +"ui/statics/issue-15261.rs", +"ui/statics/issue-17233.rs", +"ui/statics/issue-17718-static-sync.rs", +"ui/statics/issue-17718-static-unsafe-interior.rs", +"ui/statics/issue-44373.rs", +"ui/statics/issue-14227.rs", +"ui/statics/issue-44373-2.rs", +"ui/statics/issue-91050-1.rs", +"ui/statics/issue-91050-2.rs", +"ui/std/issue-15149.rs", +"ui/std/issue-81357-unsound-file-methods.rs", +"ui/stdlib-unit-tests/issue-21058.rs", +"ui/structs-enums/struct-rec/issue-74224.rs", +"ui/structs-enums/struct-rec/issue-84611.rs", +"ui/structs-enums/issue-1701.rs", +"ui/structs-enums/issue-2718-a.rs", +"ui/structs-enums/issue-38002.rs", +"ui/structs-enums/issue-50731.rs", +"ui/structs-enums/issue-3008-1.rs", +"ui/structs-enums/issue-3008-2.rs", +"ui/structs-enums/issue-3008-3.rs", +"ui/structs-enums/issue-103869.rs", +"ui/structs/issue-80853.rs", +"ui/suggestions/auxiliary/issue-61963-1.rs", +"ui/suggestions/auxiliary/issue-61963.rs", +"ui/suggestions/auxiliary/issue-81839.rs", +"ui/suggestions/lifetimes/issue-105544.rs", +"ui/suggestions/issue-101421.rs", +"ui/suggestions/issue-101465.rs", +"ui/suggestions/issue-101623.rs", +"ui/suggestions/issue-101984.rs", +"ui/suggestions/issue-102354.rs", +"ui/suggestions/issue-102892.rs", +"ui/suggestions/issue-103112.rs", +"ui/suggestions/issue-104086-suggest-let.rs", +"ui/suggestions/issue-104287.rs", +"ui/suggestions/issue-104327.rs", +"ui/suggestions/issue-104328.rs", +"ui/suggestions/issue-105226.rs", +"ui/suggestions/issue-105494.rs", +"ui/suggestions/issue-105645.rs", +"ui/suggestions/issue-106443-sugg-clone-for-arg.rs", +"ui/suggestions/issue-106443-sugg-clone-for-bound.rs", +"ui/suggestions/issue-109291.rs", +"ui/suggestions/issue-109396.rs", +"ui/suggestions/issue-109436.rs", +"ui/suggestions/issue-109854.rs", +"ui/suggestions/issue-21673.rs", +"ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.rs", +"ui/suggestions/issue-61963.rs", +"ui/suggestions/issue-62843.rs", +"ui/suggestions/issue-64252-self-type.rs", +"ui/suggestions/issue-66968-suggest-sorted-words.rs", +"ui/suggestions/issue-68049-1.rs", +"ui/suggestions/issue-68049-2.rs", +"ui/suggestions/issue-71394-no-from-impl.rs", +"ui/suggestions/issue-81098.rs", +"ui/suggestions/issue-82566-1.rs", +"ui/suggestions/issue-82566-2.rs", +"ui/suggestions/issue-84592.rs", +"ui/suggestions/issue-84700.rs", +"ui/suggestions/issue-84973-2.rs", +"ui/suggestions/issue-84973-blacklist.rs", +"ui/suggestions/issue-84973-negative.rs", +"ui/suggestions/issue-84973.rs", +"ui/suggestions/issue-85347.rs", +"ui/suggestions/issue-85943-no-suggest-unsized-indirection-in-where-clause.rs", +"ui/suggestions/issue-85945-check-where-clause-before-suggesting-unsized.rs", +"ui/suggestions/issue-86100-tuple-paren-comma.rs", +"ui/suggestions/issue-88730.rs", +"ui/suggestions/issue-89064.rs", +"ui/suggestions/issue-89333.rs", +"ui/suggestions/issue-90974.rs", +"ui/suggestions/issue-94171.rs", +"ui/suggestions/issue-96223.rs", +"ui/suggestions/issue-97760.rs", +"ui/suggestions/issue-98500.rs", +"ui/suggestions/issue-99080.rs", +"ui/suggestions/issue-99240-2.rs", +"ui/suggestions/issue-99240.rs", +"ui/suggestions/issue-99597.rs", +"ui/suggestions/issue-103646.rs", +"ui/suggestions/issue-88696.rs", +"ui/suggestions/issue-101065.rs", +"ui/suggestions/issue-104961.rs", +"ui/suggestions/issue-105761-suggest-self-for-closure.rs", +"ui/suggestions/issue-107860.rs", +"ui/suggestions/issue-108470.rs", +"ui/suggestions/issue-52820.rs", +"ui/suggestions/issue-53692.rs", +"ui/suggestions/issue-57672.rs", +"ui/suggestions/issue-59819.rs", +"ui/suggestions/issue-61226.rs", +"ui/suggestions/issue-72766.rs", +"ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs", +"ui/suggestions/issue-81839.rs", +"ui/suggestions/issue-82361.rs", +"ui/suggestions/issue-83892.rs", +"ui/suggestions/issue-83943.rs", +"ui/suggestions/issue-86667.rs", +"ui/suggestions/issue-90213-expected-boxfuture-self-ice.rs", +"ui/suggestions/issue-96555.rs", +"ui/suggestions/issue-97677.rs", +"ui/suggestions/issue-97704.rs", +"ui/suggestions/issue-102972.rs", +"ui/suggestions/issue-109991.rs", +"ui/suggestions/issue-112590-suggest-import.rs", +"ui/suggestions/issue-89640.rs", +"ui/symbol-names/issue-53912.rs", +"ui/symbol-names/issue-60925.rs", +"ui/symbol-names/issue-75326.rs", +"ui/symbol-names/issue-76365.rs", +"ui/test-attrs/custom-test-frameworks/issue-107454.rs", +"ui/test-attrs/issue-109816.rs", +"ui/test-attrs/issue-12997-1.rs", +"ui/test-attrs/issue-12997-2.rs", +"ui/test-attrs/issue-16597-empty.rs", +"ui/test-attrs/issue-16597.rs", +"ui/test-attrs/issue-20823.rs", +"ui/test-attrs/issue-34932.rs", +"ui/test-attrs/issue-36768.rs", +"ui/test-attrs/issue-52557.rs", +"ui/test-attrs/issue-53675-a-test-called-panic.rs", +"ui/threads-sendsync/issue-24313.rs", +"ui/threads-sendsync/issue-29488.rs", +"ui/threads-sendsync/issue-43733-2.rs", +"ui/threads-sendsync/issue-4446.rs", +"ui/threads-sendsync/issue-4448.rs", +"ui/threads-sendsync/issue-8827.rs", +"ui/threads-sendsync/issue-9396.rs", +"ui/threads-sendsync/issue-43733.rs", +"ui/trait-bounds/issue-75961.rs", +"ui/trait-bounds/issue-93008.rs", +"ui/trait-bounds/issue-94680.rs", +"ui/trait-bounds/issue-94999.rs", +"ui/trait-bounds/issue-95640.rs", +"ui/traits/alias/issue-108072-unmet-trait-alias-bound.rs", +"ui/traits/alias/issue-108132-unmet-trait-alias-bound-on-generic-impl.rs", +"ui/traits/alias/issue-83613.rs", +"ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs", +"ui/traits/alias/issue-60021-assoc-method-resolve.rs", +"ui/traits/alias/issue-60755.rs", +"ui/traits/alias/issue-72415-assoc-const-resolve.rs", +"ui/traits/alias/issue-75983.rs", +"ui/traits/associated_type_bound/issue-51446.rs", +"ui/traits/auxiliary/issue_89119_intercrate_caching.rs", +"ui/traits/new-solver/coherence/issue-102048.rs", +"ui/traits/object/issue-44454-1.rs", +"ui/traits/object/issue-44454-2.rs", +"ui/traits/object/issue-44454-3.rs", +"ui/traits/object/issue-33140-traitobject-crate.rs", +"ui/traits/suggest-deferences/issue-39029.rs", +"ui/traits/suggest-deferences/issue-62530.rs", +"ui/traits/trait-upcasting/issue-11515.rs", +"ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs", +"ui/traits/vtable/issue-97381.rs", +"ui/traits/vtable/issue-91807.rs", +"ui/traits/issue-102989.rs", +"ui/traits/issue-103563.rs", +"ui/traits/issue-104322.rs", +"ui/traits/issue-106072.rs", +"ui/traits/issue-18400.rs", +"ui/traits/issue-18412.rs", +"ui/traits/issue-20692.rs", +"ui/traits/issue-22019.rs", +"ui/traits/issue-22110.rs", +"ui/traits/issue-22384.rs", +"ui/traits/issue-22655.rs", +"ui/traits/issue-23003.rs", +"ui/traits/issue-23825.rs", +"ui/traits/issue-24010.rs", +"ui/traits/issue-26339.rs", +"ui/traits/issue-28576.rs", +"ui/traits/issue-32963.rs", +"ui/traits/issue-33140-hack-boundaries.rs", +"ui/traits/issue-33140.rs", +"ui/traits/issue-35869.rs", +"ui/traits/issue-3683.rs", +"ui/traits/issue-38033.rs", +"ui/traits/issue-38404.rs", +"ui/traits/issue-38604.rs", +"ui/traits/issue-3973.rs", +"ui/traits/issue-4107.rs", +"ui/traits/issue-43132.rs", +"ui/traits/issue-43784-supertrait.rs", +"ui/traits/issue-50480.rs", +"ui/traits/issue-52893.rs", +"ui/traits/issue-56202.rs", +"ui/traits/issue-56488.rs", +"ui/traits/issue-59029-1.rs", +"ui/traits/issue-59029-2.rs", +"ui/traits/issue-6128.rs", +"ui/traits/issue-6334.rs", +"ui/traits/issue-65284-suggest-generic-trait-bound.rs", +"ui/traits/issue-65673.rs", +"ui/traits/issue-68295.rs", +"ui/traits/issue-7013.rs", +"ui/traits/issue-71036.rs", +"ui/traits/issue-71136.rs", +"ui/traits/issue-72410.rs", +"ui/traits/issue-75627.rs", +"ui/traits/issue-77982.rs", +"ui/traits/issue-78372.rs", +"ui/traits/issue-79458.rs", +"ui/traits/issue-8153.rs", +"ui/traits/issue-85735.rs", +"ui/traits/issue-87558.rs", +"ui/traits/issue-91594.rs", +"ui/traits/issue-9394-inherited-calls.rs", +"ui/traits/issue-97576.rs", +"ui/traits/issue-99875.rs", +"ui/traits/issue-105231.rs", +"ui/traits/issue-83538-tainted-cache-after-cycle.rs", +"ui/traits/issue-23003-overflow.rs", +"ui/traits/issue-70944.rs", +"ui/traits/issue-72455.rs", +"ui/traits/issue-78632.rs", +"ui/traits/issue-82830.rs", +"ui/traits/issue-84399-bad-fresh-caching.rs", +"ui/traits/issue-85360-eval-obligation-ice.rs", +"ui/traits/issue-89119.rs", +"ui/traits/issue-90195-2.rs", +"ui/traits/issue-90195.rs", +"ui/traits/issue-90662-projection-caching.rs", +"ui/traits/issue-91949-hangs-on-recursion.rs", +"ui/traits/issue-92292.rs", +"ui/traits/issue-95311.rs", +"ui/traits/issue-95898.rs", +"ui/traits/issue-96664.rs", +"ui/traits/issue-96665.rs", +"ui/traits/issue-97695-double-trivial-bound.rs", +"ui/transmutability/arrays/issue-103783-array-length.rs", +"ui/transmutability/issue-101739-1.rs", +"ui/transmutability/issue-101739-2.rs", +"ui/transmutability/issue-110467.rs", +"ui/transmutability/issue-110892.rs", +"ui/trivial-bounds/issue-73021-impossible-inline.rs", +"ui/try-block/issue-45124.rs", +"ui/type-alias-enum-variants/issue-57866.rs", +"ui/type-alias-enum-variants/issue-61801-path-pattern-can-infer.rs", +"ui/type-alias-enum-variants/issue-63151-dead-code-lint-fields-in-patterns.rs", +"ui/type-alias-impl-trait/issue-52843-closure-constrain.rs", +"ui/type-alias-impl-trait/issue-52843.rs", +"ui/type-alias-impl-trait/issue-53092-2.rs", +"ui/type-alias-impl-trait/issue-53092.rs", +"ui/type-alias-impl-trait/issue-53096.rs", +"ui/type-alias-impl-trait/issue-53598.rs", +"ui/type-alias-impl-trait/issue-57700.rs", +"ui/type-alias-impl-trait/issue-58887.rs", +"ui/type-alias-impl-trait/issue-60371.rs", +"ui/type-alias-impl-trait/issue-60407.rs", +"ui/type-alias-impl-trait/issue-63279.rs", +"ui/type-alias-impl-trait/issue-65384.rs", +"ui/type-alias-impl-trait/issue-65918.rs", +"ui/type-alias-impl-trait/issue-74244.rs", +"ui/type-alias-impl-trait/issue-74280.rs", +"ui/type-alias-impl-trait/issue-74761-2.rs", +"ui/type-alias-impl-trait/issue-74761.rs", +"ui/type-alias-impl-trait/issue-84660-unsoundness.rs", +"ui/type-alias-impl-trait/issue-90400-1.rs", +"ui/type-alias-impl-trait/issue-90400-2.rs", +"ui/type-alias-impl-trait/issue-94429.rs", +"ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs", +"ui/type-alias-impl-trait/issue-98608.rs", +"ui/type-alias-impl-trait/issue-101750.rs", +"ui/type-alias-impl-trait/issue-104817.rs", +"ui/type-alias-impl-trait/issue-53398-cyclic-types.rs", +"ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs", +"ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs", +"ui/type-alias-impl-trait/issue-57611-trait-alias.rs", +"ui/type-alias-impl-trait/issue-57807-associated-type.rs", +"ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs", +"ui/type-alias-impl-trait/issue-58662-simplified.rs", +"ui/type-alias-impl-trait/issue-58951-2.rs", +"ui/type-alias-impl-trait/issue-58951.rs", +"ui/type-alias-impl-trait/issue-60564-working.rs", +"ui/type-alias-impl-trait/issue-60662.rs", +"ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs", +"ui/type-alias-impl-trait/issue-63355.rs", +"ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs", +"ui/type-alias-impl-trait/issue-66580-closure-coherence.rs", +"ui/type-alias-impl-trait/issue-67844-nested-opaque.rs", +"ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs", +"ui/type-alias-impl-trait/issue-69323.rs", +"ui/type-alias-impl-trait/issue-72793.rs", +"ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs", +"ui/type-alias-impl-trait/issue-78450.rs", +"ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs", +"ui/type-alias-impl-trait/issue-87455-static-lifetime-ice.rs", +"ui/type-alias-impl-trait/issue-89686.rs", +"ui/type-alias-impl-trait/issue-89952.rs", +"ui/type-alias-impl-trait/issue-93411.rs", +"ui/type-alias-impl-trait/issue-96572-unconstrained.rs", +"ui/type-alias-impl-trait/issue-98604.rs", +"ui/type-alias-impl-trait/issue-109054.rs", +"ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.rs", +"ui/type-alias-impl-trait/issue-57961.rs", +"ui/type-alias-impl-trait/issue-60564.rs", +"ui/type-alias-impl-trait/issue-63263-closure-return.rs", +"ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs", +"ui/type-alias-impl-trait/issue-68368-non-defining-use-2.rs", +"ui/type-alias-impl-trait/issue-68368-non-defining-use.rs", +"ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs", +"ui/type-alias-impl-trait/issue-70121.rs", +"ui/type-alias-impl-trait/issue-77179.rs", +"ui/type-alias/issue-62263-self-in-atb.rs", +"ui/type-alias/issue-62305-self-assoc-ty.rs", +"ui/type-alias/issue-62364-self-ty-arg.rs", +"ui/type-alias/issue-14933.rs", +"ui/type-alias/issue-37515.rs", +"ui/type-inference/issue-30225.rs", +"ui/type-inference/issue-113283-alllocator-trait-eq.rs", +"ui/type/ascription/issue-34255-1.rs", +"ui/type/ascription/issue-47666.rs", +"ui/type/ascription/issue-54516.rs", +"ui/type/ascription/issue-60933.rs", +"ui/type/type-check/issue-22897.rs", +"ui/type/type-check/issue-40294.rs", +"ui/type/type-check/issue-41314.rs", +"ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs", +"ui/type/type-check/issue-88577-check-fn-with-more-than-65535-arguments.rs", +"ui/type/issue-100584.rs", +"ui/type/issue-101866.rs", +"ui/type/issue-102598.rs", +"ui/type/issue-103271.rs", +"ui/type/issue-58355.rs", +"ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs", +"ui/type/issue-91268.rs", +"ui/type/issue-94187-verbose-type-name.rs", +"ui/typeck/auxiliary/issue-36708.rs", +"ui/typeck/auxiliary/issue-81943-lib.rs", +"ui/typeck/issue-100246.rs", +"ui/typeck/issue-100285.rs", +"ui/typeck/issue-103899.rs", +"ui/typeck/issue-10401.rs", +"ui/typeck/issue-104513-ice.rs", +"ui/typeck/issue-104582.rs", +"ui/typeck/issue-105946.rs", +"ui/typeck/issue-107087.rs", +"ui/typeck/issue-10969.rs", +"ui/typeck/issue-110052.rs", +"ui/typeck/issue-13853-2.rs", +"ui/typeck/issue-13853-5.rs", +"ui/typeck/issue-13853.rs", +"ui/typeck/issue-18937-1.rs", +"ui/typeck/issue-18937.rs", +"ui/typeck/issue-29124.rs", +"ui/typeck/issue-31173.rs", +"ui/typeck/issue-33575.rs", +"ui/typeck/issue-36708.rs", +"ui/typeck/issue-43189.rs", +"ui/typeck/issue-46112.rs", +"ui/typeck/issue-50687-ice-on-borrow.rs", +"ui/typeck/issue-52082-type-param-shadows-existing-type.rs", +"ui/typeck/issue-53712.rs", +"ui/typeck/issue-57404.rs", +"ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.rs", +"ui/typeck/issue-65611.rs", +"ui/typeck/issue-67971.rs", +"ui/typeck/issue-69378-ice-on-invalid-type-node-after-recovery.rs", +"ui/typeck/issue-74086.rs", +"ui/typeck/issue-75883.rs", +"ui/typeck/issue-75889.rs", +"ui/typeck/issue-7813.rs", +"ui/typeck/issue-79040.rs", +"ui/typeck/issue-80779.rs", +"ui/typeck/issue-81293.rs", +"ui/typeck/issue-81885.rs", +"ui/typeck/issue-81943.rs", +"ui/typeck/issue-83621-placeholder-static-in-extern.rs", +"ui/typeck/issue-83693.rs", +"ui/typeck/issue-84160.rs", +"ui/typeck/issue-84768.rs", +"ui/typeck/issue-84831.rs", +"ui/typeck/issue-87771-ice-assign-assign-to-bool.rs", +"ui/typeck/issue-87872-missing-inaccessible-field-literal.rs", +"ui/typeck/issue-87872-missing-inaccessible-field-pattern.rs", +"ui/typeck/issue-88609.rs", +"ui/typeck/issue-88643.rs", +"ui/typeck/issue-88844.rs", +"ui/typeck/issue-89275.rs", +"ui/typeck/issue-89806.rs", +"ui/typeck/issue-90101.rs", +"ui/typeck/issue-90164.rs", +"ui/typeck/issue-90319.rs", +"ui/typeck/issue-90804-incorrect-reference-suggestion.rs", +"ui/typeck/issue-91267.rs", +"ui/typeck/issue-91450-inner-ty-error.rs", +"ui/typeck/issue-92481.rs", +"ui/typeck/issue-93486.rs", +"ui/typeck/issue-96530.rs", +"ui/typeck/issue-96738.rs", +"ui/typeck/issue-98260.rs", +"ui/typeck/issue-98982.rs", +"ui/typeck/issue-106929.rs", +"ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs", +"ui/typeck/issue-1871.rs", +"ui/typeck/issue-2063.rs", +"ui/typeck/issue-100164.rs", +"ui/typeck/issue-104510-ice.rs", +"ui/typeck/issue-107775.rs", +"ui/typeck/issue-112252-ptr-arithmetics-help.rs", +"ui/typeck/issue-2063-resource.rs", +"ui/typeck/issue-22375.rs", +"ui/typeck/issue-55810-must-typeck-match-pats-before-guards.rs", +"ui/typeck/issue-61711-once-caused-rustc-inf-loop.rs", +"ui/typeck/issue-68590-reborrow-through-derefmut.rs", +"ui/typeck/issue-72225-call-fnmut-through-derefmut.rs", +"ui/typeck/issue-73592-borrow_mut-through-deref.rs", +"ui/typeck/issue-74933.rs", +"ui/typeck/issue-80207-unsized-return.rs", +"ui/typeck/issue-82772.rs", +"ui/typeck/issue-88803-call-expr-method.rs", +"ui/typeck/issue-89044-wrapped-expr-method.rs", +"ui/typeck/issue-89856.rs", +"ui/typeck/issue-89935.rs", +"ui/typeck/issue-90027-async-fn-return-suggestion.rs", +"ui/typeck/issue-90483-inaccessible-field-adjustment.rs", +"ui/typeck/issue-91210-ptr-method.rs", +"ui/typeck/issue-91328.rs", +"ui/typeck/issue-91334.rs", +"ui/typeck/issue-91633.rs", +"ui/typeck/issue-86721-return-expr-ice.rs", +"ui/typeof/issue-100183.rs", +"ui/typeof/issue-29184.rs", +"ui/typeof/issue-42060.rs", +"ui/unboxed-closures/issue-18652.rs", +"ui/unboxed-closures/issue-18661.rs", +"ui/unboxed-closures/issue-30906.rs", +"ui/unboxed-closures/issue-53448.rs", +"ui/underscore-imports/issue-110164.rs", +"ui/uniform-paths/auxiliary/issue-53691.rs", +"ui/uniform-paths/issue-53691.rs", +"ui/uninhabited/issue-107505.rs", +"ui/union/issue-41073.rs", +"ui/union/issue-81199.rs", +"ui/union/issue-99375.rs", +"ui/unsafe/auxiliary/issue-106126.rs", +"ui/unsafe/issue-106126-good-path-bug.rs", +"ui/unsafe/issue-3080.rs", +"ui/unsafe/issue-45087-unreachable-unsafe.rs", +"ui/unsafe/issue-45107-unnecessary-unsafe-in-closure.rs", +"ui/unsafe/issue-47412.rs", +"ui/unsafe/issue-85435-unsafe-op-in-let-under-unsafe-under-closure.rs", +"ui/unsafe/issue-87414-query-cycle.rs", +"ui/unsized-locals/issue-30276-feature-flagged.rs", +"ui/unsized-locals/issue-30276.rs", +"ui/unsized-locals/issue-50940-with-feature.rs", +"ui/unsized-locals/issue-50940.rs", +"ui/unsized-locals/issue-67981.rs", +"ui/unsized/issue-30355.rs", +"ui/unsized/issue-75707.rs", +"ui/unsized/issue-91801.rs", +"ui/unsized/issue-91803.rs", +"ui/unsized/issue-40231-1.rs", +"ui/unsized/issue-40231-2.rs", +"ui/unsized/issue-71659.rs", +"ui/unsized/issue-75899-but-gats.rs", +"ui/unsized/issue-75899.rs", +"ui/unsized/issue-97732.rs", +"ui/use/issue-18986.rs", +"ui/use/issue-60976-extern-use-primitive-type.rs", +"ui/wf/issue-103573.rs", +"ui/wf/issue-110157.rs", +"ui/wf/issue-87495.rs", +"ui/wf/issue-95665.rs", +"ui/wf/issue-96810.rs", +"ui/wf/issue-48638.rs", +"ui/where-clauses/issue-50825-1.rs", +"ui/where-clauses/issue-50825.rs", +"ui/issues-71798.rs", +"ui/issue-11881.rs", +"ui/issue-13560.rs", +"ui/issue-16822.rs", +"ui/issue-18502.rs", +"ui/issue-2804.rs", +"ui/higher-ranked/trait-bounds/issue-30786.rs", +"ui/higher-ranked/trait-bounds/issue-36139-normalize-closure-sig.rs", +"ui/higher-ranked/trait-bounds/issue-39292.rs", +"ui/higher-ranked/trait-bounds/issue-46989.rs", +"ui/higher-ranked/trait-bounds/issue-58451.rs", +"ui/higher-ranked/trait-bounds/issue-59311.rs", +"ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-85455.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89118.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-44005.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-56556.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-2.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-4.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-5.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-6.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-70120.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-74261.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-76956.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-80706.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-80956.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-81809.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90612.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs", +"ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90875.rs", +"ui/higher-ranked/trait-bounds/issue-100689.rs", +"ui/higher-ranked/trait-bounds/issue-102899.rs", +"ui/higher-ranked/trait-bounds/issue-42114.rs", +"ui/higher-ranked/trait-bounds/issue-43623.rs", +"ui/higher-ranked/trait-bounds/issue-57639.rs", +"ui/higher-ranked/trait-bounds/issue-60283.rs", +"ui/higher-ranked/trait-bounds/issue-88446.rs", +"ui/higher-ranked/trait-bounds/issue-88586-hr-self-outlives-in-trait-def.rs", +"ui/higher-ranked/trait-bounds/issue-90177.rs", +"ui/higher-ranked/trait-bounds/issue-95034.rs", +"ui/higher-ranked/trait-bounds/issue-95230.rs", +"ui/issue-76387-llvm-miscompile.rs", +"ui/issue-15924.rs", +"ui/issue-24106.rs", +"ui-fulldeps/plugin/auxiliary/issue-40001-plugin.rs", +"ui-fulldeps/plugin/issue-40001.rs", +"ui-fulldeps/plugin/issue-15778-fail.rs", +] \ No newline at end of file diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 699559e4ba9c..f9a90bf3a005 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -3,7 +3,9 @@ //! - there are no stray `.stderr` files use ignore::Walk; -use std::collections::HashMap; +use lazy_static::lazy_static; +use regex::Regex; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; @@ -88,6 +90,12 @@ fn check_entries(tests_path: &Path, bad: &mut bool) { pub fn check(path: &Path, bad: &mut bool) { check_entries(&path, bad); + + // the list of files in ui tests that are allowed to start with `issue-XXXX` + let mut allowed_issue_filenames: HashSet = HashSet::from( + include!("issues.txt").map(|path| path.replace("/", std::path::MAIN_SEPARATOR_STR)), + ); + let (ui, ui_fulldeps) = (path.join("ui"), path.join("ui-fulldeps")); let paths = [ui.as_path(), ui_fulldeps.as_path()]; crate::walk::walk_no_read(&paths, |_, _| false, &mut |entry| { @@ -100,6 +108,11 @@ pub fn check(path: &Path, bad: &mut bool) { { tidy_error!(bad, "file {} has unexpected extension {}", file_path.display(), ext); } + + // NB: We do not use file_stem() as some file names have multiple `.`s and we + // must strip all of them. + let testname = + file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0; if ext == "stderr" || ext == "stdout" { // Test output filenames have one of the formats: // ``` @@ -111,11 +124,7 @@ pub fn check(path: &Path, bad: &mut bool) { // // For now, just make sure that there is a corresponding // `$testname.rs` file. - // - // NB: We do not use file_stem() as some file names have multiple `.`s and we - // must strip all of them. - let testname = - file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0; + if !file_path.with_file_name(testname).with_extension("rs").exists() && !testname.contains("ignore-tidy") { @@ -128,6 +137,38 @@ pub fn check(path: &Path, bad: &mut bool) { } } } + + if ext == "rs" { + lazy_static! { + static ref ISSUE_NAME_REGEX: Regex = + Regex::new(r"^issues?[-_]?\d{3,}").unwrap(); + } + + if ISSUE_NAME_REGEX.is_match(testname) { + // these paths are always relative to the passed `path` and always UTF8 + let stripped_path = file_path.strip_prefix(path).unwrap().to_str().unwrap(); + if !allowed_issue_filenames.remove(stripped_path) { + tidy_error!( + bad, + "UI test `{}` should use a name that describes the test and link the issue in a comment instead.", + file_path.display(), + ); + } + } + } } }); + + // if an excluded file is renamed, it must be removed from this list + if allowed_issue_filenames.len() > 0 { + for file_name in allowed_issue_filenames { + let mut p = PathBuf::from(path); + p.push(file_name); + tidy_error!( + bad, + "file `{}` no longer exists and should be removed from the exclusions in `src/tools/tidy/src/issues.txt`", + p.display() + ); + } + } } From 7d773c3304731716cc54db3531ba54067ca55a4a Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Wed, 26 Jul 2023 10:28:53 +0100 Subject: [PATCH 105/105] compiletest: remove ci-specific remap-path-prefix Now that we have fixed the underlying cause of long type name inconsistencies in #113893, we can remove the remap-path-prefix logic from CI --- src/tools/compiletest/src/header.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 4ae2249097f6..269d9384376f 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -6,7 +6,6 @@ use std::io::BufReader; use std::path::{Path, PathBuf}; use std::process::Command; -use build_helper::ci::CiEnv; use tracing::*; use crate::common::{Config, Debugger, FailMode, Mode, PassMode}; @@ -298,13 +297,6 @@ impl TestProps { /// `//[foo]`), then the property is ignored unless `cfg` is /// `Some("foo")`. fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) { - // In CI, we've sometimes encountered non-determinism related to truncating very long paths. - // Set a consistent (short) prefix to avoid issues, but only in CI to avoid regressing the - // contributor experience. - if CiEnv::is_ci() { - self.remap_src_base = config.mode == Mode::Ui && !config.suite.contains("rustdoc"); - } - let mut has_edition = false; if !testfile.is_dir() { let file = File::open(testfile).unwrap();